Compare commits

...

131 Commits

Author SHA1 Message Date
Massimiliano Assante fc37ffec6f Update 'enunciate.xml' 10 months ago
lucio e0a50f2a72 added header for swagger 10 months ago
lucio b4c9cfeee6 updated 10 months ago
lucio 7920345286 theme modified 10 months ago
lucio 80704b9df2 basepath changed for swagger 10 months ago
lucio a42919ffcf enunciate updated 10 months ago
lucio 6b54bf4554 enunciate files updated 10 months ago
lucio 122cba00e4 added DocManager to jersey initialiser 10 months ago
Lucio Lelii 60d9bf570c Update 'CHANGELOG.md' 10 months ago
Lucio Lelii f9870a0190 Update 'CHANGELOG.md' 10 months ago
lucio d9a229929b enunciate documentation added 10 months ago
Lucio Lelii 0b907277cd Update 'pom.xml' 1 year ago
Lucio Lelii 500aaa3fc9 Update 'pom.xml' 1 year ago
Lucio Lelii e167aa24d9 smartgears bom updated 1 year ago
Lucio Lelii 39d73b97c4 aspectj updated for java11 1 year ago
Lucio Lelii 4dc547afd2 Update 'CHANGELOG.md' 1 year ago
Lucio Lelii 122b326203 update for release 1 year ago
Lucio Lelii 1bbd1d200f added library to manage particular jpeg 1 year ago
Lucio Lelii 647dbb651a Update 'src/main/java/org/gcube/data/access/storagehub/services/MessageManager.java'
solved Incident #22230
2 years ago
lucio.lelii 7f88e20a88 first message shown twice for new user issue solved 2 years ago
lucio.lelii 08f0160d8d changed vre recents parameter 2 years ago
lucio.lelii 2a46ac3aa2 solved error on vre recents 2 years ago
lucio.lelii 882f849e2f pom and changelog updated 2 years ago
lucio.lelii 8c934a138e Merge remote-tracking branch 'origin/vreQueryRemoval'
Conflicts:
	CHANGELOG.md
	pom.xml
2 years ago
lucio.lelii 20390e3147 incident https://support.d4science.org/issues/22184 solved 2 years ago
lucio.lelii 47bf9b57d2 pom and changelog update 3 years ago
lucio.lelii 27c6f2d3e1 scriptManager accept different names 3 years ago
lucio.lelii 586d9df939 removed a part of query to retrieve recents 3 years ago
lucio.lelii 7ebd1ae629 fix updated 3 years ago
lucio.lelii 51cbd0776a solved bug https://support.d4science.org/issues/22147 3 years ago
lucio.lelii f30029c052 - using groupHandler method to retrieve VRE
- added logs to ScriptManager error
3 years ago
Lucio Lelii 68eb65e168 removing query for retreiving VRE root Folder 3 years ago
lucio.lelii 8fd9bb5ac2 Merge branch 'master' of https://code-repo.d4science.org/gCubeSystem/storagehub.git 3 years ago
lucio.lelii 147e8a8961 solved bug on instantiation error for types that are not in the model 3 years ago
ROBERTO CIRILLO 933009f8ab Update 'pom.xml' 3 years ago
ROBERTO CIRILLO 0222d27a5e Update 'CHANGELOG.md' 3 years ago
ROBERTO CIRILLO 082b49ebc6 Update 'CHANGELOG.md' 3 years ago
ROBERTO CIRILLO e255a2ff5d Update 'pom.xml'
update to 1.3.2-SNAPSHOT
3 years ago
lucio.lelii 24576cd30f solved bug on messages attachments 3 years ago
Lucio Lelii f68588e05c solved issue on specific version download 3 years ago
Lucio Lelii cb609cf51c solved issue on deleting version content on empty trash 3 years ago
Lucio Lelii 8929ff579c storagebackends takes ids on delete 3 years ago
Lucio Lelii aeb434dd06 solved issue executing force delete on folder 3 years ago
lucio.lelii 470cc28035 solved issue on attachemnts 3 years ago
lucio.lelii 13016497f1 fixed a bug on Excludes 3 years ago
lucio.lelii eab7dc19b4 commit for release 3 years ago
lucio.lelii 3723ac730c script utils dep added 3 years ago
lucio.lelii 2aadb9cce7 - External Folder imporvements - Inbox and Outbox creation 3 years ago
lucio.lelii bcbe97f547 - ACLManager Delegate Added 3 years ago
lucio.lelii a2613dc1a7 - possibility to impersonate people added 3 years ago
lucio.lelii 64952442ed unshareAll fixed 3 years ago
lucio.lelii e271e9fe78 removing also ACL on already unshared folder 3 years ago
lucio.lelii 7566a3cf9f issue on home folder removal fixed 3 years ago
lucio.lelii 8c677df64e NodeAdmin resource added 3 years ago
lucio.lelii 61c84fbb11 reverted chenage 3 years ago
lucio.lelii 31751ca11e solved an issue on versioning 3 years ago
lucio.lelii 7591536a69 removing old not versioned node 3 years ago
lucio.lelii 6b690caf56 users list ordering added 3 years ago
lucio.lelii 288e8d4254 smartgear bom version fixed 3 years ago
lucio.lelii b68d30cd53 removed snapshot for release 3 years ago
lucio.lelii 7df7afecd3 use of query (indexes are not working well in jackrabbit) to retrieve
shared folder removed on delete user
3 years ago
lucio.lelii 1e916b21b6 added method to check user existence 3 years ago
lucio.lelii 56e01e91f8 Merge branch 'master' of https://code-repo.d4science.org/gCubeSystem/storagehub.git 3 years ago
lucio.lelii 94cb6e4cd3 added catch of all exception on unshare during delete 3 years ago
ROBERTO CIRILLO 5760a44220 Update 'pom.xml'
removed snapshot from gcube-smartgears-bom version
3 years ago
lucio.lelii cbb77864c5 mimetype set also on non parsable pdf
method add and remove admin on a VREFolder enabled also for admin role

fixed bug on unsharing folder on user removal
3 years ago
user1 effbb513a7 ObjectMapper import changed to thegcube embedded jackson 3 years ago
user1 953cec03ee removed an unused session 4 years ago
user1 0d499d6c88 acl error message changed 4 years ago
user1 79f755dc13 pom cleaned from warnings 4 years ago
user1 8b494440af changelog modified 4 years ago
user1 d57647a714 mimetype set also on non parasble pdf 4 years ago
Fabio Sinibaldi cb57512174 adoption of gcube-smartgears-bom.2.0.0-SNAPSHOT 4 years ago
Lucio Lelii 2732b05426 SNAPSHOT removed from version 4 years ago
Lucio Lelii 432859d9bb descriptor removed 4 years ago
Lucio Lelii dcf1a614b4 commit for release 4 years ago
Lucio Lelii 84cec8aa76 changelog added 4 years ago
Lucio Lelii 52df23f2a9 changelog added 4 years ago
Lucio Lelii ce8a1e744f commit for release 4.23
resolves a bug on Archive upload
4 years ago
Lucio Lelii 70ae49e28d update for test issue 4 years ago
Lucio Lelii f2742ce0e0 solved bug with getAnchestors and public folders 4 years ago
Lucio Lelii 7ed01ecc4e ItemAction for restore set to MOVED 4 years ago
Lucio Lelii 1db74cc4df solved bugs on restore item and authorization 4 years ago
Lucio Lelii aae84a27d6 pom updated for release 4.22 4 years ago
Lucio Lelii b80f1ccb0d changelog updated 4 years ago
Lucio Lelii ea438888c8 added a todo 4 years ago
Lucio Lelii 01b74ae117 added a servlet for administration 4 years ago
Lucio Lelii 94d9307b4c refactoring of some classes 4 years ago
Lucio Lelii e82d695bbf setting owner of an full unshared folder to caller 4 years ago
Lucio Lelii 458b8a72ea allows moving shared item outside the shared folder 4 years ago
Lucio Lelii 487eae33e2 refactoring 4 years ago
Lucio Lelii 08351b2005 public folder check added on Authorization Checker 4 years ago
Lucio Lelii 4c13f4098e moved classes in differen packages 4 years ago
lucio 6e69de91d0 solved an error on authorization for deleted user 4 years ago
lucio 62fe5a77a0 removes duplicate nodes in search 4 years ago
lucio acbd780dff pom updated for release 4 years ago
lucio a7ee9afb76 search excludes not authorized node from the results 4 years ago
lucio 9e3b5f08e0 pom updated for release 4 years ago
lucio 133d71f14f search exludes hidden 4 years ago
Lucio Lelii 9d895f0adf pom updated 4 years ago
lucio 177888e1b4 - openByPath added
- replaced some warning log with debug
4 years ago
lucio 868eadfdaa complete remove of file on user and group deletion 4 years ago
lucio 6c9aaa9489 pom updated 4 years ago
lucio 774e2b4bfb getByPath method added 4 years ago
lucio fae5173b17 diaplyName bug with root scope containing dashes fixed 4 years ago
lucio ddbac93245 VRE ACl can be changed using setAcl method 4 years ago
lucio 8a84c93c18 fixed bug on administrator check 4 years ago
lucio 2033af4572 bug on vre folder displayName on folder containing - in the name solved 4 years ago
lucio 0711d8a702 control on invalid group id and user added on addAdmin 4 years ago
lucio b9d62994f9 bug on group creation fixed 4 years ago
lucio 8f725d46c0 group creation logs added 4 years ago
lucio eb3daa0a26 added vre folder to group creator workspace 4 years ago
lucio e08984af23 On Grup creation the user creator is automatically added to the gruop 4 years ago
lucio 067f487f8b search made case insensitive 4 years ago
lucio df1956c08d accounting on serach method fixed 4 years ago
Lucio Lelii 4ebc3ce222 Update pom.xml 4 years ago
Lucio Lelii abc2e16cde SNAPSHOT removed on dependency ranges 4 years ago
Lucio Lelii 0311d9782d Update pom.xml 4 years ago
lucio 1bcaa47d48 Merge remote-tracking branch 'origin/rolesmanaging'
Conflicts:
	src/main/webapp/WEB-INF/README
	src/main/webapp/WEB-INF/gcube-app.xml
4 years ago
lucio 0f645fdde7 commit for release 4 years ago
lucio 8ac4752ca7 pom committed 4 years ago
lucio a4dc3cff54 My ApplicationListener added 4 years ago
lucio de91f86daf version changed for release 4 years ago
lucio 188d11ff70 Application Listener add to correctly shutdown the jackrabbit repository 4 years ago
lucio c1ab8333b8 solved error on group creation 4 years ago
lucio 67fe556a4f methdo for administrator management added 4 years ago
lucio 2dc02aa194 unshared accounting added 4 years ago
lucio 4802a0542e Corrected a Authorization message error 4 years ago
lucio 0f156c6637 gcube-app file updated 4 years ago
lucio 0508aa0e3a added inner method name to GroupManager 5 years ago
lucio 18cba6c067 added client method for User and Group management 5 years ago

@ -13,15 +13,15 @@
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes>
<attribute name="test" value="true"/>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry excluding="**" kind="src" output="target/test-classes" path="src/test/resources">
<attributes>
<attribute name="maven.pomderived" value="true"/>
<attribute name="test" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
@ -35,5 +35,6 @@
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry combineaccessrules="false" kind="src" path="/storagehub-model"/>
<classpathentry kind="output" path="target/classes"/>
</classpath>

@ -1,14 +1,8 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.compliance=1.8
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=ignore
org.eclipse.jdt.core.compiler.release=disabled
org.eclipse.jdt.core.compiler.source=1.8

@ -1,16 +1,226 @@
<?xml version="1.0" encoding="UTF-8"?><project-modules id="moduleCoreId" project-version="1.5.0">
<wb-module deploy-name="storagehub">
<wb-resource deploy-path="/" source-path="/target/m2e-wtp/web-resources"/>
<wb-resource deploy-path="/" source-path="/src/main/webapp" tag="defaultRootSource"/>
<wb-resource deploy-path="/WEB-INF/classes" source-path="/src/main/java"/>
<wb-resource deploy-path="/WEB-INF/classes" source-path="/src/main/resources"/>
<dependent-module archiveName="authorization-control-library-1.0.0-SNAPSHOT.jar" deploy-path="/WEB-INF/lib" handle="module:/resource/authorization-control-library/authorization-control-library">
<dependency-type>uses</dependency-type>
</dependent-module>
<dependent-module archiveName="storagehub-model-1.0.5.jar" deploy-path="/WEB-INF/lib" handle="module:/resource/storagehub-model/storagehub-model">
<dependency-type>uses</dependency-type>
</dependent-module>
<property name="context-root" value="storagehub"/>
<property name="java-output-path" value="/storagehub-webapp_BRANCH/target/classes"/>
</wb-module>
</project-modules>

@ -0,0 +1,78 @@
# Changelog
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [v1.4.2] - [2023-05-22]
- added enunciate libraries for documentation
## [v1.4.1] - [2022-11-14]
- added libraries to manage different image format
## [v1.4.0] - [2021-10-07]
- slow query removed from VRE retrieving and recents
- incident #22184 solved
## [v1.3.2] - [2021-09-28]
- fix 22087
## [v1.3.1] - [2021-09-08]
- solved bug on attachment rights
## [v1.3.0] - [2021-03-31]
- possibility to impersonate people added
## [v1.2.5] - [2021-03-11]
use of query (indexes are not working well in jackrabbit) to retrieve shared folder removed on delete user
## [v1.2.4] - [2021-01-11]
mimetype set also on non parsable pdf
method add and remove admin on a VREFolder enabled also for admin role
fixed bug on unsharing folder on user removal
added method exist user
## [v1.2.2] - [2020-10-12]
method for description update added
## [v1.2.1] - [2020-06-20]
bug on Archive uploader solved
## [v1.2.0] - [2020-04-15]
trash items changes owner on restore
restore with new destination folder added
move between shared and private or different shared folder enabled
## [v1.0.8] - [2019-09-20]
Bug on ushare owner fixed
## [v1.0.5] - [2019-04-04]
Active wait for lock in case of item creation added
## [v1.0.0] - [2015-07-01]
First commit

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

@ -1,4 +1,4 @@
#European Union Public Licence V.1.1
#European Union Public Licence V.1.2
##*EUPL © the European Community 2007*
@ -12,7 +12,7 @@ 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**
**Licensed under the EUPL V.1.2**
or has expressed by any other mean his willingness to license under the EUPL.

@ -1,14 +0,0 @@
<ReleaseNotes>
<Changeset component="org.gcube.data-access.storagehub-webapp.1.0.8"
date="2019-09-20">
<Change>Bug on ushare owner fixed</Change>
</Changeset>
<Changeset component="org.gcube.data-access.storagehub-webapp.1.0.5"
date="2019-04-04">
<Change>Active wait for lock in case of item creation added</Change>
</Changeset>
<Changeset component="org.gcube.data-access.storagehub-webapp.1.0.0"
date="2015-07-01">
<Change>First commit</Change>
</Changeset>
</ReleaseNotes>

@ -1,32 +0,0 @@
<assembly
xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
<id>servicearchive</id>
<formats>
<format>tar.gz</format>
</formats>
<baseDirectory>/</baseDirectory>
<fileSets>
<fileSet>
<directory>.</directory>
<outputDirectory>/</outputDirectory>
<useDefaultExcludes>true</useDefaultExcludes>
<includes>
<include>README.md</include>
<include>LICENSE.md</include>
<include>changelog.xml</include>
<include>profile.xml</include>
</includes>
<fileMode>755</fileMode>
<filtered>true</filtered>
</fileSet>
</fileSets>
<files>
<file>
<source>target/${build.finalName}.war</source>
<outputDirectory>/${artifactId}</outputDirectory>
</file>
</files>
</assembly>

@ -1,7 +1,8 @@
<application mode='online'>
<name>StorageHub</name>
<group>DataAccess</group>
<version>1.0.0-SNAPSHOT</version>
<version>1.4.2</version>
<description>Storage Hub webapp</description>
<local-persistence location='target' />
<exclude>/workspace/api-docs/*</exclude>
</application>

@ -0,0 +1,24 @@
<?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">
<api-classes>
<!-- Use patterns to exclude classes... e.g. for URI-Resolver <exclude
pattern="org.gcube.datatransfer.resolver.services.DocsGenerator" /> -->
</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" />
<docs
freemarkerTemplate="${project.basedir}/src/main/resources/META-INF/enunciate/d4science_docs.fmt">
<additional-css
file="css/d4science_enunciate_custom.css" />
</docs>
<swagger basePath="/workspace" />
</modules>
</enunciate>

@ -1,53 +1,44 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>maven-parent</artifactId>
<groupId>org.gcube.tools</groupId>
<version>1.1.0</version>
<relativePath />
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.gcube.data.access</groupId>
<artifactId>storagehub</artifactId>
<version>1.0.8</version>
<version>1.4.2</version>
<name>storagehub</name>
<scm>
<connection>scm:git:https://code-repo.d4science.org/gCubeSystem/storagehub.git</connection>
<developerConnection>scm:git:https://code-repo.d4science.org/gCubeSystem/storagehub.git</developerConnection>
<url>https://code-repo.d4science.org/gCubeSystem/storagehub</url>
</scm>
<packaging>war</packaging>
<properties>
<webappDirectory>${project.basedir}/src/main/webapp/WEB-INF</webappDirectory>
<jackrabbit.version>2.16.0</jackrabbit.version>
<jackrabbit.version>2.20.2</jackrabbit.version>
<jackson.version>2.8.11</jackson.version>
<slf4j.version>1.7.4</slf4j.version>
<tomcat.version>7.0.40</tomcat.version>
<jetty.version>6.1.26</jetty.version>
<tika.version>1.21</tika.version>
<slf4j.api.version>1.6.6</slf4j.api.version>
<slf4j.version>1.7.4</slf4j.version> <!-- sync with logback version -->
<logback.version>1.0.12</logback.version>
<distroDirectory>${project.basedir}/distro</distroDirectory>
<description>REST web service for Jackrabbit</description>
<warname>storagehub</warname>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<enunciate.version>2.14.0</enunciate.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.gcube.distribution</groupId>
<artifactId>maven-smartgears-bom</artifactId>
<version>LATEST</version>
<artifactId>gcube-smartgears-bom</artifactId>
<version>2.1.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
@ -58,82 +49,85 @@
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
</dependency>
<dependency>
<groupId>org.gcube.core</groupId>
<artifactId>common-smartgears</artifactId>
</dependency>
<dependency>
<groupId>org.gcube.core</groupId>
<artifactId>common-smartgears-app</artifactId>
</dependency>
<dependency>
<groupId>org.gcube.common</groupId>
<artifactId>authorization-control-library</artifactId>
<version>[1.0.0-SNAPSHOT,2.0.0-SNAPSHOT)</version>
<version>[1.0.0,2.0.0-SNAPSHOT)</version>
</dependency>
<dependency>
<groupId>org.gcube.common</groupId>
<artifactId>common-authorization</artifactId>
</dependency>
<dependency>
<groupId>org.gcube.core</groupId>
<artifactId>common-encryption</artifactId>
</dependency>
<dependency>
<groupId>org.gcube.core</groupId>
<artifactId>common-scope-maps</artifactId>
</dependency>
<dependency>
<groupId>org.gcube.core</groupId>
<artifactId>common-scope</artifactId>
</dependency>
<dependency>
<groupId>org.gcube.core</groupId>
<artifactId>common-encryption</artifactId>
</dependency>
<dependency>
<groupId>org.gcube.common</groupId>
<artifactId>storagehub-model</artifactId>
<version>[1.0.0-SNAPSHOT,2.0.0-SNAPSHOT)</version>
</dependency>
<dependency>
<groupId>org.gcube.data.access</groupId>
<artifactId>storagehub-script-utils</artifactId>
<version>[1.0.0, 2.0.0-SNAPSHOT)</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.13.1</version>
<version>5.5.13.2</version>
</dependency>
<!-- needed to manage strange image types -->
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-jpeg</artifactId>
<version>3.3.2</version>
</dependency>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-bmp</artifactId>
<version>3.3.2</version>
</dependency>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-core</artifactId>
<version>3.3.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk15on -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.62</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.gcube.common</groupId>
<artifactId>gxJRS</artifactId>
<version>[1.0.0-SNAPSHOT, 2.0.0-SNAPSHOT)</version>
</dependency>
<!-- JCR dependencies -->
<dependency>
<groupId>javax.jcr</groupId>
<artifactId>jcr</artifactId>
@ -142,7 +136,7 @@
<dependency>
<groupId>org.apache.jackrabbit</groupId>
<artifactId>jackrabbit-api</artifactId>
<version>${jackrabbit.version}</version>
<version>2.19.3</version>
</dependency>
<dependency>
<groupId>org.apache.jackrabbit</groupId>
@ -154,139 +148,159 @@
<artifactId>jackrabbit-jcr-server</artifactId>
<version>${jackrabbit.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.tika/tika-parsers -->
<dependency>
<groupId>org.apache.tika</groupId>
<artifactId>tika-parsers</artifactId>
<version>1.21</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.tika/tika-core -->
<dependency>
<groupId>org.apache.tika</groupId>
<artifactId>tika-core</artifactId>
<version>1.21</version>
</dependency>
<!-- jersey -->
<!-- https://mvnrepository.com/artifact/net.bull.javamelody/javamelody-core -->
<dependency>
<groupId>net.bull.javamelody</groupId>
<artifactId>javamelody-core</artifactId>
<version>1.82.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.2</version>
<scope>provided</scope>
</dependency>
<!-- jersey & weld -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.interceptor/javax.interceptor-api -->
<dependency>
<groupId>javax.interceptor</groupId>
<artifactId>javax.interceptor-api</artifactId>
<version>1.2.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.enterprise/cdi-api -->
<dependency>
<groupId>javax.enterprise</groupId>
<artifactId>cdi-api</artifactId>
<version>2.0</version>
</dependency>
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
<version>2.0</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
<version>2.13</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.containers.glassfish</groupId>
<artifactId>jersey-gf-cdi</artifactId>
<version>2.13</version>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet-core</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.glassfish.jersey.inject/jersey-hk2 -->
<dependency>
<groupId>javax.transaction</groupId>
<artifactId>javax.transaction-api</artifactId>
<version>1.2</version>
<groupId>org.glassfish.jersey.inject</groupId>
<artifactId>jersey-hk2</artifactId>
<version>2.30.1</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-server</artifactId>
</dependency>
<!-- weld -->
<dependency>
<groupId>javax.enterprise</groupId>
<artifactId>cdi-api</artifactId>
<version>1.1</version>
<groupId>org.glassfish.jersey.ext.cdi</groupId>
<artifactId>jersey-cdi1x</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.ext.cdi</groupId>
<artifactId>jersey-cdi1x-servlet</artifactId>
</dependency>
<dependency>
<groupId>org.jboss.weld.servlet</groupId>
<artifactId>weld-servlet</artifactId>
<version>2.2.10.Final</version>
<artifactId>weld-servlet-core</artifactId>
<version>3.1.0.Final</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.jboss/jandex -->
<dependency>
<groupId>org.jboss</groupId>
<artifactId>jandex</artifactId>
<version>1.2.2.Final</version>
<version>2.2.2.Final</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.glassfish.jersey.core/jersey-common -->
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
<version>2.3.0</version>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-common</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>2.13</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-multipart</artifactId>
<version>2.13</version>
</dependency>
<dependency>
<groupId>postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>9.1-901.jdbc4</version>
<scope>runtime</scope>
</dependency>
<!-- Storage dependencies -->
<dependency>
<groupId>org.gcube.contentmanagement</groupId>
<artifactId>storage-manager-core</artifactId>
<version>[2.0.0-SNAPSHOT,3.0.0-SNAPSHOT)</version>
<version>[3.0.0-SNAPSHOT,4.0.0-SNAPSHOT)</version>
</dependency>
<dependency>
<groupId>org.gcube.contentmanagement</groupId>
<artifactId>storage-manager-wrapper</artifactId>
<version>[2.0.0-SNAPSHOT,3.0.0-SNAPSHOT)</version>
<version>[3.0.0-SNAPSHOT,4.0.0-SNAPSHOT)</version>
</dependency>
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.9.10</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>16.0</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
<version>1.17</version>
</dependency>
<dependency>
<groupId>org.tukaani</groupId>
<artifactId>xz</artifactId>
<version>1.5</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
<artifactId>jersey-test-framework-provider-simple</artifactId>
<version>2.13</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.derby</groupId>
<artifactId>derby</artifactId>
@ -299,7 +313,6 @@
<version>10.8.2.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.googlecode.jeeunit</groupId>
<artifactId>jeeunit</artifactId>
@ -312,21 +325,29 @@
<artifactId>gson</artifactId>
<version>2.7</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.0.13</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
<groupId>com.vlkan.rfos</groupId>
<artifactId>rotating-fos</artifactId>
<version>0.9.2</version>
</dependency>
<!-- enunciate deps -->
<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>
<!-- https://mvnrepository.com/artifact/org.mockito/mockito-all -->
<dependency>
<groupId>org.mockito</groupId>
@ -334,14 +355,11 @@
<version>1.9.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jul-to-slf4j</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.jboss.weld.se</groupId>
<artifactId>weld-se</artifactId>
@ -354,24 +372,19 @@
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.test-framework</groupId>
<artifactId>jersey-test-framework-core</artifactId>
<version>2.13</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
<artifactId>jersey-test-framework-provider-grizzly2</artifactId>
<version>2.13</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<finalName>${artifactId}</finalName>
<finalName>${project.artifactId}</finalName>
<pluginManagement>
<plugins>
<plugin>
@ -385,7 +398,6 @@
<pluginExecutionFilter>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<versionRange>[1.0,)</versionRange>
<goals>
<goal>test-compile</goal>
@ -403,7 +415,7 @@
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.7</version>
<version>1.14.0</version>
<configuration>
<complianceLevel>1.8</complianceLevel>
<source>1.8</source>
@ -433,7 +445,6 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.4</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
@ -454,32 +465,53 @@
<resources>
<resource>
<directory>${distroDirectory}</directory>
<filtering>true</filtering>
<filtering>true</filtering>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
<!-- Enunciate Maven plugin -->
<plugin>
<groupId>com.webcohesion.enunciate</groupId>
<artifactId>enunciate-maven-plugin</artifactId>
<version>${enunciate.version}</version>
<configuration></configuration>
<executions>
<execution>
<id>assemble</id>
<goals>
<goal>assemble</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- Copy Enunciate Documentation from your-application/docs to your-application.war -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptors>
<descriptor>descriptor.xml</descriptor>
</descriptors>
</configuration>
<artifactId>maven-resources-plugin</artifactId>
<version>2.5</version>
<executions>
<execution>
<id>servicearchive</id>
<phase>install</phase>
<id>copy-enunciate-docs</id>
<phase>process-resources</phase>
<goals>
<goal>single</goal>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>target</outputDirectory>
<resources>
<resource>
<targetPath>${project.build.directory}/${project.artifactId}/api-docs</targetPath>
<directory>${project.build.directory}/api-docs</directory>
<filtering>true</filtering>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

@ -1,25 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Resource xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ID></ID>
<Type>Service</Type>
<Profile>
<Description>Storage Hub Webapp</Description>
<Class>DataAccess</Class>
<Name>${artifactId}</Name>
<Version>1.0.0</Version>
<Packages>
<Software>
<Name>${artifactId}</Name>
<Version>${version}</Version>
<MavenCoordinates>
<groupId>${groupId}</groupId>
<artifactId>${artifactId}</artifactId>
<version>${version}</version>
</MavenCoordinates>
<Files>
<File>${build.finalName}.jar</File>
</Files>
</Software>
</Packages>
</Profile>
</Resource>

@ -1,112 +1,154 @@
package org.gcube.data.access.storagehub;
import org.apache.jackrabbit.api.security.user.Group;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.jcr.Node;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.security.AccessControlEntry;
import javax.jcr.security.Privilege;
import org.apache.jackrabbit.api.JackrabbitSession;
import org.apache.jackrabbit.api.security.JackrabbitAccessControlList;
import org.apache.jackrabbit.api.security.user.Authorizable;
import org.apache.jackrabbit.commons.jackrabbit.authorization.AccessControlUtils;
import org.gcube.common.authorization.library.provider.AuthorizationProvider;
import org.apache.jackrabbit.api.security.user.Group;
import org.apache.jackrabbit.api.security.user.UserManager;
import org.gcube.common.storagehub.model.Excludes;
import org.gcube.common.storagehub.model.acls.ACL;
import org.gcube.common.storagehub.model.acls.AccessType;
import org.gcube.common.storagehub.model.exceptions.BackendGenericError;
import org.gcube.common.storagehub.model.exceptions.InvalidCallParameters;
import org.gcube.common.storagehub.model.exceptions.UserNotAuthorizedException;
import org.gcube.common.storagehub.model.items.FolderItem;
import org.gcube.common.storagehub.model.items.Item;
import org.gcube.common.storagehub.model.items.SharedFolder;
import org.gcube.data.access.storagehub.handlers.Node2ItemConverter;
import org.gcube.common.storagehub.model.items.TrashItem;
import org.gcube.data.access.storagehub.handlers.items.Node2ItemConverter;
import org.gcube.data.access.storagehub.services.interfaces.ACLManagerInterface;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import lombok.extern.java.Log;
import lombok.extern.log4j.Log4j;
/**
*
*
* the caller must be authorized, so i'm not passing the login also if it works on behalf of an user
*
*
*/
@Singleton
public class AuthorizationChecker {
private static Logger log = LoggerFactory.getLogger(AuthorizationChecker.class);
@Inject
Node2ItemConverter node2Item;
public void checkReadAuthorizationControl(Session session, String id) throws UserNotAuthorizedException , BackendGenericError, RepositoryException{
@Inject
PathUtil pathUtil;
@Inject
ACLManagerInterface aclManager;
public void checkReadAuthorizationControl(Session session, String userToCheck, String id) throws UserNotAuthorizedException , BackendGenericError, RepositoryException{
Node node = session.getNodeByIdentifier(id);
String login = AuthorizationProvider.instance.get().getClient().getId();
Item item = node2Item.getItem(node, Excludes.ALL);
if (item==null) throw new UserNotAuthorizedException("Insufficent Provileges for user "+login+" to read node with id "+id+": it's not a valid StorageHub node");
if (item==null) throw new UserNotAuthorizedException("Insufficent Privileges for user "+userToCheck+" to read node with id "+id+": it's not a valid StorageHub node");
//checking if the item is in the owner trash folder
if(item instanceof TrashItem && item.getParentPath().equals(pathUtil.getTrashPath(userToCheck, session).toPath()))
return;
if (!item.isShared() && item.getOwner()!=null && item.getOwner().equals(userToCheck)) return;
if (hasParentPublicFolder(session, item)) return;
//TODO: remove when messages will be passed to a new system
String parentPath = item.getParentPath();
if (parentPath.endsWith("hl:attachments") && (parentPath.contains("/OutBox/") || parentPath.contains("/InBox/"))) {
return ;
}
if (item.isShared()) {
SharedFolder parentShared = node2Item.getItem(retrieveSharedFolderParent(node, session), Excludes.EXCLUDE_ACCOUNTING);
if (parentShared.getUsers().getMap().keySet().contains(login)) return;
//SharedFolder parentShared = node2Item.getItem(retrieveSharedFolderParent(node, session), Excludes.EXCLUDE_ACCOUNTING);
//if (parentShared.getUsers().getMap().keySet().contains(userToCheck)) return;
//CHECKING ACL FOR VREFOLDER AND SHARED FOLDER
JackrabbitAccessControlList accessControlList = AccessControlUtils.getAccessControlList(session, parentShared.getPath());
AccessControlEntry[] entries = accessControlList.getAccessControlEntries();
Authorizable userAuthorizable = ((JackrabbitSession) session).getUserManager().getAuthorizable(login);
for (AccessControlEntry entry: entries) {
log.debug("checking access right for {} with compared with {}",login, entry.getPrincipal());
Authorizable authorizable = ((JackrabbitSession) session).getUserManager().getAuthorizable(entry.getPrincipal());
//TODO; check why sometimes the next line gets a nullpointer
if (!authorizable.isGroup() && entry.getPrincipal().getName().equals(login)) return;
if (authorizable.isGroup() && ((Group) authorizable).isMember(userAuthorizable)) return;
}
throw new UserNotAuthorizedException("Insufficent Provileges for user "+login+" to read node with id "+id);
List<ACL> acls = aclManager.get(item, session);
UserManager userManager = ((JackrabbitSession) session).getUserManager();
Authorizable userAuthorizable = userManager.getAuthorizable(userToCheck);
for (ACL entry: acls) {
log.debug("checking access right for {} with compared with {}",userToCheck, entry.getPricipal());
Authorizable authorizable = userManager.getAuthorizable(entry.getPricipal());
if (authorizable==null) {
log.warn("{} doesn't have a correspondant auhtorizable object, check it ", entry.getPricipal());
continue;
}
} else if (item.getOwner()==null || !item.getOwner().equals(login))
throw new UserNotAuthorizedException("Insufficent Provileges for user "+login+" to read node with id "+id);
try {
if (!authorizable.isGroup() && entry.getPricipal().equals(userToCheck)) return;
if (authorizable.isGroup() && ((Group) authorizable).isMember(userAuthorizable)) return;
}catch (Throwable e) {
log.warn("someting went wrong checking authorizations",e);
}
}
}
throw new UserNotAuthorizedException("Insufficent Privileges for user "+userToCheck+" to read node with id "+id);
}
private Node retrieveSharedFolderParent(Node node, Session session) throws BackendGenericError, RepositoryException{
if (node2Item.checkNodeType(node, SharedFolder.class)) return node;
else
return retrieveSharedFolderParent(node.getParent(), session);
private boolean hasParentPublicFolder(Session session, Item item) {
if(item==null || item.getParentPath()==null) return false;
if (item.getParentPath().replaceAll("/Home/[^/]*/"+Constants.WORKSPACE_ROOT_FOLDER_NAME,"").isEmpty() || item.getParentPath().replaceAll(Constants.SHARED_FOLDER_PATH, "").isEmpty()) {
if (item instanceof FolderItem)
return ((FolderItem) item).isPublicItem();
else return false;
} else {
if (item instanceof FolderItem)
try {
return ((FolderItem) item).isPublicItem() || hasParentPublicFolder(session, node2Item.getItem(item.getParentId(), session, Excludes.ALL));
}catch (Throwable e) {
log.warn("error checking public parents",e);
return false;
}
else
try {
return hasParentPublicFolder(session, node2Item.getItem(item.getParentId(), session, Excludes.ALL));
}catch (Throwable e) {
log.warn("error checking public parents",e);
return false;
}
}
}
public void checkWriteAuthorizationControl(Session session, String id, boolean isNewItem) throws UserNotAuthorizedException, BackendGenericError, RepositoryException {
//in case of newItem the id is the parent otherwise the old node to replace
Node node = session.getNodeByIdentifier(id);
Item item = node2Item.getItem(node, Excludes.ALL);
String login = AuthorizationProvider.instance.get().getClient().getId();
if (item==null) throw new UserNotAuthorizedException("Insufficent Provileges for user "+login+" to write into node with id "+id+": it's not a valid StorageHub node");
//newItem means that a new item will be created and id is the destination directory
public void checkWriteAuthorizationControl(Session session, String userToCheck, Item item, Node node, boolean isNewItem) throws UserNotAuthorizedException, BackendGenericError, RepositoryException {
if (item==null) throw new UserNotAuthorizedException("Not valid StorageHub node");
if (Constants.WRITE_PROTECTED_FOLDER.contains(item.getName()) || Constants.WRITE_PROTECTED_FOLDER.contains(item.getTitle()))
throw new UserNotAuthorizedException("Insufficent Provileges for user "+login+" to write into node with id "+id+": it's a protected folder");
throw new UserNotAuthorizedException("Insufficent Privileges for user "+userToCheck+" to write into node with id "+item.getId()+": it's a protected folder");
if (item.isShared()) {
Node parentSharedNode = retrieveSharedFolderParent(node, session);
JackrabbitAccessControlList accessControlList = AccessControlUtils.getAccessControlList(session, parentSharedNode.getPath());
AccessControlEntry[] entries = accessControlList.getAccessControlEntries();
Authorizable UserAuthorizable = ((JackrabbitSession) session).getUserManager().getAuthorizable(login);
//CHECKING ACL FOR VREFOLDER AND SHARED FOLDER
List<ACL> acls = aclManager.get(item, session);
UserManager userManager = ((JackrabbitSession) session).getUserManager();
Authorizable UserAuthorizable = userManager.getAuthorizable(userToCheck);
//put it in a different method
for (AccessControlEntry entry: entries) {
Authorizable authorizable = ((JackrabbitSession) session).getUserManager().getAuthorizable(entry.getPrincipal());
if ((!authorizable.isGroup() && entry.getPrincipal().getName().equals(login)) || (authorizable.isGroup() && ((Group) authorizable).isMember(UserAuthorizable))){
for (Privilege privilege : entry.getPrivileges()){
AccessType access = AccessType.fromValue(privilege.getName());
if (isNewItem && access!=AccessType.READ_ONLY)
for (ACL entry: acls) {
Authorizable authorizable = userManager.getAuthorizable(entry.getPricipal());
if ((!authorizable.isGroup() && entry.getPricipal().equals(userToCheck)) || (authorizable.isGroup() && ((Group) authorizable).isMember(UserAuthorizable))){
for (AccessType privilege : entry.getAccessTypes()){
if (isNewItem && privilege!=AccessType.READ_ONLY)
return;
else
if (!isNewItem &&
(access==AccessType.ADMINISTRATOR || access==AccessType.WRITE_ALL || (access==AccessType.WRITE_OWNER && item.getOwner().equals(login))))
(privilege==AccessType.ADMINISTRATOR || privilege==AccessType.WRITE_ALL || (privilege==AccessType.WRITE_OWNER && item.getOwner().equals(userToCheck))))
return;
}
@ -114,72 +156,56 @@ public class AuthorizationChecker {
}
}
} else
if(item.getOwner().equals(login))
if(item.getOwner().equals(userToCheck))
return;
throw new UserNotAuthorizedException("Insufficent Provileges for user "+login+" to write into node with id "+id);
throw new UserNotAuthorizedException("Insufficent Privileges for user "+userToCheck+" to write into node with id "+item.getId());
}
//newItem means that a new item will be created and id is the destination directory
public void checkWriteAuthorizationControl(Session session, String userToCheck , String id, boolean isNewItem) throws UserNotAuthorizedException, BackendGenericError, RepositoryException {
//in case of newItem the id is the parent otherwise the old node to replace
Node node = session.getNodeByIdentifier(id);
Item item = node2Item.getItem(node, Excludes.ALL);
checkWriteAuthorizationControl(session, userToCheck, item, node, isNewItem);
}
/**
*
* checks if item with {id} can be moved
*
*/
public void checkMoveOpsForProtectedFolders(Session session, String id) throws InvalidCallParameters, BackendGenericError, RepositoryException {
Node node = session.getNodeByIdentifier(id);
Item item = node2Item.getItem(node, Excludes.ALL);
if (Constants.PROTECTED_FOLDER.contains(item.getName()) || Constants.PROTECTED_FOLDER.contains(item.getTitle()))
throw new InvalidCallParameters("protected folder cannot be moved or deleted");
}
public void checkAdministratorControl(Session session, SharedFolder item) throws UserNotAuthorizedException, BackendGenericError, RepositoryException {
//TODO: riguardare questo pezzo di codice
String login = AuthorizationProvider.instance.get().getClient().getId();
if (item==null) throw new UserNotAuthorizedException("Insufficent Provileges for user "+login+": it's not a valid StorageHub node");
Node node = session.getNodeByIdentifier(item.getId());
/**
*
* checks if {userToCheck} is an admin for {item}
*
*/
public void checkAdministratorControl(Session session, String userToCheck, SharedFolder item) throws UserNotAuthorizedException, BackendGenericError, RepositoryException {
if (item==null) throw new UserNotAuthorizedException("Insufficent Privileges for user "+userToCheck+": it's not a valid StorageHub node");
if (item.isShared()) {
Node parentSharedNode = retrieveSharedFolderParent(node, session);
JackrabbitAccessControlList accessControlList = AccessControlUtils.getAccessControlList(session, parentSharedNode.getPath());
AccessControlEntry[] entries = accessControlList.getAccessControlEntries();
//put it in a different method
SharedFolder parentShared = node2Item.getItem(parentSharedNode, Excludes.EXCLUDE_ACCOUNTING);
for (AccessControlEntry entry: entries) {
if (entry.getPrincipal().getName().equals(login) || (parentShared.isVreFolder() && entry.getPrincipal().getName().equals(parentShared.getTitle()))) {
for (Privilege privilege : entry.getPrivileges()){
AccessType access = AccessType.fromValue(privilege.getName());
if (access==AccessType.ADMINISTRATOR)
List<ACL> acls = aclManager.get(item, session);
for (ACL entry: acls) {
if (entry.getPricipal().equals(userToCheck)) {
for (AccessType privilege : entry.getAccessTypes()){
if (privilege==AccessType.ADMINISTRATOR)
return;
}
throw new UserNotAuthorizedException("The user "+login+" is not an administrator of node with id "+item.getId());
}
}
}
throw new UserNotAuthorizedException("The user "+login+" is not an administrator of node with id "+item.getId());
}
throw new UserNotAuthorizedException("The user "+userToCheck+" is not an administrator of node with id "+item.getId());
}
/*
private String retrieveOwner(Node node) {
Node nodeOwner;
//get Owner
try{
return node.getProperty(NodeProperty.PORTAL_LOGIN.toString()).getString();
}catch (Exception e) {
try {
nodeOwner = node.getNode(NodeProperty.OWNER.toString());
return nodeOwner.getProperty(NodeProperty.PORTAL_LOGIN.toString()).getString();
// this.userId = nodeOwner.getProperty(USER_ID).getString();
// this.portalLogin = nodeOwner.getProperty(PORTAL_LOGIN).getString();
// node.getSession().save();
} catch (Exception e1) {
throw new RuntimeException(e1);
}
}
}
*/
}

@ -5,7 +5,17 @@ import java.util.List;
public class Constants {
public static final String VRE_FOLDER_PARENT_NAME = "MySpecialFolders";
public static final String OLD_VRE_FOLDER_PARENT_NAME = "MySpecialFolders";
public static final String PERSONAL_VRES_FOLDER_PARENT_NAME = "VREs";
public static final String INBOX_FOLDER_NAME = "InBox";
public static final String OUTBOX_FOLDER_NAME = "OutBox";
public static final String ATTACHMENTNODE_NAME = "hl:attachments";
public static final String SHARED_WITH_ME_PARENT_NAME = "SharedWithMe";
public static final String SHARED_FOLDER_PATH = "/Share";
@ -19,9 +29,11 @@ public class Constants {
public static final String ADMIN_PARAM_PWD ="admin-pwd";
public static final List<String> FOLDERS_TO_EXLUDE = Arrays.asList(Constants.VRE_FOLDER_PARENT_NAME, Constants.TRASH_ROOT_FOLDER_NAME);
public static final String HOME_VERSION_PROP = "hl:version";
public static final List<String> FOLDERS_TO_EXLUDE = Arrays.asList(Constants.OLD_VRE_FOLDER_PARENT_NAME, Constants.TRASH_ROOT_FOLDER_NAME);
public static final List<String> WRITE_PROTECTED_FOLDER = Arrays.asList(Constants.VRE_FOLDER_PARENT_NAME, Constants.TRASH_ROOT_FOLDER_NAME);
public static final List<String> WRITE_PROTECTED_FOLDER = Arrays.asList(Constants.OLD_VRE_FOLDER_PARENT_NAME, Constants.TRASH_ROOT_FOLDER_NAME);
public static final List<String> PROTECTED_FOLDER = Arrays.asList(Constants.WORKSPACE_ROOT_FOLDER_NAME, Constants.VRE_FOLDER_PARENT_NAME, Constants.TRASH_ROOT_FOLDER_NAME);
public static final List<String> PROTECTED_FOLDER = Arrays.asList(Constants.WORKSPACE_ROOT_FOLDER_NAME, Constants.OLD_VRE_FOLDER_PARENT_NAME, Constants.TRASH_ROOT_FOLDER_NAME);
}

@ -5,6 +5,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@ -0,0 +1,42 @@
package org.gcube.data.access.storagehub;
import javax.inject.Inject;
import javax.ws.rs.ext.Provider;
import org.apache.jackrabbit.api.JackrabbitRepository;
import org.gcube.data.access.storagehub.services.RepositoryInitializer;
import org.glassfish.jersey.server.monitoring.ApplicationEvent;
import org.glassfish.jersey.server.monitoring.ApplicationEventListener;
import org.glassfish.jersey.server.monitoring.RequestEvent;
import org.glassfish.jersey.server.monitoring.RequestEventListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Provider
public class MyApplicationListener implements ApplicationEventListener {
private static final Logger log = LoggerFactory.getLogger(MyApplicationListener.class);
@Inject
RepositoryInitializer repository;
@Override
public void onEvent(ApplicationEvent event) {
log.info("StorageHub - event called");
switch (event.getType()) {
case DESTROY_FINISHED:
log.info("Destroying application storageHub");
((JackrabbitRepository) repository.getRepository()).shutdown();
log.info("Jackrabbit repository stopped");
default:
break;
}
}
@Override
public RequestEventListener onRequest(RequestEvent requestEvent) {
return null;
}
}

@ -0,0 +1,51 @@
package org.gcube.data.access.storagehub;
import java.util.Iterator;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.RepositoryException;
import org.gcube.common.storagehub.model.exceptions.BackendGenericError;
import org.gcube.data.access.storagehub.handlers.ClassHandler;
public class NodeChildrenFilterIterator implements Iterator<Node> {
private NodeIterator it;
public NodeChildrenFilterIterator(Node node) throws BackendGenericError{
super();
try {
it = node.getNodes();
} catch (RepositoryException e) {
throw new BackendGenericError(e);
}
}
public NodeChildrenFilterIterator(NodeIterator iterator) throws BackendGenericError{
it = iterator;
}
private Node currentNode = null;
@Override
public boolean hasNext() {
try {
while (it.hasNext()) {
currentNode=it.nextNode();
if (ClassHandler.instance().get(currentNode.getPrimaryNodeType().getName())!=null)
return true;
}
return false;
}catch (RepositoryException e) {
throw new RuntimeException(e);
}
}
@Override
public Node next() {
return currentNode;
}
}

@ -0,0 +1,70 @@
package org.gcube.data.access.storagehub;
import javax.inject.Singleton;
import javax.jcr.Node;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import org.gcube.common.storagehub.model.Path;
import org.gcube.common.storagehub.model.Paths;
@Singleton
public class PathUtil {
public Path getWorkspacePath(String login){
return Paths.getPath(String.format("/Home/%s/%s",login,Constants.WORKSPACE_ROOT_FOLDER_NAME));
}
public Path getHome(String login){
return Paths.getPath(String.format("/Home/%s",login));
}
public Path getInboxPath(String login) {
return Paths.append(getHome(login),Constants.INBOX_FOLDER_NAME);
}
public Path getOutboxPath(String login) {
return Paths.append(getHome(login),Constants.OUTBOX_FOLDER_NAME);
}
@Deprecated
private Path getOldTrashPath(String login){
return Paths.append(getWorkspacePath(login),Constants.TRASH_ROOT_FOLDER_NAME);
}
private Path getNewTrashPath(String login){
return Paths.append(getHome(login), Constants.TRASH_ROOT_FOLDER_NAME);
}
@Deprecated
private Path getOldVREsPath(String login){
return Paths.append(getWorkspacePath(login),Constants.OLD_VRE_FOLDER_PARENT_NAME);
}
private Path getNewVREsPath(String login){
return Paths.append(getHome(login),Constants.PERSONAL_VRES_FOLDER_PARENT_NAME);
}
public Path getSharedWithMePath(String login){
return Paths.append(getWorkspacePath(login),Constants.SHARED_WITH_ME_PARENT_NAME);
}
public Path getVREsPath(String login, Session session) throws RepositoryException {
Path home = getHome(login);
Node node = session.getNode(home.toPath());
if (node.hasProperty(Constants.HOME_VERSION_PROP) && node.getProperty(Constants.HOME_VERSION_PROP).getLong()>0)
return getNewVREsPath(login);
else
return getOldVREsPath(login);
}
public Path getTrashPath(String login, Session session) throws RepositoryException {
Path home = getHome(login);
Node node = session.getNode(home.toPath());
if (node.hasProperty(Constants.HOME_VERSION_PROP) && node.getProperty(Constants.HOME_VERSION_PROP).getLong()>0)
return getNewTrashPath(login);
else
return getOldTrashPath(login);
}
}

@ -5,6 +5,7 @@ import javax.jcr.Repository;
import javax.naming.Context;
import javax.naming.InitialContext;
import org.apache.jackrabbit.api.JackrabbitRepository;
import org.gcube.data.access.storagehub.services.RepositoryInitializer;
@Singleton
@ -12,19 +13,22 @@ public class RepositoryInitializerImpl implements RepositoryInitializer{
private Repository repository;
@Override
public Repository getRepository(){
public synchronized Repository getRepository(){
return repository;
}
public RepositoryInitializerImpl() throws Exception{
protected RepositoryInitializerImpl() throws Exception{
InitialContext context = new InitialContext();
Context environment = (Context) context.lookup("java:comp/env");
repository = (Repository) environment.lookup("jcr/repository");
}
public void shutdown() {
((JackrabbitRepository)repository).shutdown();
}

@ -0,0 +1,7 @@
package org.gcube.data.access.storagehub;
public class Roles {
public static final String VREMANAGER_ROLE = "VRE-Manager";
public static final String INFRASTRUCTURE_MANAGER_ROLE = "Infrastructure-Manager";
}

@ -8,12 +8,16 @@ import javax.ws.rs.core.Application;
import org.gcube.common.gxrest.response.entity.SerializableErrorEntityTextWriter;
import org.gcube.data.access.storagehub.services.ACLManager;
import org.gcube.data.access.storagehub.services.DocManager;
import org.gcube.data.access.storagehub.services.GroupManager;
import org.gcube.data.access.storagehub.services.Impersonable;
import org.gcube.data.access.storagehub.services.ItemSharing;
import org.gcube.data.access.storagehub.services.ItemsCreator;
import org.gcube.data.access.storagehub.services.ItemsManager;
import org.gcube.data.access.storagehub.services.MessageManager;
import org.gcube.data.access.storagehub.services.UserManager;
import org.gcube.data.access.storagehub.services.WorkspaceManager;
import org.gcube.data.access.storagehub.services.admin.ScriptManager;
import org.glassfish.jersey.media.multipart.MultiPartFeature;
@Path("workspace")
@ -23,7 +27,7 @@ public class StorageHub extends Application {
public Set<Class<?>> getClasses() {
final Set<Class<?>> classes = new HashSet<Class<?>>();
// register resources and features
classes.add(MultiPartFeature.class);
classes.add(Impersonable.class);
classes.add(WorkspaceManager.class);
classes.add(ItemsManager.class);
classes.add(ItemsCreator.class);
@ -31,7 +35,12 @@ public class StorageHub extends Application {
classes.add(ItemSharing.class);
classes.add(UserManager.class);
classes.add(GroupManager.class);
classes.add(ScriptManager.class);
classes.add(MessageManager.class);
classes.add(MultiPartFeature.class);
classes.add(SerializableErrorEntityTextWriter.class);
classes.add(MyApplicationListener.class);
classes.add(DocManager.class);
return classes;
}

@ -0,0 +1,41 @@
package org.gcube.data.access.storagehub;
import org.gcube.data.access.storagehub.services.RepositoryInitializer;
import org.gcube.smartgears.ApplicationManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class StorageHubAppllicationManager implements ApplicationManager {
private static Logger logger = LoggerFactory.getLogger(StorageHubAppllicationManager.class);
private boolean alreadyShutDown = false;
public static RepositoryInitializer repository;
@Override
public synchronized void onInit() {
logger.info("jackrabbit initialization started");
try {
repository = new RepositoryInitializerImpl();
} catch (Exception e) {
logger.error("ERROR INITIALIZING REPOSITORY",e);
}
repository.getRepository();
}
@Override
public synchronized void onShutdown() {
if (!alreadyShutDown)
try {
logger.info("jackrabbit is shutting down");
repository.shutdown();
alreadyShutDown= true;
} catch (Exception e) {
logger.warn("the database was not shutdown properly",e);
}
}
}

@ -1,6 +1,5 @@
package org.gcube.data.access.storagehub;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@ -8,12 +7,10 @@ import java.net.URL;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Deque;
import java.util.LinkedList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import java.util.function.Predicate;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
@ -22,39 +19,36 @@ import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.lock.Lock;
import javax.jcr.lock.LockException;
import javax.jcr.version.Version;
import javax.jcr.query.Query;
import org.apache.commons.io.FilenameUtils;
import org.apache.jackrabbit.util.ISO9075;
import org.apache.jackrabbit.util.Text;
import org.gcube.common.authorization.library.provider.AuthorizationProvider;
import org.gcube.common.storagehub.model.Excludes;
import org.gcube.common.storagehub.model.Paths;
import org.gcube.common.storagehub.model.exceptions.BackendGenericError;
import org.gcube.common.storagehub.model.exceptions.IdNotFoundException;
import org.gcube.common.storagehub.model.exceptions.ItemLockedException;
import org.gcube.common.storagehub.model.items.AbstractFileItem;
import org.gcube.common.storagehub.model.exceptions.StorageHubException;
import org.gcube.common.storagehub.model.exceptions.UserNotAuthorizedException;
import org.gcube.common.storagehub.model.items.ExternalLink;
import org.gcube.common.storagehub.model.items.FolderItem;
import org.gcube.common.storagehub.model.items.GCubeItem;
import org.gcube.common.storagehub.model.items.Item;
import org.gcube.common.storagehub.model.items.RootItem;
import org.gcube.common.storagehub.model.items.SharedFolder;
import org.gcube.common.storagehub.model.types.ItemAction;
import org.gcube.common.storagehub.model.types.NodeProperty;
import org.gcube.contentmanager.storageclient.wrapper.AccessType;
import org.gcube.contentmanager.storageclient.wrapper.MemoryType;
import org.gcube.contentmanager.storageclient.wrapper.StorageClient;
import org.gcube.data.access.storagehub.accounting.AccountingHandler;
import org.gcube.data.access.storagehub.handlers.Item2NodeConverter;
import org.gcube.data.access.storagehub.handlers.Node2ItemConverter;
import org.gcube.data.access.storagehub.handlers.StorageBackendHandler;
import org.gcube.data.access.storagehub.handlers.VersionHandler;
import org.gcube.data.access.storagehub.storage.backend.impl.GCubeStorageBackend;
import org.gcube.data.access.storagehub.handlers.items.Item2NodeConverter;
import org.gcube.data.access.storagehub.handlers.items.Node2ItemConverter;
import org.gcube.data.access.storagehub.handlers.items.builders.FolderCreationParameters;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Utils {
public final static String SERVICE_NAME = "home-library";
public final static String SERVICE_CLASS = "org.gcube.portlets.user";
public final static String SERVICE_NAME = "home-library";
public final static String SERVICE_CLASS = "org.gcube.portlets.user";
private static final String FOLDERS_TYPE = "nthl:workspaceItem";
private static final Logger logger = LoggerFactory.getLogger(Utils.class);
@ -78,7 +72,7 @@ public class Utils {
return digest;
}
public static long getItemCount(Node parent, boolean showHidden, Class<? extends Item> nodeType) throws RepositoryException, BackendGenericError{
public static long getItemCount(Node parent, boolean showHidden, Class<? extends RootItem> nodeType) throws RepositoryException, BackendGenericError{
return getItemList(parent, Excludes.ALL, null, showHidden, nodeType).size();
}
@ -107,36 +101,82 @@ public class Utils {
}
public static <T extends Item> List<T> searchByNameOnFolder(Session ses, String user, AuthorizationChecker authChecker, Node parent, List<String> excludes, Range range, boolean showHidden, boolean excludeTrashed, Class<? extends RootItem> nodeTypeToInclude, String nameParam) throws RepositoryException, BackendGenericError{
String xpath = String.format("/jcr:root%s//element(*,nthl:workspaceItem)[jcr:like(fn:lower-case(@jcr:title), '%s')]",ISO9075.encodePath(parent.getPath()), nameParam.toLowerCase());
public static <T extends Item> List<T> getItemList(Node parent, List<String> excludes, Range range, boolean showHidden, Class<? extends Item> nodeTypeToInclude) throws RepositoryException, BackendGenericError{
//String query = String.format("SELECT * FROM [nthl:workspaceLeafItem] AS node WHERE ISDESCENDANTNODE('%s') ORDER BY node.[jcr:lastModified] DESC ",vreFolder.getPath());
logger.trace("query for search is {}",xpath);
long start = System.currentTimeMillis();
@SuppressWarnings("deprecation")
Query jcrQuery = ses.getWorkspace().getQueryManager().createQuery(xpath, Query.XPATH);
NodeChildrenFilterIterator iterator = new NodeChildrenFilterIterator(jcrQuery.execute().getNodes());
logger.trace("[SEARCH] real search took {} millis",(System.currentTimeMillis()-start));
Predicate<Node> checker = new Predicate<Node>() {
@Override
public boolean test(Node t) {
try {
authChecker.checkReadAuthorizationControl(t.getSession(), user, t.getIdentifier());
return true;
} catch (UserNotAuthorizedException | BackendGenericError | RepositoryException e) {
return false;
}
}
logger.debug("getting children of node {}", parent.getIdentifier());
};
List<T> returnList = new ArrayList<T>();
return getItemListFromNodeIterator(checker, iterator , excludes, range, showHidden, excludeTrashed, nodeTypeToInclude);
}
public static <T extends Item> List<T> getItemList(Node parent, List<String> excludes, Range range, boolean showHidden, Class<? extends RootItem> nodeTypeToInclude) throws RepositoryException, BackendGenericError{
return getItemList(null, parent, excludes, range, showHidden, nodeTypeToInclude);
}
public static <T extends Item> List<T> getItemList(Predicate<Node> checker, Node parent, List<String> excludes, Range range, boolean showHidden, Class<? extends RootItem> nodeTypeToInclude) throws RepositoryException, BackendGenericError{
logger.trace("getting children of node {}", parent.getIdentifier());
long start = System.currentTimeMillis();
NodeIterator iterator = parent.getNodes();
NodeChildrenFilterIterator iterator = new NodeChildrenFilterIterator(parent);
logger.trace("time to get iterator {}",(System.currentTimeMillis()-start));
return getItemListFromNodeIterator(checker, iterator, excludes, range, showHidden, false, nodeTypeToInclude);
}
private static <T extends Item> List<T> getItemListFromNodeIterator(Predicate<Node> checker, NodeChildrenFilterIterator iterator, List<String> excludes, Range range, boolean showHidden, boolean excludeTrashed, Class<? extends RootItem> nodeTypeToInclude) throws RepositoryException, BackendGenericError{
List<T> returnList = new ArrayList<T>();
logger.trace("nodeType is {}",nodeTypeToInclude);
int count =0;
logger.trace("selected range is {}", range);
Node2ItemConverter node2Item= new Node2ItemConverter();
Set<String> duplicateId = new HashSet<String>();
while (iterator.hasNext()){
Node current = iterator.nextNode();
Node current = iterator.next();
logger.trace("[SEARCH] evaluating node {} ",current.hasProperty(NodeProperty.TITLE.toString())? current.getProperty(NodeProperty.TITLE.toString()):current.getName());
//REMOVE duplicate nodes, in case the indexes are not working
if (duplicateId.contains(current.getIdentifier())) {
logger.warn("duplicated node found");
continue;
}
//EXCLUDES node from predicate
if (checker!=null && !checker.test(current))
continue;
logger.debug("current node "+current.getName());
if (isToExclude(current, showHidden))
continue;
logger.debug("current node not excluded "+current.getName());
logger.trace("[SEARCH] current node not excluded {} ",current.hasProperty(NodeProperty.TITLE.toString())? current.getProperty(NodeProperty.TITLE.toString()):current.getName());
if (range==null || (count>=range.getStart() && returnList.size()<range.getLimit())) {
T item = node2Item.getFilteredItem(current, excludes, nodeTypeToInclude);
if (item==null) continue;
if (item==null || (item.isTrashed() && excludeTrashed)) continue;
returnList.add(item);
}
count++;
duplicateId.add(current.getIdentifier());
}
return returnList;
}
@ -147,83 +187,10 @@ public class Utils {
(node.getPrimaryNodeType().getName().equals(FOLDERS_TYPE) && Constants.FOLDERS_TO_EXLUDE.contains(node.getName())));
}
public static org.gcube.common.storagehub.model.Path getWorkspacePath(){
return Paths.getPath(String.format("/Home/%s/Workspace",AuthorizationProvider.instance.get().getClient().getId()));
}
public static org.gcube.common.storagehub.model.Path getWorkspacePath(String login){
return Paths.getPath(String.format("/Home/%s/Workspace",login));
}
public static org.gcube.common.storagehub.model.Path getHome(String login){
return Paths.getPath(String.format("/Home/%s",login));
}
public static Deque<Item> getAllNodesForZip(FolderItem directory, Session session, AccountingHandler accountingHandler, List<String> excludes) throws RepositoryException, BackendGenericError{
Deque<Item> queue = new LinkedList<Item>();
Node currentNode = session.getNodeByIdentifier(directory.getId());
queue.push(directory);
Deque<Item> tempQueue = new LinkedList<Item>();
logger.debug("adding directory {}",currentNode.getPath());
for (Item item : Utils.getItemList(currentNode,Excludes.GET_ONLY_CONTENT, null, false, null)){
if (excludes.contains(item.getId())) continue;
if (item instanceof FolderItem)
tempQueue.addAll(getAllNodesForZip((FolderItem) item, session, accountingHandler, excludes));
else if (item instanceof AbstractFileItem){
logger.debug("adding file {}",item.getPath());
AbstractFileItem fileItem = (AbstractFileItem) item;
accountingHandler.createReadObj(fileItem.getTitle(), session, session.getNodeByIdentifier(item.getId()), false);
queue.addLast(item);
}
}
queue.addAll(tempQueue);
return queue;
}
public static void zipNode(ZipOutputStream zos, Deque<Item> queue, String login, org.gcube.common.storagehub.model.Path originalPath, StorageBackendHandler storageHandler) throws Exception{
logger.trace("originalPath is {}",originalPath.toPath());
org.gcube.common.storagehub.model.Path actualPath = Paths.getPath("");
while (!queue.isEmpty()) {
Item item = queue.pop();
if (item instanceof FolderItem) {
actualPath = Paths.getPath(item.getPath());
logger.debug("actualPath is {}",actualPath.toPath());
String name = Paths.remove(actualPath, originalPath).toPath().replaceFirst("/", "");
logger.debug("writing dir {}",name);
if (name.isEmpty()) continue;
try {
zos.putNextEntry(new ZipEntry(name));
}finally {
zos.closeEntry();
}
} else if (item instanceof AbstractFileItem){
try {
InputStream streamToWrite = storageHandler.download(((AbstractFileItem)item).getContent().getStorageId());
if (streamToWrite == null){
logger.warn("discarding item {} ",item.getName());
continue;
}
try(BufferedInputStream is = new BufferedInputStream(streamToWrite)){
String name = (Paths.remove(actualPath, originalPath).toPath()+item.getName()).replaceFirst("/", "");
logger.debug("writing file {}",name);
zos.putNextEntry(new ZipEntry(name));
copyStream(is, zos);
}catch (Exception e) {
logger.warn("error writing item {}", item.getName(),e);
} finally{
zos.closeEntry();
}
zos.flush();
}catch (Throwable e) {
logger.warn("error reading content for item {}", item.getPath(),e);
}
}
}
zos.close();
}
private static void copyStream(InputStream in, OutputStream out) throws IOException {
public static void copyStream(InputStream in, OutputStream out) throws IOException {
byte[] buffer = new byte[2048];
int readcount = 0;
while ((readcount=in.read(buffer))!=-1) {
@ -234,40 +201,17 @@ public class Utils {
public static boolean hasSharedChildren(Node node) throws RepositoryException, BackendGenericError{
Node2ItemConverter node2Item = new Node2ItemConverter();
NodeIterator children = node.getNodes();
NodeChildrenFilterIterator children = new NodeChildrenFilterIterator(node);
while (children.hasNext()) {
Node child= children.nextNode();
Node child= children.next();
if (node2Item.checkNodeType(child, SharedFolder.class)) return true;
if (node2Item.checkNodeType(child, FolderItem.class) && hasSharedChildren(child)) return true;
}
return false;
}
public static void getAllContentIds(Session ses, Set<String> idsToDelete, Item itemToDelete, VersionHandler versionHandler) throws Exception{
if (itemToDelete instanceof AbstractFileItem) {
List<Version> versions = versionHandler.getContentVersionHistory(ses.getNodeByIdentifier(itemToDelete.getId()), ses);
versions.forEach(v -> {
try {
String storageId =v.getFrozenNode().getProperty(NodeProperty.STORAGE_ID.toString()).getString();
idsToDelete.add(storageId);
logger.info("retrieved StorageId {} for version {}", storageId, v.getName());
} catch (Exception e) {
logger.warn("error retreiving sotrageId version for item with id {}",itemToDelete.getId(),e);
}
});
idsToDelete.add(((AbstractFileItem) itemToDelete).getContent().getStorageId());
}else if (itemToDelete instanceof FolderItem) {
List<Item> items = Utils.getItemList(ses.getNodeByIdentifier(itemToDelete.getId()), Excludes.GET_ONLY_CONTENT , null, true, null);
for (Item item: items)
getAllContentIds(ses, idsToDelete, item, versionHandler);
}
}
public static String checkExistanceAndGetUniqueName(Session ses, Node destination, String name) throws BackendGenericError{
try {
destination.getNode(name);
@ -283,7 +227,7 @@ public class Utils {
String nameTocheck = ext.isEmpty()? String.format("%s(*)",filename): String.format("%s(*).%s",filename, ext);
logger.debug("filename is {}, extension is {} , and name to check is {}", filename, ext, nameTocheck);
logger.trace("filename is {}, extension is {} , and name to check is {}", filename, ext, nameTocheck);
NodeIterator ni = destination.getNodes(nameTocheck);
int maxval = 0;
@ -302,32 +246,46 @@ public class Utils {
}
}
public static Node createFolderInternally(Session ses, Node destinationNode, String name, String description, boolean hidden, String login, AccountingHandler accountingHandler) throws BackendGenericError {
String uniqueName = Utils.checkExistanceAndGetUniqueName(ses, destinationNode, name);
public static Node createFolderInternally(FolderCreationParameters params, AccountingHandler accountingHandler) throws StorageHubException {
logger.debug("creating folder {} in {}", params.getName(), params.getParentId());
Node destinationNode;
try {
destinationNode = params.getSession().getNodeByIdentifier(params.getParentId());
}catch (RepositoryException e) {
throw new IdNotFoundException(params.getParentId());
}
String uniqueName = Utils.checkExistanceAndGetUniqueName(params.getSession(), destinationNode, params.getName());
FolderItem item = new FolderItem();
Calendar now = Calendar.getInstance();
item.setName(uniqueName);
item.setTitle(uniqueName);
item.setDescription(description);
item.setDescription(params.getDescription());
//TODO: item.setExternalStorage();
//item.setCreationTime(now);
item.setHidden(hidden);
boolean hiddenDestNode= false;
try {
hiddenDestNode = destinationNode.getProperty(NodeProperty.HIDDEN.toString()).getBoolean();
}catch (Throwable e) {}
item.setHidden(params.isHidden() || hiddenDestNode);
item.setLastAction(ItemAction.CREATED);
item.setLastModificationTime(now);
item.setLastModifiedBy(login);
item.setOwner(login);
item.setLastModifiedBy(params.getUser());
item.setOwner(params.getUser());
item.setPublicItem(false);
//to inherit hidden property
//item.setHidden(destinationItem.isHidden());
Node newNode = new Item2NodeConverter().getNode(destinationNode, item);
if (accountingHandler!=null)
accountingHandler.createFolderAddObj(name, item.getClass().getSimpleName(), null, ses, newNode, false);
if (accountingHandler!=null) {
accountingHandler.createFolderAddObj(uniqueName, item.getClass().getSimpleName(), null, params.getSession(), params.getUser(), destinationNode, false);
accountingHandler.createEntryCreate(item.getTitle(), params.getSession(), newNode, params.getUser(), false);
}
return newNode;
}
public static Node createURLInternally(Session ses, Node destinationNode, String name, URL value, String description, String login, AccountingHandler accountingHandler) throws BackendGenericError {
String uniqueName = Utils.checkExistanceAndGetUniqueName(ses, destinationNode, name);
@ -344,13 +302,20 @@ public class Utils {
item.setOwner(login);
item.setPublicItem(false);
item.setValue(value);
//to inherit hidden property
//item.setHidden(destinationItem.isHidden());
try {
item.setHidden(destinationNode.getProperty(NodeProperty.HIDDEN.toString()).getBoolean());
} catch (Throwable e) {
item.setHidden(false);
}
Node newNode = new Item2NodeConverter().getNode(destinationNode, item);
if (accountingHandler!=null)
accountingHandler.createFolderAddObj(name, item.getClass().getSimpleName(), null, ses, newNode, false);
if (accountingHandler!=null) {
accountingHandler.createFolderAddObj(name, item.getClass().getSimpleName(), null, ses, login, destinationNode, false);
accountingHandler.createEntryCreate(item.getTitle(), ses, newNode,login, false);
}
return newNode;
}
@ -380,4 +345,6 @@ public class Utils {
node.setProperty(NodeProperty.LAST_MODIFIED_BY.toString(), login);
node.setProperty(NodeProperty.LAST_ACTION.toString(), action.name());
}
}

@ -14,7 +14,6 @@ import javax.jcr.version.VersionHistory;
import javax.jcr.version.VersionIterator;
import javax.jcr.version.VersionManager;
import org.gcube.common.authorization.library.provider.AuthorizationProvider;
import org.gcube.common.storagehub.model.items.nodes.accounting.AccountingEntryType;
import org.gcube.common.storagehub.model.types.NodeProperty;
import org.slf4j.Logger;
@ -37,7 +36,7 @@ public class AccountingHandler {
private static final Logger logger = LoggerFactory.getLogger(AccountingHandler.class);
public void createReadObj(String title, Session ses, Node node, boolean saveHistory ) {
public void createReadObj(String title, Session ses, Node node, String login, boolean saveHistory ) {
try {
if (!node.hasNode(NodeProperty.ACCOUNTING.toString())){
@ -47,7 +46,7 @@ public class AccountingHandler {
Node accountingNodeParent = node.getNode(NodeProperty.ACCOUNTING.toString());
Node accountingNode = accountingNodeParent.addNode(UUID.randomUUID().toString(),AccountingEntryType.READ.getNodeTypeDefinition());
accountingNode.setProperty(USER, AuthorizationProvider.instance.get().getClient().getId());
accountingNode.setProperty(USER, login);
accountingNode.setProperty(DATE, Calendar.getInstance());
accountingNode.setProperty(ITEM_NAME, title);
@ -70,7 +69,26 @@ public class AccountingHandler {
}
}
public void createFileUpdated(String title, Session ses, Node node, boolean saveHistory ) {
public void createEntryCreate(String title, Session ses, Node node, String login, boolean saveHistory ) {
try {
if (!node.hasNode(NodeProperty.ACCOUNTING.toString())){
node.addNode(NodeProperty.ACCOUNTING.toString(), NodeProperty.NT_ACCOUNTING.toString());
}
Node accountingNodeParent = node.getNode(NodeProperty.ACCOUNTING.toString());
Node accountingNode = accountingNodeParent.addNode(UUID.randomUUID().toString(),AccountingEntryType.CREATE.getNodeTypeDefinition());
accountingNode.setProperty(USER, login);
accountingNode.setProperty(DATE, Calendar.getInstance());
accountingNode.setProperty(ITEM_NAME, title);
if (saveHistory) ses.save();
} catch (RepositoryException e) {
logger.warn("error trying to retrieve accountign node",e);
}
}
public void createFileUpdated(String title, Session ses, Node node, String login, boolean saveHistory ) {
try {
if (!node.hasNode(NodeProperty.ACCOUNTING.toString())){
@ -80,7 +98,7 @@ public class AccountingHandler {
Node accountingNodeParent = node.getNode(NodeProperty.ACCOUNTING.toString());
Node accountingNode = accountingNodeParent.addNode(UUID.randomUUID().toString(),AccountingEntryType.UPDATE.getNodeTypeDefinition());
accountingNode.setProperty(USER, AuthorizationProvider.instance.get().getClient().getId());
accountingNode.setProperty(USER, login);
accountingNode.setProperty(DATE, Calendar.getInstance());
accountingNode.setProperty(ITEM_NAME, title);
@ -107,17 +125,17 @@ public class AccountingHandler {
}
public void createFolderAddObj(String title, String itemType, String mimeType, Session ses, Node node, boolean saveHistory ) {
public void createFolderAddObj(String title, String itemType, String mimeType, Session ses, String login, Node parentNode, boolean saveHistory ) {
try {
Node directoryNode = node.getParent();
if (!directoryNode.hasNode(NodeProperty.ACCOUNTING.toString())){
directoryNode.addNode(NodeProperty.ACCOUNTING.toString(), NodeProperty.NT_ACCOUNTING.toString());
if (!parentNode.hasNode(NodeProperty.ACCOUNTING.toString())){
parentNode.addNode(NodeProperty.ACCOUNTING.toString(), NodeProperty.NT_ACCOUNTING.toString());
}
Node accountingNodeParent = directoryNode.getNode(NodeProperty.ACCOUNTING.toString());
Node accountingNodeParent = parentNode.getNode(NodeProperty.ACCOUNTING.toString());
Node accountingNode = accountingNodeParent.addNode(UUID.randomUUID().toString(),AccountingEntryType.ADD.getNodeTypeDefinition());
accountingNode.setProperty(USER, AuthorizationProvider.instance.get().getClient().getId());
accountingNode.setProperty(USER, login);
accountingNode.setProperty(DATE, Calendar.getInstance());
accountingNode.setProperty(ITEM_NAME, title);
accountingNode.setProperty(ITEM_TYPE, itemType);
@ -130,7 +148,7 @@ public class AccountingHandler {
}
}
public void createFolderRemoveObj(String title, String itemType, String mimeType, Session ses, Node parentNode, boolean saveHistory ) {
public void createFolderRemoveObj(String title, String itemType, String mimeType, Session ses, String login, Node parentNode, boolean saveHistory ) {
try {
if (!parentNode.hasNode(NodeProperty.ACCOUNTING.toString())){
@ -139,7 +157,7 @@ public class AccountingHandler {
Node accountingNodeParent = parentNode.getNode(NodeProperty.ACCOUNTING.toString());
Node accountingNode = accountingNodeParent.addNode(UUID.randomUUID().toString(),AccountingEntryType.REMOVAL.getNodeTypeDefinition());
accountingNode.setProperty(USER, AuthorizationProvider.instance.get().getClient().getId());
accountingNode.setProperty(USER, login);
accountingNode.setProperty(DATE, Calendar.getInstance());
accountingNode.setProperty(ITEM_NAME, title);
accountingNode.setProperty(ITEM_TYPE, itemType);
@ -152,7 +170,7 @@ public class AccountingHandler {
}
}
public void createShareFolder(String title, Set<String> users, Session ses, Node sharedNode, boolean saveHistory ) {
public void createShareFolder(String title, Set<String> users, Session ses, Node sharedNode, String login, boolean saveHistory ) {
try {
if (!sharedNode.hasNode(NodeProperty.ACCOUNTING.toString())){
@ -161,7 +179,7 @@ public class AccountingHandler {
Node accountingNodeParent = sharedNode.getNode(NodeProperty.ACCOUNTING.toString());
Node accountingNode = accountingNodeParent.addNode(UUID.randomUUID().toString(),AccountingEntryType.SHARE.getNodeTypeDefinition());
accountingNode.setProperty(USER, AuthorizationProvider.instance.get().getClient().getId());
accountingNode.setProperty(USER, login);
accountingNode.setProperty(DATE, Calendar.getInstance());
accountingNode.setProperty(ITEM_NAME, title);
accountingNode.setProperty(MEMBERS, users.toArray(new String[users.size()]));
@ -172,7 +190,7 @@ public class AccountingHandler {
}
}
public void createUnshareFolder(String title, Session ses, Node sharedNode, boolean saveHistory ) {
public void createUnshareFolder(String title, Session ses, String user, Node sharedNode, boolean saveHistory ) {
try {
if (!sharedNode.hasNode(NodeProperty.ACCOUNTING.toString())){
@ -180,8 +198,8 @@ public class AccountingHandler {
}
Node accountingNodeParent = sharedNode.getNode(NodeProperty.ACCOUNTING.toString());
Node accountingNode = accountingNodeParent.addNode(UUID.randomUUID().toString(),AccountingEntryType.SHARE.getNodeTypeDefinition());
accountingNode.setProperty(USER, AuthorizationProvider.instance.get().getClient().getId());
Node accountingNode = accountingNodeParent.addNode(UUID.randomUUID().toString(),AccountingEntryType.UNSHARE.getNodeTypeDefinition());
accountingNode.setProperty(USER, user);
accountingNode.setProperty(DATE, Calendar.getInstance());
accountingNode.setProperty(ITEM_NAME, title);
@ -191,7 +209,7 @@ public class AccountingHandler {
}
}
public void createRename(String oldTitle, String newTitle, Node node, Session ses, boolean saveHistory ) {
public void createRename(String oldTitle, String newTitle, Node node, String login, Session ses, boolean saveHistory ) {
try {
if (!node.hasNode(NodeProperty.ACCOUNTING.toString())){
@ -200,7 +218,7 @@ public class AccountingHandler {
Node accountingNodeParent = node.getNode(NodeProperty.ACCOUNTING.toString());
Node accountingNode = accountingNodeParent.addNode(UUID.randomUUID().toString(),AccountingEntryType.RENAMING.getNodeTypeDefinition());
accountingNode.setProperty(USER, AuthorizationProvider.instance.get().getClient().getId());
accountingNode.setProperty(USER, login);
accountingNode.setProperty(DATE, Calendar.getInstance());
accountingNode.setProperty(OLD_ITEM_NAME, oldTitle);
accountingNode.setProperty(NEW_ITEM_NAME, newTitle);

@ -1,11 +1,14 @@
package org.gcube.data.access.storagehub.handlers;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.gcube.common.storagehub.model.annotations.RootNode;
import org.gcube.common.storagehub.model.items.Item;
import org.gcube.common.storagehub.model.items.RootItem;
import org.reflections.Reflections;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -24,31 +27,37 @@ public class ClassHandler {
private Reflections reflection = new Reflections();
private Map<String, Class<? extends Item>> classMap = new HashMap<String, Class<? extends Item>>();
private Map<Class<? extends Item>, String> typeMap = new HashMap<Class<? extends Item>, String>();
private List<String> deprecatedNode = Arrays.asList("nthl:query", "nthl:aquamapsItem", "nthl:timeSeriesItem", "nthl:report", "nthl:reportTemplate", "nthl:workflowReport",
"nthl:workflowTemplate", "nthl:gCubeMetadata", "nthl:gCubeDocument", "nthl:gCubeDocumentLink", "nthl:gCubeImageDocumentLink", "nthl:gCubePDFDocumentLink",
"nthl:gCubeImageDocument", "nthl:gCubePDFDocument", "nthl:gCubeURLDocument", "nthl:gCubeAnnotation", "nthl:externalResourceLink", "nthl:tabularDataLink");
private Map<String, Class<? extends RootItem>> classMap = new HashMap<String, Class<? extends RootItem>>();
private Map<Class<? extends RootItem>, String> typeMap = new HashMap<Class<? extends RootItem>, String>();
@SuppressWarnings("unchecked")
private ClassHandler() {
Set<Class<?>> classesAnnotated = reflection.getTypesAnnotatedWith(RootNode.class);
for (Class<?> clazz: classesAnnotated ){
if (Item.class.isAssignableFrom(clazz)) {
if (RootItem.class.isAssignableFrom(clazz)) {
String value = clazz.getAnnotation(RootNode.class).value();
log.debug("loading class {} with value {} ", clazz, value );
classMap.put(value, (Class<? extends Item>) clazz);
typeMap.put((Class<? extends Item>) clazz, value);
classMap.put(value, (Class<? extends RootItem>) clazz);
typeMap.put((Class<? extends RootItem>) clazz, value);
}
}
}
public Class<? extends Item> get(String nodeType){
public Class<? extends RootItem> get(String nodeType){
if (classMap.containsKey(nodeType)) return classMap.get(nodeType);
else return Item.class;
if (deprecatedNode.contains(nodeType)) return Item.class;
return null;
//throw new RuntimeException("mapping not found for nodetype "+ nodeType);
}
public String getNodeType(Class<? extends Item> clazz){
public String getNodeType(Class<? extends RootItem> clazz){
if (typeMap.containsKey(clazz)) return typeMap.get(clazz);
throw new RuntimeException("mapping not found for nodetype "+ clazz.getSimpleName());
}

@ -0,0 +1,101 @@
package org.gcube.data.access.storagehub.handlers;
import java.io.BufferedInputStream;
import java.io.InputStream;
import java.util.Deque;
import java.util.LinkedList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import javax.inject.Inject;
import javax.jcr.Node;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import org.gcube.common.storagehub.model.Excludes;
import org.gcube.common.storagehub.model.Paths;
import org.gcube.common.storagehub.model.exceptions.BackendGenericError;
import org.gcube.common.storagehub.model.items.AbstractFileItem;
import org.gcube.common.storagehub.model.items.FolderItem;
import org.gcube.common.storagehub.model.items.Item;
import org.gcube.common.storagehub.model.plugins.FolderManager;
import org.gcube.data.access.storagehub.Utils;
import org.gcube.data.access.storagehub.accounting.AccountingHandler;
import org.gcube.data.access.storagehub.handlers.plugins.FolderPluginHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class CompressHandler {
private Logger logger = LoggerFactory.getLogger(CompressHandler.class);
@Inject
FolderPluginHandler pluginHandler;
public Deque<Item> getAllNodesForZip(FolderItem directory, Session session, String login, AccountingHandler accountingHandler, List<String> excludes) throws RepositoryException, BackendGenericError{
Deque<Item> queue = new LinkedList<Item>();
Node currentNode = session.getNodeByIdentifier(directory.getId());
queue.push(directory);
Deque<Item> tempQueue = new LinkedList<Item>();
logger.trace("adding directory {}",currentNode.getPath());
for (Item item : Utils.getItemList(currentNode,Excludes.GET_ONLY_CONTENT, null, false, null)){
if (excludes.contains(item.getId())) continue;
if (item instanceof FolderItem)
tempQueue.addAll(getAllNodesForZip((FolderItem) item, session, login, accountingHandler, excludes));
else if (item instanceof AbstractFileItem){
logger.trace("adding file {}",item.getPath());
AbstractFileItem fileItem = (AbstractFileItem) item;
accountingHandler.createReadObj(fileItem.getTitle(), session, session.getNodeByIdentifier(item.getId()), login, false);
queue.addLast(item);
}
}
queue.addAll(tempQueue);
return queue;
}
public void zipNode(ZipOutputStream zos, Deque<Item> queue, String login, org.gcube.common.storagehub.model.Path originalPath) throws Exception{
logger.trace("originalPath is {}",originalPath.toPath());
org.gcube.common.storagehub.model.Path actualPath = Paths.getPath("");
while (!queue.isEmpty()) {
Item item = queue.pop();
if (item instanceof FolderItem) {
actualPath = Paths.getPath(item.getPath());
logger.trace("actualPath is {}",actualPath.toPath());
String name = Paths.remove(actualPath, originalPath).toPath().replaceFirst("/", "");
logger.trace("writing dir {}",name);
if (name.isEmpty()) continue;
try {
zos.putNextEntry(new ZipEntry(name));
}finally {
zos.closeEntry();
}
} else if (item instanceof AbstractFileItem){
try {
AbstractFileItem fileItem = (AbstractFileItem)item;
FolderManager manager = pluginHandler.getFolderManager(fileItem);
InputStream streamToWrite = manager.getStorageBackend().download(fileItem.getContent());
if (streamToWrite == null){
logger.warn("discarding item {} ",item.getName());
continue;
}
try(BufferedInputStream is = new BufferedInputStream(streamToWrite)){
String name = (Paths.remove(actualPath, originalPath).toPath()+item.getName()).replaceFirst("/", "");
logger.trace("writing file {}",name);
zos.putNextEntry(new ZipEntry(name));
Utils.copyStream(is, zos);
}catch (Exception e) {
logger.warn("error writing item {}", item.getName(),e);
} finally{
zos.closeEntry();
}
zos.flush();
}catch (Throwable e) {
logger.warn("error reading content for item {}", item.getPath(),e);
}
}
}
zos.close();
}
}

@ -0,0 +1,82 @@
package org.gcube.data.access.storagehub.handlers;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.jcr.ItemNotFoundException;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.PathNotFoundException;
import javax.jcr.RepositoryException;
import org.apache.jackrabbit.api.JackrabbitSession;
import org.apache.jackrabbit.api.security.user.Group;
import org.apache.jackrabbit.api.security.user.User;
import org.gcube.common.storagehub.model.exceptions.InvalidCallParameters;
import org.gcube.common.storagehub.model.exceptions.StorageHubException;
import org.gcube.common.storagehub.model.types.NodeProperty;
import org.gcube.data.access.storagehub.Constants;
import org.gcube.data.access.storagehub.PathUtil;
import org.gcube.data.access.storagehub.services.GroupManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Singleton
public class GroupHandler {
private static final Logger log = LoggerFactory.getLogger(GroupManager.class);
@Inject
PathUtil pathUtil;
public boolean removeUserFromGroup(String groupId, String userId, JackrabbitSession session) throws StorageHubException, RepositoryException {
org.apache.jackrabbit.api.security.user.UserManager usrManager = session.getUserManager();
Group group = (Group)usrManager.getAuthorizable(groupId);
User user = (User)usrManager.getAuthorizable(userId);
if (!group.isMember(user))
throw new InvalidCallParameters("user "+userId+" is not member of group "+groupId);
//delete folder on user
String folderName = group.getPrincipal().getName();
Node folder = getFolderNodeRelatedToGroup(session, folderName);
NodeIterator ni = folder.getSharedSet();
while (ni.hasNext()) {
Node node = ni.nextNode();
if (node.getPath().startsWith(pathUtil.getWorkspacePath(user.getPrincipal().getName()).toPath())) {
node.removeShare();
break;
}
}
return group.removeMember(user);
}
public Node getFolderNodeRelatedToGroup(JackrabbitSession session, String name) throws ItemNotFoundException, RepositoryException {
Node sharedRootNode = session.getNode(Constants.SHARED_FOLDER_PATH);
Node vreFolder = null;
try {
vreFolder = sharedRootNode.getNode(name);
}catch (PathNotFoundException e) {
log.debug("is an old HL VRE");
}
if (vreFolder==null) {
NodeIterator nodes = sharedRootNode.getNodes();
while (nodes.hasNext()) {
Node node = nodes.nextNode();
if (node.hasProperty(NodeProperty.TITLE.toString()) && node.getProperty(NodeProperty.TITLE.toString()).getString().equals(name)) {
vreFolder= node;
break;
}
}
}
if (vreFolder==null) throw new ItemNotFoundException("vre folder not found for group "+name);
return vreFolder;
}
}

@ -1,51 +0,0 @@
package org.gcube.data.access.storagehub.handlers;
import java.io.InputStream;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.gcube.common.storagehub.model.items.AbstractFileItem;
import org.gcube.common.storagehub.model.items.FolderItem;
import org.gcube.common.storagehub.model.items.Item;
import org.gcube.common.storagehub.model.storages.MetaInfo;
import org.gcube.data.access.storagehub.storage.backend.impl.GCubeStorageBackend;
@Singleton
public class StorageBackendHandler {
@Inject
private GCubeStorageBackend defaultBackend;
public String move(Item item, FolderItem destination) {
//if item is a folder we have to move everything
return defaultBackend.move(((AbstractFileItem) item).getContent().getStorageId());
}
public String copy(AbstractFileItem item) {
return defaultBackend.copy(item.getContent().getStorageId(), item.getContent().getRemotePath());
}
public MetaInfo upload(InputStream stream, String itemPath) {
return defaultBackend.upload(stream, itemPath);
}
public InputStream download(String id) {
return defaultBackend.getContent(id);
}
public void delete(String id) {
defaultBackend.delete(id);
}
public String getTotalVolume() {
return defaultBackend.getTotalSizeStored();
}
public String getTotalItemsCount() {
return defaultBackend.getTotalItemsCount();
}
}

@ -1,35 +1,45 @@
package org.gcube.data.access.storagehub.handlers;
import java.util.Calendar;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.jcr.ItemNotFoundException;
import javax.jcr.Node;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.lock.LockException;
import javax.jcr.version.Version;
import org.gcube.common.authorization.library.AuthorizedTasks;
import org.gcube.common.authorization.library.provider.AuthorizationProvider;
import org.gcube.common.storagehub.model.Excludes;
import org.gcube.common.storagehub.model.Paths;
import org.gcube.common.storagehub.model.exceptions.BackendGenericError;
import org.gcube.common.storagehub.model.exceptions.InvalidCallParameters;
import org.gcube.common.storagehub.model.exceptions.ItemLockedException;
import org.gcube.common.storagehub.model.exceptions.StorageHubException;
import org.gcube.common.storagehub.model.exceptions.UserNotAuthorizedException;
import org.gcube.common.storagehub.model.items.AbstractFileItem;
import org.gcube.common.storagehub.model.items.FolderItem;
import org.gcube.common.storagehub.model.items.Item;
import org.gcube.common.storagehub.model.items.TrashItem;
import org.gcube.common.storagehub.model.items.nodes.Content;
import org.gcube.common.storagehub.model.plugins.FolderManager;
import org.gcube.common.storagehub.model.types.ItemAction;
import org.gcube.contentmanagement.blobstorage.service.IClient;
import org.gcube.data.access.storagehub.AuthorizationChecker;
import org.gcube.data.access.storagehub.Constants;
import org.gcube.data.access.storagehub.PathUtil;
import org.gcube.data.access.storagehub.Utils;
import org.gcube.data.access.storagehub.accounting.AccountingHandler;
import org.gcube.data.access.storagehub.handlers.items.Item2NodeConverter;
import org.gcube.data.access.storagehub.handlers.items.Node2ItemConverter;
import org.gcube.data.access.storagehub.handlers.plugins.FolderPluginHandler;
import org.gcube.data.access.storagehub.types.ContentPair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -38,65 +48,128 @@ public class TrashHandler {
private static Logger log = LoggerFactory.getLogger(TrashHandler.class);
ExecutorService executor = Executors.newFixedThreadPool(100);
@Inject
VersionHandler versionHandler;
@Inject
AuthorizationChecker authChecker;
@Inject
AccountingHandler accountingHandler;
@Inject
AuthorizationChecker authChecker;
Item2NodeConverter item2Node;
@Inject
Item2NodeConverter item2Node;
Node2ItemConverter node2Item;
@Inject
PathUtil pathUtil;
@Inject FolderPluginHandler managerHandler;
@Inject
StorageBackendHandler storageHandler;
FolderPluginHandler folderHandler;
public void removeNodes(Session ses, List<Item> itemsToDelete) throws RepositoryException, StorageHubException{
log.debug("defnitively removing nodes with ids {}",itemsToDelete);
for (Item item: itemsToDelete) {
removeNodesInternally(ses, item);
removeNodesInternally(ses, item, false);
}
}
private void removeNodesInternally(Session ses, Item itemToDelete) throws RepositoryException, StorageHubException {
try {
Set<String> contentIdsToDelete = new HashSet<>();
public void removeOnlyNodesContent(Session ses, List<Item> itemsToDelete) throws RepositoryException, StorageHubException{
log.debug("defnitively removing nodes with ids {}",itemsToDelete);
for (Item item: itemsToDelete) {
removeNodesInternally(ses, item, true);
}
}
private void retrieveItemsToDelete(Set<AbstractFileItem> itemsToDelete, Item itemToDelete) throws Exception{
if (itemToDelete instanceof AbstractFileItem) {
itemsToDelete.add(((AbstractFileItem) itemToDelete));
}else if (itemToDelete instanceof FolderItem) {
//only to be sure to not delete shared content
if (itemToDelete.isShared()) return;
List<Item> items = Utils.getItemList((Node) itemToDelete.getRelatedNode(), Excludes.GET_ONLY_CONTENT , null, true, null);
for (Item item: items)
retrieveItemsToDelete(itemsToDelete, item);
}
}
private Set<ContentPair> retrieveContentToDelete(Collection<AbstractFileItem> itemsToDelete) {
Set<ContentPair> contentSet = new HashSet<ContentPair>();
for (AbstractFileItem item: itemsToDelete) {
if (item.getContent()== null || item.getContent().getStorageId()==null) {
log.warn("item with id {} contains null content",item.getId());
continue;
}
try {
FolderManager manager = folderHandler.getFolderManager(item);
contentSet.add(new ContentPair(item.getContent(), manager.getStorageBackend()));
Node nodeToDelete = ses.getNodeByIdentifier(itemToDelete.getId());
List<Version> versions = versionHandler.getContentVersionHistory((Node)item.getRelatedNode());
for (Version version: versions) {
try {
Content content = node2Item.getContentFromVersion(version);
if (content!= null && content.getStorageId()!=null)
contentSet.add(new ContentPair(content, manager.getStorageBackend()));
else log.warn("invalid version {}",version.getName());
}catch (Throwable t) {
log.warn("error retrieving version content for {}",version.getName(),t);
}
}
}catch (Exception e) {
log.warn("item with id {} cannot be deleted",item.getId(),e);
}
}
return contentSet;
}
private void removeNodesInternally(Session ses, Item itemToDelete, boolean onlyContent) throws RepositoryException, StorageHubException {
try {
Set<AbstractFileItem> itemsToDelete = new HashSet<>();
Node nodeToDelete = (Node) itemToDelete.getRelatedNode();
if (itemToDelete instanceof TrashItem) {
List<Item> trashChildren = Utils.getItemList(nodeToDelete, Excludes.GET_ONLY_CONTENT, null, true, null);
for (Item itemContentToRetrieve: trashChildren)
Utils.getAllContentIds(ses, contentIdsToDelete, itemContentToRetrieve, versionHandler);
} else {
Utils.getAllContentIds(ses, contentIdsToDelete, itemToDelete, versionHandler);
}
nodeToDelete.remove();
log.debug("content ids to remove are {}",contentIdsToDelete);
retrieveItemsToDelete(itemsToDelete, itemContentToRetrieve);
} else
retrieveItemsToDelete(itemsToDelete, itemToDelete);
if (!onlyContent)
nodeToDelete.remove();
String ids = itemsToDelete.stream().map((i) -> i.getId()).collect(Collectors.joining(","));
log.debug("content ids to remove are {}",ids);
Set<ContentPair> contentToDelete = retrieveContentToDelete(itemsToDelete);
String user = AuthorizationProvider.instance.get().getClient().getId();
Runnable deleteFromStorageRunnable = AuthorizedTasks.bind(new Runnable() {
@Override
public void run() {
for (String id: contentIdsToDelete) {
for (ContentPair cp: contentToDelete ) {
try {
storageHandler.delete(id);
log.debug("file with id {} correctly removed on storage",id);
cp.getStorageBackend().onDelete(cp.getContent());
log.debug("file with id {} correctly removed from storage {}",cp.getContent().getStorageId(),cp.getStorageBackend().getClass().getSimpleName());
}catch(Throwable t) {
log.warn("error removing file on storage with id {}",id, t);
log.warn("error removing file with id {} from storage {}",cp.getContent().getStorageId(), cp.getStorageBackend().getClass().getSimpleName(), t);
}
}
}
});
new Thread(deleteFromStorageRunnable).start();
ses.save();
executor.execute(deleteFromStorageRunnable);
if (!onlyContent)
ses.save();
}catch (LockException e) {
throw new ItemLockedException("the selected node or his parent is locked", e);
}catch (Exception e) {
@ -104,20 +177,20 @@ public class TrashHandler {
}
}
public void moveToTrash(Session ses, Node nodeToDelete, Item item) throws RepositoryException, BackendGenericError{
log.debug("moving node {} to trash ",item.getId());
final Node trashFolder = ses.getNode(Paths.append(Utils.getWorkspacePath(),Constants.TRASH_ROOT_FOLDER_NAME).toPath());
final String login = AuthorizationProvider.instance.get().getClient().getId();
public void moveToTrash(Session ses, Node nodeToDelete, Item item, String login) throws RepositoryException, BackendGenericError{
log.debug("moving node {} to trash ",item.getId());
final Node trashFolder = ses.getNode(pathUtil.getTrashPath(login, ses).toPath());
try {
ses.getWorkspace().getLockManager().lock(trashFolder.getPath(), true, true, 0,login);
ses.getWorkspace().getLockManager().lock(nodeToDelete.getPath(), true, true, 0,login);
log.debug("preparing thrash item");
TrashItem trashItem = new TrashItem();
trashItem.setDeletedBy(AuthorizationProvider.instance.get().getClient().getId());
trashItem.setDeletedBy(login);
trashItem.setDeletedFrom(nodeToDelete.getParent().getPath());
Calendar now = Calendar.getInstance();
trashItem.setDeletedTime(now);
@ -131,7 +204,7 @@ public class TrashHandler {
trashItem.setName(item.getId());
trashItem.setOriginalParentId(nodeToDelete.getParent().getIdentifier());
trashItem.setOwner(item.getOwner());
trashItem.setOwner(login);
trashItem.setLastModificationTime(item.getLastModificationTime());
trashItem.setLastModifiedBy(item.getLastModifiedBy());
@ -161,7 +234,7 @@ public class TrashHandler {
mimetype = ((AbstractFileItem) item).getContent().getMimeType();
else log.warn("the AbstractFileItem with id {} has no content (check it!!)", item.getId());
}
accountingHandler.createFolderRemoveObj(item.getName(), item.getClass().getSimpleName(), mimetype, ses, ses.getNodeByIdentifier(item.getParentId()), true);
accountingHandler.createFolderRemoveObj(item.getName(), item.getClass().getSimpleName(), mimetype, ses, login, (Node) item.getRelatedNode(), true);
}catch(Throwable t) {
log.error("error exceuting move to trash",t);
throw new BackendGenericError(t);
@ -172,27 +245,69 @@ public class TrashHandler {
}
public String restoreItem(Session ses, TrashItem item) throws RepositoryException, BackendGenericError, UserNotAuthorizedException{
log.debug("restoring node from trash");
final String login = AuthorizationProvider.instance.get().getClient().getId();
public String restoreItem(Session ses, TrashItem item, FolderItem destination, String login) throws RepositoryException, StorageHubException, BackendGenericError{
log.debug("restoring node from trash with user ");
//final Node trashFolder = ses.getNode(Paths.append(Utils.getHomePath(),Constants.TRASH_ROOT_FOLDER_NAME).toPath());
Node originalParent = ses.getNodeByIdentifier(item.getOriginalParentId());
authChecker.checkWriteAuthorizationControl(ses, originalParent.getIdentifier(), false );
ses.getWorkspace().getLockManager().lock(originalParent.getPath(), true, true, 0,login);
List<Item> items = Utils.getItemList(ses.getNodeByIdentifier(item.getId()), Excludes.ALL, null, false, null);
if (items.size()!=1) {
log.warn("a problem occurred restoring item from trash");
throw new BackendGenericError("An error occurred on trash item");
Node destinationNode= null;
if (destination==null) {
boolean originalParentExists = true;
boolean originalParentTrashed = false;
Node originalParent = null;
try {
originalParent = ses.getNodeByIdentifier(item.getOriginalParentId());
}catch (ItemNotFoundException e) {
originalParentExists = false;
}
Item originalParentItem = null;
if (originalParentExists) {
originalParentItem = node2Item.getItem(originalParent, Excludes.ALL);
originalParentTrashed = originalParentItem.isTrashed();
}
if(originalParentExists && !originalParentTrashed) {
destinationNode = originalParent;
}else {
String homeWS = pathUtil.getWorkspacePath(login).toPath();
Node node = ses.getNode(homeWS);
destinationNode = node;
}
} else {
Node node = (Node)destination.getRelatedNode();
if (!node2Item.checkNodeType(node, FolderItem.class))
throw new InvalidCallParameters("destination Node is not a folder");
destinationNode = node;
}
Item itemToMove = items.get(0);
String newNodePath = Paths.append(Paths.getPath(originalParent.getPath()), itemToMove.getName()).toPath();
ses.move(itemToMove.getPath(), newNodePath);
Utils.setPropertyOnChangeNode(ses.getNode(newNodePath), login, ItemAction.MOVED);
ses.removeItem(item.getPath());
ses.save();
return ses.getNode(newNodePath).getIdentifier();
authChecker.checkWriteAuthorizationControl(ses, login, destinationNode.getIdentifier(), true );
ses.getWorkspace().getLockManager().lock(destinationNode.getPath(), true, true, 0,login);
String newNodePath = null;
try {
//the real node is a child of the Trash node
List<Item> items = Utils.getItemList(ses.getNodeByIdentifier(item.getId()), Excludes.ALL, null, false, null);
if (items.size()!=1) {
log.warn("a problem occurred restoring item from trash");
throw new BackendGenericError("An error occurred on trash item");
}
Item itemToMove = items.get(0);
item2Node.updateOwnerOnSubTree(ses.getNodeByIdentifier(itemToMove.getId()), login);
String uniqueName = Utils.checkExistanceAndGetUniqueName(ses, destinationNode, itemToMove.getName());
newNodePath = Paths.append(Paths.getPath(destinationNode.getPath()),uniqueName).toPath();
ses.move(itemToMove.getPath(), newNodePath);
Utils.setPropertyOnChangeNode(ses.getNode(newNodePath), login, ItemAction.MOVED);
ses.removeItem(item.getPath());
ses.save();
}catch (Exception e) {
if (ses.getWorkspace().getLockManager().isLocked(destinationNode.getPath()))
ses.getWorkspace().getLockManager().unlock(destinationNode.getPath());
}
return ses.getNode(newNodePath).getIdentifier();
}
}

@ -8,6 +8,7 @@ import java.util.Set;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.lock.LockException;
@ -27,9 +28,13 @@ import org.gcube.common.storagehub.model.items.FolderItem;
import org.gcube.common.storagehub.model.items.Item;
import org.gcube.common.storagehub.model.items.SharedFolder;
import org.gcube.common.storagehub.model.types.ItemAction;
import org.gcube.common.storagehub.model.types.NodeProperty;
import org.gcube.data.access.storagehub.AuthorizationChecker;
import org.gcube.data.access.storagehub.PathUtil;
import org.gcube.data.access.storagehub.Utils;
import org.gcube.data.access.storagehub.accounting.AccountingHandler;
import org.gcube.data.access.storagehub.handlers.items.Item2NodeConverter;
import org.gcube.data.access.storagehub.handlers.items.Node2ItemConverter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -47,28 +52,41 @@ public class UnshareHandler {
@Inject
AuthorizationChecker authChecker;
@Inject
PathUtil pathUtil;
@Inject
Item2NodeConverter item2Node;
public String unshare(Session ses, Set<String> users, Node sharedNode, String login) throws RepositoryException, StorageHubException{
return _unshare(ses, users, sharedNode, login, true);
}
public String unshareForRemoval(Session ses, Set<String> users, Node sharedNode, String login) throws RepositoryException, StorageHubException{
return _unshare(ses, users, sharedNode, login, false);
}
private String _unshare(Session ses, Set<String> users, Node sharedNode, String login, boolean withCopyOnUnshare) throws RepositoryException, StorageHubException{
Item item = node2Item.getItem(sharedNode, Excludes.ALL);
if (!(item instanceof FolderItem) || !((FolderItem) item).isShared() || ((SharedFolder) item).isVreFolder())
if (!(item instanceof FolderItem) || !((FolderItem) item).isShared() || ((SharedFolder) item).isVreFolder()) {
log.warn("this item type cannot be unshared {}",item.getClass().getSimpleName());
return null;
}
SharedFolder sharedItem =(SharedFolder) item;
Set<String> usersInSharedFolder = new HashSet<>(sharedItem.getUsers().getMap().keySet());
usersInSharedFolder.removeAll(users);
if (users==null || users.size()==0)
return unshareAll(login, ses, sharedItem);
return unshareAll(login, ses, sharedItem, withCopyOnUnshare);
if (usersInSharedFolder.size()<=1) {
if (users.size()==1 && users.contains(login))
return unshareAll(sharedItem.getOwner(), ses , sharedItem);
else return unshareAll(login, ses, sharedItem);
return unshareAll(sharedItem.getOwner(), ses , sharedItem, withCopyOnUnshare);
else return unshareAll(login, ses, sharedItem, withCopyOnUnshare);
}
try {
ses.getWorkspace().getLockManager().lock(sharedNode.getPath(), true, true, 0,login);
}catch (LockException e) {
@ -85,9 +103,10 @@ public class UnshareHandler {
}
private String unshareAll(String login, Session ses, SharedFolder item) throws StorageHubException, BackendGenericError, RepositoryException{
private String unshareAll(String login, Session ses, SharedFolder item, boolean withCopyCreation) throws StorageHubException, BackendGenericError, RepositoryException{
log.info("unshare all called");
if (!login.equals(item.getOwner()))
throw new UserNotAuthorizedException("user "+login+" not authorized to unshare all");
@ -99,37 +118,66 @@ public class UnshareHandler {
throw new ItemLockedException(e);
}
Node unsharedNode;
String unsharedNodeIdentifier =null;
try {
log.debug("user list is empty, I'm going to remove also the shared dir");
//take the admin folder and remove his clone then move the shared folder from share to the user home and change the folder type
String adminDirPath = (String)item.getUsers().getMap().get(login);
String[] splitString = adminDirPath.split("/");
String parentDirectoryId = splitString[0];
String directoryName = splitString[1];
Node parentNode = ses.getNodeByIdentifier(parentDirectoryId);
log.debug("parent node path is {}/{}",parentNode.getPath(), directoryName);
Node adminNode = ses.getNode(String.format("%s/%s",parentNode.getPath(), directoryName));
adminNode.removeShare();
if (withCopyCreation) {
Node sharedOwnerNode = null;
NodeIterator it = sharedItemNode.getSharedSet();
while(it.hasNext()) {
Node node = it.nextNode();
unsharedNode = createUnsharedFolder(ses, parentNode, directoryName, item.getDescription(), login);
log.info("[UNSHARE] checking node {} starts with {} ",node.getPath(),pathUtil.getHome(login).toPath());
if (node.getPath().startsWith(pathUtil.getHome(login).toPath())) {
sharedOwnerNode =node;
break;
}
}
List<Item> itemsToCopy = Utils.getItemList(sharedItemNode, Excludes.ALL, null, true, null);
for (Item itemCopy: itemsToCopy) {
Node itemToCopyNode = ses.getNodeByIdentifier(itemCopy.getId());
log.debug("copying {} to {}", itemToCopyNode.getPath(), unsharedNode.getPath());
ses.move(itemToCopyNode.getPath(), String.format("%s/%s",unsharedNode.getPath(), itemToCopyNode.getName()));
Node shareParent = sharedOwnerNode.getParent();
sharedOwnerNode.removeShare();
Node unsharedNode = createUnsharedFolder(ses, shareParent , item.getTitle() , item.getDescription(), login);
List<Item> itemsToCopy = Utils.getItemList(sharedItemNode, Excludes.ALL, null, true, null);
for (Item itemCopy: itemsToCopy) {
Node itemToCopyNode = ses.getNodeByIdentifier(itemCopy.getId());
log.debug("copying {} to {}", itemToCopyNode.getPath(), unsharedNode.getPath());
ses.move(itemToCopyNode.getPath(), String.format("%s/%s",unsharedNode.getPath(), itemToCopyNode.getName()));
}
unsharedNode.getNode(NodeProperty.ACCOUNTING.toString()).remove();
ses.move(sharedItemNode.getNode(NodeProperty.ACCOUNTING.toString()).getPath(), String.format("%s/%s",unsharedNode.getPath(), NodeProperty.ACCOUNTING.toString()));
//set owner of all the unshared items to the caller
item2Node.updateOwnerOnSubTree(unsharedNode, login);
accountingHandler.createUnshareFolder(sharedItemNode.getProperty(NodeProperty.TITLE.toString()).getString(), ses, "ALL", unsharedNode, false);
unsharedNodeIdentifier = unsharedNode.getIdentifier();
log.info("[UNSHARE] unshared node id {}",unsharedNodeIdentifier);
ses.save();
}
ses.save();
log.debug("all the users have been removed, the folder is totally unshared");
}catch(Throwable t) {
log.error("erro unsharing all",t);
throw t;
}finally {
ses.getWorkspace().getLockManager().unlock(sharedItemNode.getPath());
}
sharedItemNode.removeSharedSet();
ses.save();
log.debug("all the users have been removed, the folder is totally unshared");
return unsharedNode.getIdentifier();
return unsharedNodeIdentifier;
}
@ -137,17 +185,30 @@ public class UnshareHandler {
private String unshareCaller(String login, Session ses, SharedFolder item) throws StorageHubException, RepositoryException{
log.info("unshare caller");
if (login.equals(item.getOwner()))
throw new InvalidCallParameters("the caller is the owner, the folder cannot be unshared");
if (item.getUsers().getMap().get(login)==null)
throw new InvalidCallParameters("the folder is not shared with user "+login);
Node sharedFolderNode =ses.getNodeByIdentifier(item.getId());
String parentId = removeSharingForUser(login, ses, item);
Node sharedFolderNode =ses.getNodeByIdentifier(item.getId());
if (item.getUsers().getMap().get(login)!=null) {
Node usersNode = sharedFolderNode.getNode(NodeConstants.USERS_NAME);
usersNode.remove();
Node newUsersNode = sharedFolderNode.addNode(NodeConstants.USERS_NAME);
item.getUsers().getMap().entrySet().stream().filter(entry -> !entry.getKey().equals(login)).forEach(entry-> {try {
newUsersNode.setProperty(entry.getKey(), (String)entry.getValue());
} catch (Exception e) {
log.error("error adding property to shared node users node under {}",item.getId());
}});
}
Node shareNode = getUserSharingNode(login, ses, item);
String parentId = shareNode.getParent().getIdentifier();
//not returning an error to correct all the old ACL
if (shareNode != null)
shareNode.removeShare();
AccessControlManager acm = ses.getAccessControlManager();
JackrabbitAccessControlList acls = AccessControlUtils.getAccessControlList(acm, sharedFolderNode.getPath());
@ -162,31 +223,22 @@ public class UnshareHandler {
if (entryToDelete!=null)
acls.removeAccessControlEntry(entryToDelete);
log.debug("removed Access control entry for user {}",login);
Node sharedItemNode = ses.getNodeByIdentifier(item.getId());
Node usersNode = sharedItemNode.getNode(NodeConstants.USERS_NAME);
usersNode.remove();
Node newUsersNode = sharedItemNode.addNode(NodeConstants.USERS_NAME);
item.getUsers().getMap().entrySet().stream().filter(entry -> !entry.getKey().equals(login)).forEach(entry-> {try {
newUsersNode.setProperty(entry.getKey(), (String)entry.getValue());
} catch (Exception e) {
log.error("error adding property to shared node users node under {}",item.getId());
}});
acm.setPolicy(sharedFolderNode.getPath(), acls);
log.debug("removed Access control entry for user {}",login);
accountingHandler.createUnshareFolder(item.getTitle(), ses, login, sharedFolderNode, false);
ses.save();
return parentId;
}
private String unsharePartial(Set<String> usersToUnshare, String login, Session ses, SharedFolder item) throws StorageHubException, RepositoryException {
authChecker.checkAdministratorControl(ses, (SharedFolder)item);
log.info("unshare partial");
authChecker.checkAdministratorControl(ses, login, (SharedFolder)item);
if (usersToUnshare.contains(item.getOwner()))
throw new UserNotAuthorizedException("user "+login+" not authorized to unshare owner");
@ -196,7 +248,11 @@ public class UnshareHandler {
JackrabbitAccessControlList acls = AccessControlUtils.getAccessControlList(acm, sharedFolderNode.getPath());
for (String user : usersToUnshare) {
removeSharingForUser(user, ses, item);
Node userShareNode = getUserSharingNode(user, ses, item);
//not returning an error to correct all the old ACL
if (userShareNode != null)
userShareNode.removeShare();
AccessControlEntry entryToDelete= null;
for (AccessControlEntry ace :acls.getAccessControlEntries()) {
@ -208,11 +264,11 @@ public class UnshareHandler {
}
if (entryToDelete!=null)
acls.removeAccessControlEntry(entryToDelete);
log.debug("removed Access control entry for user {}",user);
}
Node sharedItemNode = ses.getNodeByIdentifier(item.getId());
Node usersNode = sharedItemNode.getNode(NodeConstants.USERS_NAME);
usersNode.remove();
@ -226,6 +282,10 @@ public class UnshareHandler {
acm.setPolicy(sharedFolderNode.getPath(), acls);
for (String user: usersToUnshare) {
accountingHandler.createUnshareFolder(sharedItemNode.getProperty(NodeProperty.TITLE.toString()).getString(), ses, user, sharedItemNode, false);
}
ses.save();
return item.getId();
@ -233,18 +293,34 @@ public class UnshareHandler {
}
private String removeSharingForUser(String user, Session ses, SharedFolder item) throws RepositoryException {
String userDirPath = (String)item.getUsers().getMap().get(user);
if (userDirPath==null) return null;
String[] splitString = userDirPath.split("/");
String parentDirectoryId = splitString[0];
String directoryName = splitString[1];
Node parentNode = ses.getNodeByIdentifier(parentDirectoryId);
Node userNode = ses.getNode(String.format("%s/%s",parentNode.getPath(), directoryName));
userNode.removeShare();
accountingHandler.createUnshareFolder(directoryName, ses, parentNode, false);
log.debug("directory removed for user {}",user);
return parentDirectoryId;
private Node getUserSharingNode(String user, Session ses, SharedFolder item) throws RepositoryException {
Node shareNode = null;
try {
String userDirPath = (String)item.getUsers().getMap().get(user);
if (userDirPath==null) return null;
String[] splitString = userDirPath.split("/");
String parentDirectoryId = splitString[0];
String directoryName = splitString[1];
Node parentNode = null;
parentNode = ses.getNodeByIdentifier(parentDirectoryId);
shareNode = ses.getNode(String.format("%s/%s",parentNode.getPath(), directoryName));
}catch (Throwable e) {
log.warn("users map is not containing a valid value");
}
Node sharedFolderNode = ses.getNodeByIdentifier(item.getId());
if (shareNode==null) {
NodeIterator it = sharedFolderNode.getSharedSet();
while(it.hasNext()) {
Node node = it.nextNode();
if (node.getPath().startsWith(pathUtil.getHome(user).toPath())) {
shareNode =node;
break;
}
}
}
return shareNode;
}

@ -1,59 +0,0 @@
package org.gcube.data.access.storagehub.handlers;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.jcr.SimpleCredentials;
import javax.servlet.ServletContext;
import org.gcube.common.storagehub.model.items.Item;
import org.gcube.data.access.storagehub.Constants;
import org.gcube.data.access.storagehub.services.RepositoryInitializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Singleton
public class VREManager {
private static final Logger logger = LoggerFactory.getLogger(VREManager.class);
private Map<String, VRE> vreMap = new HashMap<>();
@Inject
RepositoryInitializer repository;
ExecutorService executor = Executors.newFixedThreadPool(5);
SimpleCredentials credentials;
@Inject
public VREManager(ServletContext context) {
credentials = new SimpleCredentials(context.getInitParameter(Constants.ADMIN_PARAM_NAME),context.getInitParameter(Constants.ADMIN_PARAM_PWD).toCharArray());
}
public synchronized VRE getVRE(String completeName) {
logger.trace("requesting VRE {}",completeName);
if (vreMap.containsKey(completeName))
return vreMap.get(completeName);
else
return null;
}
public synchronized VRE putVRE(Item vreFolder) {
logger.trace("inserting VRE {}",vreFolder.getTitle());
if (vreMap.containsKey(vreFolder.getTitle())) throw new RuntimeException("something went wrong (vre already present in the map)");
else {
VRE toReturn = new VRE(vreFolder, repository.getRepository(), credentials, executor);
vreMap.put(vreFolder.getTitle(), toReturn);
return toReturn;
}
}
}

@ -1,192 +0,0 @@
package org.gcube.data.access.storagehub.handlers;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.Callable;
import javax.jcr.Credentials;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.Property;
import javax.jcr.Repository;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.observation.Event;
import javax.jcr.observation.EventJournal;
import javax.jcr.query.Query;
import org.gcube.common.storagehub.model.Excludes;
import org.gcube.common.storagehub.model.NodeConstants;
import org.gcube.common.storagehub.model.items.Item;
import org.gcube.data.access.storagehub.Constants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class VREQueryRetriever implements Callable<List<Item>> {
private static final Logger logger = LoggerFactory.getLogger(VREQueryRetriever.class);
private static final int CACHE_DIMENSION = 50;
private Repository repository;
private Credentials credentials;
private Item vreFolder;
List<Item> cachedList = new ArrayList<>(CACHE_DIMENSION);
long lastTimestamp =0;
private Node2ItemConverter node2Item = new Node2ItemConverter();
public VREQueryRetriever(Repository repository, Credentials credentials, Item vreFolder) {
super();
this.repository = repository;
this.credentials = credentials;
this.vreFolder = vreFolder;
}
public List<Item> call() {
logger.trace("executing recents task");
Session ses = null;
if (lastTimestamp==0) {
try {
long start = System.currentTimeMillis();
ses = repository.login(credentials);
String query = String.format("SELECT * FROM [nthl:workspaceLeafItem] AS node WHERE ISDESCENDANTNODE('%s') ORDER BY node.[jcr:lastModified] DESC ",vreFolder.getPath());
logger.trace("query for recents is {}",query);
Query jcrQuery = ses.getWorkspace().getQueryManager().createQuery(query, Constants.QUERY_LANGUAGE);
jcrQuery.setLimit(CACHE_DIMENSION);
lastTimestamp = System.currentTimeMillis();
NodeIterator it = jcrQuery.execute().getNodes();
logger.trace("query for recents took {}",System.currentTimeMillis()-start);
while (it.hasNext()) {
Node node = it.nextNode();
Item item =node2Item.getItem(node, Excludes.EXCLUDE_ACCOUNTING);
cachedList.add(item);
logger.trace("adding item {} with node {}",item.getTitle(), node.getName());
}
logger.trace("creating objects took {}",System.currentTimeMillis()-start);
if (cachedList.size()<=10) return cachedList;
else return cachedList.subList(0, 10);
} catch (Exception e) {
logger.error("error querying vre {}",vreFolder.getTitle(),e);
throw new RuntimeException(e);
}finally{
if (ses!=null)
ses.logout();
logger.trace("recents task finished");
}
} else {
try {
long timestampToUse = lastTimestamp;
lastTimestamp = System.currentTimeMillis();
long start = System.currentTimeMillis();
ses = repository.login(credentials);
final String[] types = { "nthl:workspaceLeafItem", "nthl:workspaceItem"};
EventJournal journalChanged = ses.getWorkspace().getObservationManager().getEventJournal(Event.PROPERTY_CHANGED^Event.NODE_REMOVED^Event.NODE_MOVED^Event.NODE_ADDED, vreFolder.getPath(), true, null, types);
journalChanged.skipTo(timestampToUse);
logger.trace("getting the journal took {}",System.currentTimeMillis()-start);
int events = 0;
while (journalChanged.hasNext()) {
events++;
Event event = journalChanged.nextEvent();
switch(event.getType()) {
case Event.NODE_ADDED:
if (ses.nodeExists(event.getPath())) {
Node nodeAdded = ses.getNode(event.getPath());
if (nodeAdded.isNodeType("nthl:workspaceLeafItem")) {
logger.trace("node added event received with name {}", nodeAdded.getName());
Item item = node2Item.getItem(nodeAdded, Arrays.asList(NodeConstants.ACCOUNTING_NAME));
insertItemInTheRightPlace(item);
}
}
break;
case Event.PROPERTY_CHANGED:
if (ses.propertyExists(event.getPath())) {
Property property = ses.getProperty(event.getPath());
if (property.getName().equalsIgnoreCase("jcr:lastModified")) {
logger.trace("event property changed on {} with value {} and parent {}",property.getName(), property.getValue().getString(), property.getParent().getPath());
String identifier = property.getParent().getIdentifier();
cachedList.removeIf(i -> i.getId().equals(identifier));
Item item = node2Item.getItem(property.getParent(), Excludes.EXCLUDE_ACCOUNTING);
insertItemInTheRightPlace(item);
}
}
break;
case Event.NODE_REMOVED:
logger.trace("node removed event received with type {}", event.getIdentifier());
cachedList.removeIf(i -> {
try {
return i.getId().equals(event.getIdentifier()) && i.getLastModificationTime().getTime().getTime()<event.getDate();
} catch (RepositoryException e) {
return false;
}
});
break;
case Event.NODE_MOVED:
Node nodeMoved = ses.getNode(event.getPath());
logger.trace("node moved event received with type {}", nodeMoved.getPrimaryNodeType());
if (nodeMoved.isNodeType("nthl:workspaceLeafItem")) {
logger.trace("event node moved on {} with path {}",nodeMoved.getName(), nodeMoved.getPath());
String identifier = nodeMoved.getIdentifier();
cachedList.removeIf(i -> i.getId().equals(identifier) && !i.getPath().startsWith(vreFolder.getPath()));
}
break;
default:
throw new Exception("error in event handling");
}
}
if (cachedList.size()>CACHE_DIMENSION)
cachedList.subList(51, cachedList.size()).clear();
logger.trace("retrieving event took {} with {} events",System.currentTimeMillis()-start, events);
if (cachedList.size()<=10) return cachedList;
else return cachedList.subList(0, 10);
} catch (Exception e) {
logger.error("error getting events for vre {}",vreFolder.getTitle(),e);
throw new RuntimeException(e);
}finally{
if (ses!=null)
ses.logout();
}
}
}
private void insertItemInTheRightPlace(Item item) {
Iterator<Item> it = cachedList.iterator();
int index =0;
while (it.hasNext()) {
Item inListItem = it.next();
if (item.getLastModificationTime().getTime().getTime()>=inListItem.getLastModificationTime().getTime().getTime()) break;
index++;
}
if (index<CACHE_DIMENSION)
cachedList.add(index, item);
}
/* @Override
public void onEvent(EventIterator events) {
logger.trace("on event called");
while (events.hasNext()) {
Event event = events.nextEvent();
try {
logger.trace("new event received of type {} on node {}",event.getType(),event.getIdentifier());
} catch (RepositoryException e) {
logger.error("error reading event",e);
}
}
}*/
}

@ -22,7 +22,7 @@ public class VersionHandler {
private static final Logger logger = LoggerFactory.getLogger(VersionHandler.class);
public void makeVersionableContent(Node node, Session session){
public void makeVersionableContent(Node node){
try {
Node contentNode = node.getNode(NodeConstants.CONTENT_NAME);
contentNode.addMixin(JcrConstants.MIX_VERSIONABLE);
@ -31,8 +31,9 @@ public class VersionHandler {
}
}
public void checkinContentNode(Node node, Session session){
public void checkinContentNode(Node node){
try {
Session session = node.getSession();
Node contentNode = node.getNode(NodeConstants.CONTENT_NAME);
VersionManager versionManager = session.getWorkspace().getVersionManager();
versionManager.checkin(contentNode.getPath());
@ -41,8 +42,9 @@ public class VersionHandler {
}
}
public void checkoutContentNode(Node node, Session session){
public void checkoutContentNode(Node node){
try {
Session session = node.getSession();
Node contentNode = node.getNode(NodeConstants.CONTENT_NAME);
VersionManager versionManager = session.getWorkspace().getVersionManager();
versionManager.checkout(contentNode.getPath());
@ -51,8 +53,9 @@ public class VersionHandler {
}
}
public List<Version> getContentVersionHistory(Node node, Session session) {
public List<Version> getContentVersionHistory(Node node) {
try {
Session session = node.getSession();
Node contentNode = node.getNode(NodeConstants.CONTENT_NAME);
VersionManager versionManager = session.getWorkspace().getVersionManager();
VersionHistory history = versionManager.getVersionHistory(contentNode.getPath());

@ -3,6 +3,7 @@ package org.gcube.data.access.storagehub.handlers.content;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Set;
import javax.inject.Singleton;
import org.gcube.common.storagehub.model.annotations.MimeTypeHandler;

@ -7,7 +7,6 @@ import org.apache.tika.metadata.Metadata;
import org.apache.tika.parser.ParseContext;
import org.apache.tika.parser.microsoft.ooxml.OOXMLParser;
import org.apache.tika.sax.BodyContentHandler;
import org.gcube.common.storagehub.model.annotations.MimeTypeHandler;
import org.gcube.common.storagehub.model.items.GenericFileItem;
import org.gcube.common.storagehub.model.items.nodes.Content;
import org.gcube.common.storagehub.model.types.ItemAction;

@ -8,6 +8,8 @@ import org.gcube.common.storagehub.model.annotations.MimeTypeHandler;
import org.gcube.common.storagehub.model.items.PDFFileItem;
import org.gcube.common.storagehub.model.items.nodes.PDFContent;
import org.gcube.common.storagehub.model.types.ItemAction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.itextpdf.text.pdf.PdfReader;
@ -22,20 +24,23 @@ public class PdfHandler implements ContentHandler {
PDFContent content = new PDFContent();
private static final Logger logger = LoggerFactory.getLogger(PdfHandler.class);
@Override
public void initiliseSpecificContent(InputStream is, String fileName, String mimeType) throws Exception {
PdfReader reader = new PdfReader(is);
content.setNumberOfPages(Long.valueOf(reader.getNumberOfPages()));
content.setVersion(String.valueOf(reader.getPdfVersion()));
HashMap<String, String> fileInfo = reader.getInfo();
content.setAuthor(fileInfo.containsKey(AUTHOR)?fileInfo.get(AUTHOR):"n/a");
content.setProducer(fileInfo.containsKey(PRODUCER)?fileInfo.get(PRODUCER):"n/a");
content.setTitle(fileInfo.containsKey(TITLE)?fileInfo.get(TITLE):"n/a");
try {
PdfReader reader = new PdfReader(is);
content.setNumberOfPages(Long.valueOf(reader.getNumberOfPages()));
content.setVersion(String.valueOf(reader.getPdfVersion()));
HashMap<String, String> fileInfo = reader.getInfo();
content.setAuthor(fileInfo.containsKey(AUTHOR)?fileInfo.get(AUTHOR):"n/a");
content.setProducer(fileInfo.containsKey(PRODUCER)?fileInfo.get(PRODUCER):"n/a");
content.setTitle(fileInfo.containsKey(TITLE)?fileInfo.get(TITLE):"n/a");
} catch (Exception e) {
logger.warn("{} is not a valid pdf", fileName, e);
}
content.setMimeType(mimeType);
}
@Override
} @Override
public PDFContent getContent() {
return content;
}

@ -1,4 +1,4 @@
package org.gcube.data.access.storagehub.handlers;
package org.gcube.data.access.storagehub.handlers.items;
import java.lang.reflect.Field;
import java.net.URL;
@ -31,11 +31,16 @@ import org.gcube.common.storagehub.model.annotations.ListNodes;
import org.gcube.common.storagehub.model.annotations.MapAttribute;
import org.gcube.common.storagehub.model.annotations.NodeAttribute;
import org.gcube.common.storagehub.model.annotations.RootNode;
import org.gcube.common.storagehub.model.exceptions.BackendGenericError;
import org.gcube.common.storagehub.model.items.AbstractFileItem;
import org.gcube.common.storagehub.model.items.FolderItem;
import org.gcube.common.storagehub.model.items.Item;
import org.gcube.common.storagehub.model.items.RootItem;
import org.gcube.common.storagehub.model.types.ItemAction;
import org.gcube.common.storagehub.model.types.NodeProperty;
import org.gcube.data.access.storagehub.NodeChildrenFilterIterator;
import org.gcube.data.access.storagehub.Utils;
import org.gcube.data.access.storagehub.handlers.ClassHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -44,7 +49,7 @@ public class Item2NodeConverter {
private static final Logger logger = LoggerFactory.getLogger(Item2NodeConverter.class);
public <T extends Item> Node getNode(Node parentNode, T item){
public <T extends RootItem> Node getNode(Node parentNode, T item){
try {
String primaryType= ClassHandler.instance().getNodeType(item.getClass());
Node newNode = parentNode.addNode(Text.escapeIllegalJcrChars(item.getName()), primaryType);
@ -56,7 +61,7 @@ public class Item2NodeConverter {
field.setAccessible(true);
try{
//Class<?> returnType = field.getType();
logger.debug("creating node - added field {}",field.getName());
logger.trace("creating node - added field {}",field.getName());
Values values = getObjectValue(field.getType(), field.get(item));
if (values.isMulti()) newNode.setProperty(attribute.value(), values.getValues());
else newNode.setProperty(attribute.value(), values.getValue());
@ -67,14 +72,14 @@ public class Item2NodeConverter {
NodeAttribute nodeAttribute = field.getAnnotation(NodeAttribute.class);
if (nodeAttribute.isReadOnly()) continue;
String nodeName = nodeAttribute.value();
logger.debug("retrieving field node "+field.getName());
logger.trace("retrieving field node "+field.getName());
field.setAccessible(true);
try{
Object obj = field.get(item);
if (obj!=null)
iterateItemNodeAttributeFields(obj, newNode, nodeName);
} catch (Exception e ) {
logger.warn("error setting value",e);
logger.debug("error setting value",e);
}
@ -113,7 +118,7 @@ public class Item2NodeConverter {
if (values.isMulti()) newNode.setProperty(attribute.value(), values.getValues());
else newNode.setProperty(attribute.value(), values.getValue());
} catch (Exception e ) {
logger.warn("error setting value",e);
logger.debug("error setting value",e);
}
} else if (field.isAnnotationPresent(MapAttribute.class)){
//logger.debug("found field {} of type annotated as MapAttribute in class {}", field.getName(), clazz.getName());
@ -125,11 +130,11 @@ public class Item2NodeConverter {
if (values.isMulti()) newNode.setProperty(entry.getKey(), values.getValues());
else newNode.setProperty(entry.getKey(), values.getValue());
} catch (Exception e ) {
logger.warn("error setting value",e);
logger.debug("error setting value",e);
}
} else if (field.isAnnotationPresent(ListNodes.class)){
logger.debug("found field {} of type annotated as ListNodes in class {} on node {}", field.getName(), object.getClass().getName(), newNode.getName());
logger.trace("found field {} of type annotated as ListNodes in class {} on node {}", field.getName(), object.getClass().getName(), newNode.getName());
field.setAccessible(true);
List<Object> toSetList = (List<Object>) field.get(object);
@ -145,7 +150,7 @@ public class Item2NodeConverter {
}
@SuppressWarnings({ "rawtypes" })
private Values getObjectValue(Class returnType, Object value) throws Exception{
public static Values getObjectValue(Class returnType, Object value) throws Exception{
if (returnType.equals(String.class)) return new Values(new StringValue((String) value));
if (returnType.isEnum()) return new Values(new StringValue(((Enum) value).toString()));
@ -204,7 +209,7 @@ public class Item2NodeConverter {
else contentNode.setProperty(attribute.value(), values.getValue());
} catch (Exception e ) {
logger.warn("error setting value for attribute "+attribute.value(),e);
logger.debug("error setting value for attribute "+attribute.value(),e);
}
}
}
@ -216,11 +221,41 @@ public class Item2NodeConverter {
}
public void updateHidden(Node node, Boolean hidden,String login) throws RepositoryException {
Utils.setPropertyOnChangeNode(node, login, ItemAction.UPDATED);
node.setProperty(NodeProperty.HIDDEN.toString(), hidden);
}
public void updateDescription(Node node, String description,String login) throws RepositoryException {
Utils.setPropertyOnChangeNode(node, login, ItemAction.UPDATED);
node.setProperty(NodeProperty.DESCRIPTION.toString(), description);
}
public void updateOwnerOnSubTree(Node node, String owner) throws RepositoryException, BackendGenericError {
Class<? extends Item> classToHandle = (Class<? extends Item>)ClassHandler.instance().get(node.getPrimaryNodeType().getName());
if (classToHandle==null) return;
if (classToHandle.isAssignableFrom(FolderItem.class)) {
NodeChildrenFilterIterator iterator = new NodeChildrenFilterIterator(node);
while (iterator.hasNext()) {
Node nextNode = iterator.next();
updateOwnerOnSubTree(nextNode, owner);
}
}
updateOwner(node, owner);
}
public void updateOwner(Node node, String owner) throws RepositoryException {
//Utils.setPropertyOnChangeNode(node, login, ItemAction.UPDATED);
if (node.hasProperty(NodeProperty.PORTAL_LOGIN.toString()))
node.setProperty(NodeProperty.PORTAL_LOGIN.toString(), owner);
else logger.debug("cannot set new owner to {} "+node.getPath());
}
public <I extends Item> void updateMetadataNode(Node node, Map<String, Object> meta, String login){
try {
//TODO: make a method to update item not only metadata, check if the new metadata has an intersection with the old one to remove properties not needed
Utils.setPropertyOnChangeNode(node, login, ItemAction.UPDATED);
Node metadataNode;
@ -246,7 +281,7 @@ public class Item2NodeConverter {
}
} catch (Exception e ) {
logger.warn("error setting value",e);
logger.debug("error setting value",e);
}
}

@ -0,0 +1,400 @@
package org.gcube.data.access.storagehub.handlers.items;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.jcr.Node;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.lock.LockException;
import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.ArchiveInputStream;
import org.apache.commons.compress.archivers.ArchiveStreamFactory;
import org.apache.tika.config.TikaConfig;
import org.apache.tika.detect.Detector;
import org.apache.tika.io.TikaInputStream;
import org.apache.tika.metadata.Metadata;
import org.gcube.common.authorization.library.AuthorizedTasks;
import org.gcube.common.storagehub.model.Excludes;
import org.gcube.common.storagehub.model.NodeConstants;
import org.gcube.common.storagehub.model.Paths;
import org.gcube.common.storagehub.model.exceptions.BackendGenericError;
import org.gcube.common.storagehub.model.exceptions.IdNotFoundException;
import org.gcube.common.storagehub.model.exceptions.InvalidCallParameters;
import org.gcube.common.storagehub.model.exceptions.InvalidItemException;
import org.gcube.common.storagehub.model.exceptions.ItemLockedException;
import org.gcube.common.storagehub.model.exceptions.StorageHubException;
import org.gcube.common.storagehub.model.items.AbstractFileItem;
import org.gcube.common.storagehub.model.items.FolderItem;
import org.gcube.common.storagehub.model.plugins.FolderManager;
import org.gcube.common.storagehub.model.storages.MetaInfo;
import org.gcube.common.storagehub.model.storages.StorageBackend;
import org.gcube.common.storagehub.model.types.ItemAction;
import org.gcube.data.access.storagehub.AuthorizationChecker;
import org.gcube.data.access.storagehub.MultipleOutputStream;
import org.gcube.data.access.storagehub.Utils;
import org.gcube.data.access.storagehub.accounting.AccountingHandler;
import org.gcube.data.access.storagehub.handlers.VersionHandler;
import org.gcube.data.access.storagehub.handlers.content.ContentHandler;
import org.gcube.data.access.storagehub.handlers.content.ContentHandlerFactory;
import org.gcube.data.access.storagehub.handlers.items.builders.ArchiveStructureCreationParameter;
import org.gcube.data.access.storagehub.handlers.items.builders.CreateParameters;
import org.gcube.data.access.storagehub.handlers.items.builders.FileCreationParameters;
import org.gcube.data.access.storagehub.handlers.items.builders.FolderCreationParameters;
import org.gcube.data.access.storagehub.handlers.items.builders.GCubeItemCreationParameters;
import org.gcube.data.access.storagehub.handlers.items.builders.URLCreationParameters;
import org.gcube.data.access.storagehub.handlers.plugins.FolderPluginHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Singleton
public class ItemHandler {
@Inject
AccountingHandler accountingHandler;
@Inject
ContentHandlerFactory contenthandlerFactory;
@Inject
AuthorizationChecker authChecker;
@Inject
VersionHandler versionHandler;
@Inject
FolderPluginHandler pluginHandler;
private static ExecutorService executor = Executors.newFixedThreadPool(100);
@Inject Node2ItemConverter node2Item;
@Inject Item2NodeConverter item2Node;
private static Logger log = LoggerFactory.getLogger(ItemHandler.class);
public <T extends CreateParameters> String create(T parameters) throws Exception{
Session ses = parameters.getSession();
Node destination;
try {
destination = ses.getNodeByIdentifier(parameters.getParentId());
}catch(RepositoryException inf) {
throw new IdNotFoundException(parameters.getParentId());
}
if (!node2Item.checkNodeType(destination, FolderItem.class))
throw new InvalidItemException("the destination item is not a folder");
authChecker.checkWriteAuthorizationControl(ses, parameters.getUser(), destination.getIdentifier(), true);
try {
Node newNode = null;
switch (parameters.getMangedType()) {
case FILE:
newNode = create((FileCreationParameters)parameters, destination);
break;
case FOLDER:
newNode = create((FolderCreationParameters)parameters, destination);
break;
case ARCHIVE:
newNode = create((ArchiveStructureCreationParameter)parameters, destination);
break;
case URL:
newNode = create((URLCreationParameters) parameters, destination);
break;
case GCUBEITEM:
newNode = create((GCubeItemCreationParameters) parameters, destination);
break;
default:
throw new InvalidCallParameters("Item not supported");
}
log.debug("item with id {} correctly created",newNode.getIdentifier());
return newNode.getIdentifier();
} finally {
if (parameters.getSession().getWorkspace().getLockManager().isLocked(destination.getPath()))
parameters.getSession().getWorkspace().getLockManager().unlock(destination.getPath());
}
}
private Node create(FolderCreationParameters params, Node destination) throws Exception{
Utils.acquireLockWithWait(params.getSession(), destination.getPath(), false, params.getUser(), 10);
Node newNode = Utils.createFolderInternally(params, accountingHandler);
params.getSession().save();
return newNode;
}
private Node create(FileCreationParameters params, Node destination) throws Exception{
Node newNode = createFileItemInternally(params.getSession(), destination, params.getStream(), params.getName(), params.getDescription(), params.getUser(), true);
params.getSession().save();
versionHandler.checkinContentNode(newNode);
log.info("file with id {} correctly created",newNode.getIdentifier());
return newNode;
}
private Node create(URLCreationParameters params, Node destination) throws Exception{
Utils.acquireLockWithWait(params.getSession(), destination.getPath(), false, params.getUser(), 10);
Node newNode = Utils.createURLInternally(params.getSession(), destination, params.getName(), params.getUrl(), params.getDescription(), params.getUser(), accountingHandler);
params.getSession().save();
return newNode;
}
private Node create(ArchiveStructureCreationParameter params, Node destination) throws Exception{
Utils.acquireLockWithWait(params.getSession(), destination.getPath(), false, params.getUser(), 10);
FolderCreationParameters folderParameters = FolderCreationParameters.builder().name(params.getParentFolderName()).author(params.getUser()).on(destination.getIdentifier()).with(params.getSession()).build();
Node parentDirectoryNode = Utils.createFolderInternally(folderParameters, accountingHandler);
params.getSession().save();
try {
if (params.getSession().getWorkspace().getLockManager().isLocked(destination.getPath()))
params.getSession().getWorkspace().getLockManager().unlock(destination.getPath());
} catch (Throwable t){
log.warn("error unlocking {}", destination.getPath(), t);
}
Set<Node> fileNodes = new HashSet<>();
HashMap<String, Node> directoryNodeMap = new HashMap<>();
try (ArchiveInputStream input = new ArchiveStreamFactory()
.createArchiveInputStream(new BufferedInputStream(params.getStream(), 1024*64))){
ArchiveEntry entry;
while ((entry = input.getNextEntry()) != null) {
String entirePath = entry.getName();
if (entry.isDirectory()) {
log.debug("creating directory with entire path {} ", entirePath);
createPath(entirePath, directoryNodeMap, parentDirectoryNode, params.getSession(), params.getUser());
continue;
} else {
try {
String name = entirePath.replaceAll("([^/]*/)*(.*)", "$2");
String parentPath = entirePath.replaceAll("(([^/]*/)*)(.*)", "$1");
log.debug("creating file with entire path {}, name {}, parentPath {} ", entirePath, name, parentPath);
Node fileNode = null;
if (parentPath.isEmpty())
fileNode = createFileItemInternally(params.getSession(), parentDirectoryNode, input, name, "", params.getUser(), false);
else {
Node parentNode = directoryNodeMap.get(parentPath);
if (parentNode ==null)
parentNode = createPath(parentPath, directoryNodeMap, parentDirectoryNode, params.getSession(), params.getUser());
fileNode = createFileItemInternally(params.getSession(), parentNode, input, name, "", params.getUser(), false);
}
fileNodes.add(fileNode);
}catch(Exception e) {
log.warn("error getting file {}",entry.getName(),e);
}
}
}
}
params.getSession().save();
for (Node node : fileNodes)
versionHandler.checkinContentNode(node);
return parentDirectoryNode;
}
private Node createPath(String parentPath, Map<String, Node> directoryNodeMap, Node rootNode, Session ses, String user) throws StorageHubException, RepositoryException{
String[] parentPathSplit = parentPath.split("/");
String name = parentPathSplit[parentPathSplit.length-1];
StringBuilder relParentPath = new StringBuilder();
for (int i = 0 ; i<=parentPathSplit.length-2; i++)
relParentPath.append(parentPathSplit[i]).append("/");
if (relParentPath.toString().isEmpty()) {
FolderCreationParameters folderParameters = FolderCreationParameters.builder().name(name).author(user).on(rootNode.getIdentifier()).with(ses).build();
Node createdNode = Utils.createFolderInternally(folderParameters, accountingHandler);
directoryNodeMap.put(name+"/", createdNode);
return createdNode;
}else {
Node relParentNode = directoryNodeMap.get(relParentPath.toString());
if (relParentNode==null) {
relParentNode = createPath(relParentPath.toString(), directoryNodeMap, rootNode, ses, user);
}
FolderCreationParameters folderParameters = FolderCreationParameters.builder().name(name).author(user).on(relParentNode.getIdentifier()).with(ses).build();
Node createdNode = Utils.createFolderInternally(folderParameters, accountingHandler);
directoryNodeMap.put(relParentPath.append(name).append("/").toString(), createdNode);
return createdNode;
}
}
private Node create(GCubeItemCreationParameters params, Node destination) throws Exception{
Utils.acquireLockWithWait(params.getSession(), destination.getPath(), false, params.getUser(), 10);
Node newNode = Utils.createGcubeItemInternally(params.getSession(), destination, params.getItem().getName(), params.getItem().getDescription(), params.getUser(), params.getItem(), accountingHandler);
params.getSession().save();
return newNode;
}
private Node createFileItemInternally(Session ses, Node destinationNode, InputStream stream, String name, String description, String login, boolean withLock) throws RepositoryException, StorageHubException{
Node newNode;
FolderItem destinationItem = node2Item.getItem(destinationNode, Excludes.ALL);
FolderManager folderManager = pluginHandler.getFolderManager(destinationItem);
StorageBackend storageBackend = folderManager.getStorageBackend();
String relativePath = destinationNode.getPath();
if (destinationItem.isExternalManaged())
relativePath = relativePath.replace(folderManager.getRootFolder().getPath(), "");
String newNodePath = Paths.append(Paths.getPath(destinationNode.getPath()), name).toPath();
if (ses.nodeExists(newNodePath)) {
if (!folderManager.manageVersion())
throw new InvalidCallParameters("storage for plugin "+folderManager.getClass().getName()+" doesn't support versioning");
newNode = ses.getNode(newNodePath);
authChecker.checkWriteAuthorizationControl(ses, login, newNode.getIdentifier(), false);
AbstractFileItem item = fillItemWithContent(stream, storageBackend, name, description, relativePath,login);
if (withLock) {
try {
ses.getWorkspace().getLockManager().lock(newNode.getPath(), true, true, 0,login);
}catch (LockException le) {
throw new ItemLockedException(le);
}
}
try {
versionHandler.checkoutContentNode(newNode);
log.trace("replacing content of class {}",item.getContent().getClass());
item2Node.replaceContent(newNode,item, ItemAction.UPDATED);
accountingHandler.createFileUpdated(item.getTitle(), ses, newNode, login, false);
ses.save();
}finally {
if (withLock) ses.getWorkspace().getLockManager().unlock(newNode.getPath());
}
}
else {
authChecker.checkWriteAuthorizationControl(ses, login, destinationNode.getIdentifier(), true);
AbstractFileItem item = fillItemWithContent(stream, storageBackend, name, description, relativePath, login);
if (withLock) {
try {
log.debug("trying to acquire lock");
Utils.acquireLockWithWait(ses, destinationNode.getPath(), false, login, 10);
}catch (LockException le) {
throw new ItemLockedException(le);
}
}
try {
newNode = item2Node.getNode(destinationNode, item);
accountingHandler.createEntryCreate(item.getTitle(), ses, newNode, login, false);
ses.save();
}finally {
if (withLock) ses.getWorkspace().getLockManager().unlock(destinationNode.getPath());
}
versionHandler.makeVersionableContent(newNode);
accountingHandler.createFolderAddObj(name, item.getClass().getSimpleName(), item.getContent().getMimeType(), ses, login, destinationNode, false);
}
return newNode;
}
private AbstractFileItem fillItemWithContent(InputStream stream, StorageBackend storageBackend, String name, String description, String relPath, String login) throws BackendGenericError{
ContentHandler handler = getContentHandler(stream, storageBackend, name, relPath, login);
AbstractFileItem item =handler.buildItem(name, description, login);
return item ;
}
private ContentHandler getContentHandler(InputStream stream, StorageBackend storageBackend, String name, String relPath, String login) throws BackendGenericError {
final MultipleOutputStream mos;
try{
mos = new MultipleOutputStream(stream, 2);
}catch (IOException e) {
throw new BackendGenericError(e);
}
Callable<ContentHandler> mimeTypeDector = new Callable<ContentHandler>() {
@Override
public ContentHandler call() throws Exception {
ContentHandler handler =null;
long start = System.currentTimeMillis();
log.debug("TIMING: reading the mimetype - start");
try(InputStream is1 = new BufferedInputStream(mos.get(), 1024*64)){
org.apache.tika.mime.MediaType mediaType = null;
TikaConfig config = TikaConfig.getDefaultConfig();
Detector detector = config.getDetector();
TikaInputStream stream = TikaInputStream.get(is1);
Metadata metadata = new Metadata();
metadata.add(Metadata.RESOURCE_NAME_KEY, name);
mediaType = detector.detect(stream, metadata);
String mimeType = mediaType.getBaseType().toString();
handler = contenthandlerFactory.create(mimeType);
is1.reset();
handler.initiliseSpecificContent(is1, name, mimeType);
log.trace("TIMING: reading the mimetype - finished in {}",System.currentTimeMillis()-start);
} catch (Throwable e) {
log.error("error retrieving mimeType",e);
throw new RuntimeException(e);
}
return handler;
}
};
Callable<MetaInfo> uploader = new Callable<MetaInfo>() {
@Override
public MetaInfo call() throws Exception {
try(InputStream is1 = mos.get()){
MetaInfo info = storageBackend.upload(is1, relPath, name);
return info;
}catch (Throwable e) {
log.error("error writing content",e );
throw e;
}
}
};
Future<ContentHandler> detectorF = executor.submit(AuthorizedTasks.bind(mimeTypeDector));
Future<MetaInfo> uploaderF = executor.submit(AuthorizedTasks.bind(uploader));
long start = System.currentTimeMillis();
log.debug("TIMING: writing the stream - start");
try {
mos.startWriting();
log.debug("TIMING: writing the stream - finished in {}",System.currentTimeMillis()-start);
ContentHandler handler = detectorF.get();
MetaInfo info = uploaderF.get();
handler.getContent().setData(NodeConstants.CONTENT_NAME);
handler.getContent().setStorageId(info.getStorageId());
handler.getContent().setSize(info.getSize());
handler.getContent().setRemotePath(info.getRemotePath());
return handler;
}catch (Exception e) {
throw new BackendGenericError(e);
}
}
}

@ -1,4 +1,4 @@
package org.gcube.data.access.storagehub.handlers;
package org.gcube.data.access.storagehub.handlers.items;
import java.lang.reflect.Field;
import java.net.URI;
@ -21,21 +21,27 @@ import javax.jcr.Property;
import javax.jcr.PropertyIterator;
import javax.jcr.PropertyType;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.Value;
import javax.jcr.version.Version;
import org.apache.commons.io.IOUtils;
import org.apache.jackrabbit.util.Text;
import org.gcube.common.storagehub.model.Excludes;
import org.gcube.common.storagehub.model.annotations.Attribute;
import org.gcube.common.storagehub.model.annotations.AttributeRootNode;
import org.gcube.common.storagehub.model.annotations.ListNodes;
import org.gcube.common.storagehub.model.annotations.MapAttribute;
import org.gcube.common.storagehub.model.annotations.NodeAttribute;
import org.gcube.common.storagehub.model.exceptions.BackendGenericError;
import org.gcube.common.storagehub.model.items.ExternalFolder;
import org.gcube.common.storagehub.model.items.Item;
import org.gcube.common.storagehub.model.items.RootItem;
import org.gcube.common.storagehub.model.items.SharedFolder;
import org.gcube.common.storagehub.model.items.TrashItem;
import org.gcube.data.access.storagehub.Utils;
import org.gcube.common.storagehub.model.items.nodes.Content;
import org.gcube.common.storagehub.model.messages.Message;
import org.gcube.data.access.storagehub.Constants;
import org.gcube.data.access.storagehub.handlers.ClassHandler;
import org.reflections.Configuration;
import org.reflections.Reflections;
import org.reflections.util.ConfigurationBuilder;
@ -43,34 +49,55 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Singleton
@SuppressWarnings("rawtypes")
public class Node2ItemConverter {
private static final Logger logger = LoggerFactory.getLogger(Node2ItemConverter.class);
private static HashMap<Class, Map<String, Class>> typeToSubtypeMap = new HashMap<>();
public <T extends Item> T getFilteredItem(Node node, List<String> excludes, Class<? extends Item> nodeTypeToInclude) throws RepositoryException, BackendGenericError{
private static HashMap<Class<?>, Map<String, Class>> typeToSubtypeMap = new HashMap<>();
public <T extends Item> T getFilteredItem(Node node, List<String> excludes, Class<? extends RootItem> nodeTypeToInclude) throws RepositoryException, BackendGenericError{
@SuppressWarnings("unchecked")
Class<T> classToHandle = (Class<T>)ClassHandler.instance().get(node.getPrimaryNodeType().getName());
if (classToHandle==null) return null;
if (nodeTypeToInclude!=null && !(nodeTypeToInclude.isAssignableFrom(classToHandle))) return null;
else return retrieveItem(node, excludes, classToHandle);
}
public <T extends Item> T getItem(String nodeIdentifier, Session session, List<String> excludes) throws RepositoryException, BackendGenericError{
Node node = session.getNodeByIdentifier(nodeIdentifier);
return getItem(node, excludes);
}
public <T extends Item> T getItem(Node node, List<String> excludes) throws RepositoryException, BackendGenericError{
@SuppressWarnings("unchecked")
Class<T> classToHandle = (Class<T>)ClassHandler.instance().get(node.getPrimaryNodeType().getName());
/*Node nodeToRetrieve= node;
if (SharedFolder.class.isAssignableFrom(classToHandle)) {
NodeIterator it= node.getSharedSet();
while (it.hasNext()) {
Node sharedNode = it.nextNode();
if (sharedNode.getPath().startsWith(Utils.getWorkspacePath().toPath())) {
nodeToRetrieve = sharedNode;
}
}
}*/
return retrieveItem(node, excludes, classToHandle);
if (classToHandle==null) return null;
T item = retrieveItem(node, excludes, classToHandle);
return item;
}
public Content getContentFromVersion(Version node) throws RepositoryException, BackendGenericError{
Content content = new Content();
setGenericFields(node.getFrozenNode(), Content.class, null, content);
return content;
}
public Message getMessageItem(Node node) throws RepositoryException{
if (!(node.getPrimaryNodeType().getName().equals("nthl:itemSentRequest")
|| node.getPrimaryNodeType().getName().equals("nthl:itemSentRequestSH")))
return null;
Message msg = new Message();
try {
Node attachmentNode = node.getNode(Constants.ATTACHMENTNODE_NAME);
msg.setWithAttachments(attachmentNode.hasNodes());
}catch (Throwable e) {
msg.setWithAttachments(false);
}
setRootItemCommonFields(node, Collections.emptyList(), Message.class, msg);
return msg;
}
@ -81,79 +108,99 @@ public class Node2ItemConverter {
}catch (Exception e) {
throw new BackendGenericError(e);
}
item.setId(node.getIdentifier());
item.setName(Text.unescapeIllegalJcrChars(node.getName()));
item.setPath(Text.unescapeIllegalJcrChars(node.getPath()));
item.setLocked(node.isLocked());
item.setPrimaryType(node.getPrimaryNodeType().getName());
Item parent = null ;
if (item instanceof SharedFolder) {
logger.trace("I'm in a Shared Folder");
item.setShared(true);
}else {
try {
parent = getItem(node.getParent(), Excludes.ALL);
item.setShared(parent.isShared());
} catch(Exception e) {
item.setShared(false);
}
try {
item.setShared(SharedFolder.class.isInstance(item) ||
hasTypedParent(node, SharedFolder.class));
}catch (Exception e) {
item.setShared(false);
}
if (item instanceof TrashItem)
item.setTrashed(true);
else {
try {
if (parent==null)
parent = getItem(node.getParent(), Excludes.ALL);
item.setTrashed(parent.isTrashed());
} catch(Exception e) {
item.setTrashed(false);
}
try {
item.setTrashed(TrashItem.class.isInstance(item) ||
hasTypedParent(node, TrashItem.class));
}catch (Exception e) {
item.setTrashed(false);
}
try {
item.setExternalManaged(hasTypedParent(node, ExternalFolder.class));
}catch (Exception e) {
item.setExternalManaged(false);
}
item.setLocked(node.isLocked());
setRootItemCommonFields(node, excludes, classToHandle, item);
return item;
}
private <T extends Item> boolean hasTypedParent(Node node, Class<T> parentType) throws BackendGenericError, RepositoryException{
if(node==null) return false;
return checkNodeType(node, parentType) || hasTypedParent(node.getParent(), parentType);
}
private <T extends RootItem> void setRootItemCommonFields(Node node, List<String> excludes, Class<T> classToHandle, T instance) throws RepositoryException{
try{
item.setParentId(node.getParent().getIdentifier());
item.setParentPath(node.getParent().getPath());
instance.setParentId(node.getParent().getIdentifier());
instance.setParentPath(node.getParent().getPath());
}catch (Throwable e) {
logger.trace("Root node doesn't have a parent");
}
instance.setRelatedNode(node);
instance.setId(node.getIdentifier());
instance.setName(Text.unescapeIllegalJcrChars(node.getName()));
instance.setPath(Text.unescapeIllegalJcrChars(node.getPath()));
instance.setPrimaryType(node.getPrimaryNodeType().getName());
setGenericFields(node, classToHandle, excludes, instance);
}
private <T> void setGenericFields(Node node, Class<T> classToHandle,List<String> excludes, T instance){
for (Field field : retrieveAllFields(classToHandle)){
if (field.isAnnotationPresent(Attribute.class)){
Attribute attribute = field.getAnnotation(Attribute.class);
field.setAccessible(true);
try{
Class<?> returnType = field.getType();
field.set(item, getPropertyValue(returnType, node.getProperty(attribute.value())));
logger.debug("retrieve item - added field {}",field.getName());
field.set(instance, getPropertyValue(returnType, node.getProperty(attribute.value())));
logger.trace("retrieve item - added field {}",field.getName());
}catch(PathNotFoundException e){
logger.trace("the current node dosn't contain {} property",attribute.value());
} catch (Exception e ) {
logger.warn("error setting value for property {} ",attribute.value());
logger.debug("error setting value for property {} ",attribute.value());
}
} else if (field.isAnnotationPresent(NodeAttribute.class)){
String fieldNodeName = field.getAnnotation(NodeAttribute.class).value();
//for now it excludes only first level node
if (excludes!=null && excludes.contains(fieldNodeName)) continue;
//for now it excludes only first level node
logger.trace("retrieving field node "+field.getName());
field.setAccessible(true);
try{
Node fieldNode = node.getNode(fieldNodeName);
logger.trace("looking in node {} searched with {}",fieldNode.getName(),fieldNodeName);
field.set(item, iterateNodeAttributeFields(field.getType(), fieldNode));
field.set(instance, iterateNodeAttributeFields(field.getType(), fieldNode));
}catch(PathNotFoundException e){
logger.trace("the current node dosn't contain {} node",fieldNodeName);
} catch (Exception e ) {
logger.warn("error setting value",e);
logger.debug("error setting value",e);
}
}
}
return item;
}
private <T> T iterateNodeAttributeFields(Class<T> clazz, Node node) throws Exception{
@ -163,13 +210,12 @@ public class Node2ItemConverter {
Attribute attribute = field.getAnnotation(Attribute.class);
field.setAccessible(true);
try{
@SuppressWarnings("rawtypes")
Class returnType = field.getType();
field.set(obj, getPropertyValue(returnType, node.getProperty(attribute.value())));
}catch(PathNotFoundException e){
logger.trace("the current node dosn't contain {} property",attribute.value());
} catch (Exception e ) {
logger.warn("error setting value {}",e.getMessage());
logger.debug("error setting value {}",e.getMessage());
}
} else if (field.isAnnotationPresent(MapAttribute.class)){
logger.trace("found field {} of type annotated as MapAttribute in class {} and node name {}", field.getName(), clazz.getName(), node.getName());
@ -188,7 +234,7 @@ public class Node2ItemConverter {
}catch(PathNotFoundException e){
logger.warn("the property {} is not mapped",prop.getName());
} catch (Exception e ) {
logger.warn("error setting value {}",e.getMessage());
logger.debug("error setting value {}",e.getMessage());
}
}
}
@ -252,7 +298,7 @@ public class Node2ItemConverter {
return obj;
}
@SuppressWarnings({ "rawtypes", "unchecked" })
@SuppressWarnings({ "unchecked" })
private Object getPropertyValue(Class returnType, Property prop) throws Exception{
if (returnType.equals(String.class)) return prop.getString();
if (returnType.isEnum()) return Enum.valueOf(returnType, prop.getString());
@ -283,7 +329,7 @@ public class Node2ItemConverter {
}while ((currentClass =currentClass.getSuperclass())!=null);
return fields;
}
private Object getPropertyValue(Property prop) throws Exception{
if (prop.isMultiple()){
Object[] values = new Object[prop.getValues().length];
@ -295,7 +341,7 @@ public class Node2ItemConverter {
return getSingleValue(prop.getValue());
}
private Object getSingleValue(Value value) throws Exception{
switch (value.getType()) {
case PropertyType.DATE:
@ -308,7 +354,7 @@ public class Node2ItemConverter {
return value.getString();
}
}
private Object[] getArrayValue(Property prop) throws Exception{
Object[] values = new Object[prop.getValues().length];
int i = 0;
@ -317,17 +363,18 @@ public class Node2ItemConverter {
return values;
}
//Checks if a node is a subtype of classToCompare
public boolean checkNodeType(Node node, Class<? extends Item> classToCompare) throws BackendGenericError{
try {
logger.info("class from nodetype is {} and class to compare is {}",ClassHandler.instance().get(node.getPrimaryNodeType().getName()), classToCompare);
logger.trace("class from nodetype is {} and class to compare is {}",ClassHandler.instance().get(node.getPrimaryNodeType().getName()), classToCompare);
return classToCompare.isAssignableFrom(ClassHandler.instance().get(node.getPrimaryNodeType().getName()));
//(node.isNodeType(ClassHandler.instance().getNodeType(classToCompare)));
}catch (Throwable e) {
throw new BackendGenericError(e);
}
}
}

@ -0,0 +1,75 @@
package org.gcube.data.access.storagehub.handlers.items.builders;
import java.io.InputStream;
import java.util.Objects;
import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
public class ArchiveStructureCreationParameter extends CreateParameters {
String parentFolderName;
FormDataContentDisposition fileDetails;
InputStream stream;
protected ArchiveStructureCreationParameter() {}
public String getParentFolderName() {
return parentFolderName;
}
public FormDataContentDisposition getFileDetails() {
return fileDetails;
}
public InputStream getStream() {
return stream;
}
@Override
protected boolean isValid() {
return Objects.nonNull(parentFolderName) && Objects.nonNull(stream) && Objects.nonNull(fileDetails);
}
@Override
public String toString() {
return "FileCreationParameters [parentFolder=" + parentFolderName + ", fileDetails=" + fileDetails
+ ", parentId=" + parentId + ", user=" + user + "]";
}
public static ArchiveCreationBuilder builder() {
return new ArchiveCreationBuilder();
}
public static class ArchiveCreationBuilder extends ItemsParameterBuilder<ArchiveStructureCreationParameter>{
private ArchiveCreationBuilder() {
super(new ArchiveStructureCreationParameter());
}
public ArchiveCreationBuilder parentName(String name) {
parameters.parentFolderName = name;
return this;
}
public ArchiveCreationBuilder stream(InputStream stream) {
parameters.stream = stream;
return this;
}
public ArchiveCreationBuilder fileDetails(FormDataContentDisposition fileDetails) {
parameters.fileDetails = fileDetails;
return this;
}
}
@Override
public ManagedType getMangedType() {
return ManagedType.ARCHIVE;
}
}

@ -0,0 +1,38 @@
package org.gcube.data.access.storagehub.handlers.items.builders;
import javax.jcr.Session;
public abstract class CreateParameters {
public enum ManagedType {
FILE,
FOLDER,
ARCHIVE,
URL,
GCUBEITEM;
}
String parentId;
Session session;
String user;
protected CreateParameters() {}
public abstract ManagedType getMangedType();
public String getParentId() {
return parentId;
}
public Session getSession() {
return session;
}
public String getUser() {
return user;
}
protected abstract boolean isValid();
public abstract String toString();
}

@ -0,0 +1,82 @@
package org.gcube.data.access.storagehub.handlers.items.builders;
import java.io.InputStream;
import java.util.Objects;
import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
public class FileCreationParameters extends CreateParameters {
String name;
String description;
FormDataContentDisposition fileDetails;
InputStream stream;
protected FileCreationParameters() {}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
public FormDataContentDisposition getFileDetails() {
return fileDetails;
}
public InputStream getStream() {
return stream;
}
@Override
protected boolean isValid() {
return Objects.nonNull(name) && Objects.nonNull(description) && Objects.nonNull(stream);
}
@Override
public String toString() {
return "FileCreationParameters [name=" + name + ", description=" + description + ", fileDetails=" + fileDetails
+ ", parentId=" + parentId + ", user=" + user + "]";
}
public static FileCreationBuilder builder() {
return new FileCreationBuilder();
}
public static class FileCreationBuilder extends ItemsParameterBuilder<FileCreationParameters>{
private FileCreationBuilder() {
super(new FileCreationParameters());
}
public FileCreationBuilder name(String name) {
parameters.name = name;
return this;
}
public FileCreationBuilder description(String description) {
parameters.description = description;
return this;
}
public FileCreationBuilder stream(InputStream stream) {
parameters.stream = stream;
return this;
}
public FileCreationBuilder fileDetails(FormDataContentDisposition fileDetails) {
parameters.fileDetails = fileDetails;
return this;
}
}
@Override
public ManagedType getMangedType() {
return ManagedType.FILE;
}
}

@ -0,0 +1,105 @@
package org.gcube.data.access.storagehub.handlers.items.builders;
import java.util.Objects;
import org.gcube.common.storagehub.model.plugins.PluginParameters;
public class FolderCreationParameters extends CreateParameters {
private String name;
private String description="";
private boolean hidden = false;
private String externalPluginName = null;
private PluginParameters externalPluginParameters = null;
protected FolderCreationParameters() {}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
public boolean isHidden() {
return hidden;
}
public boolean isExternal() {
return externalPluginName!=null;
}
public String getPluginName(String name) {
return this.externalPluginName;
}
public PluginParameters getPluginParameters() {
return externalPluginParameters;
}
@Override
protected boolean isValid() {
return Objects.nonNull(name) && Objects.nonNull(description);
}
@Override
public String toString() {
return "FolderCreationParameters [name=" + name + ", description=" + description + ", hidden=" + hidden
+ ", parentId=" + parentId + ", session=" + session + ", user=" + user + "]";
}
public static FolderCreationBuilder builder() {
return new FolderCreationBuilder();
}
public static class FolderCreationBuilder extends ItemsParameterBuilder<FolderCreationParameters>{
private FolderCreationBuilder() {
super(new FolderCreationParameters());
}
public FolderCreationBuilder name(String name) {
parameters.name = name;
return this;
}
public FolderCreationBuilder description(String description) {
parameters.description = description;
return this;
}
public ExternalFolderCreationBuilder onRepository(String pluginName) {
return new ExternalFolderCreationBuilder(pluginName, this);
}
public FolderCreationBuilder hidden(boolean hidden) {
parameters.hidden = hidden;
return this;
}
}
public static class ExternalFolderCreationBuilder {
FolderCreationBuilder cb;
protected ExternalFolderCreationBuilder(String pluginName, FolderCreationBuilder cb) {
this.cb.parameters.externalPluginName = pluginName;
}
public FolderCreationBuilder withParameters(PluginParameters params){
this.cb.parameters.externalPluginParameters = params;
return this.cb;
}
}
@Override
public ManagedType getMangedType() {
return ManagedType.FOLDER;
}
}

@ -0,0 +1,49 @@
package org.gcube.data.access.storagehub.handlers.items.builders;
import java.util.Objects;
import org.gcube.common.storagehub.model.items.GCubeItem;
public class GCubeItemCreationParameters extends CreateParameters{
GCubeItem item;
protected GCubeItemCreationParameters() {}
public GCubeItem getItem() {
return item;
}
@Override
protected boolean isValid() {
return Objects.nonNull(item);
}
@Override
public String toString() {
return "GCubeItemCreationParameters [item=" + item + ", parentId=" + parentId + ", user=" + user + "]";
}
public static GCubeItemCreationBuilder builder() {
return new GCubeItemCreationBuilder();
}
public static class GCubeItemCreationBuilder extends ItemsParameterBuilder<GCubeItemCreationParameters>{
private GCubeItemCreationBuilder() {
super(new GCubeItemCreationParameters());
}
public GCubeItemCreationBuilder item(GCubeItem item) {
parameters.item = item;
return this;
}
}
@Override
public ManagedType getMangedType() {
return ManagedType.GCUBEITEM;
}
}

@ -0,0 +1,39 @@
package org.gcube.data.access.storagehub.handlers.items.builders;
import java.util.Objects;
import javax.jcr.Session;
import org.gcube.common.storagehub.model.exceptions.InvalidCallParameters;
public abstract class ItemsParameterBuilder<T extends CreateParameters> {
T parameters;
protected ItemsParameterBuilder(T parameters) {
super();
this.parameters = parameters;
}
public ItemsParameterBuilder<T> on(String parentId) {
parameters.parentId = parentId;
return this;
}
public ItemsParameterBuilder<T> author(String author) {
parameters.user = author;
return this;
}
public ItemsParameterBuilder<T> with(Session session){
parameters.session = session;
return this;
}
public T build() throws InvalidCallParameters {
if (!(parameters.isValid() && Objects.nonNull(parameters.parentId)))
throw new InvalidCallParameters("invalid call");
return parameters;
}
}

@ -0,0 +1,69 @@
package org.gcube.data.access.storagehub.handlers.items.builders;
import java.net.URL;
import java.util.Objects;
public class URLCreationParameters extends CreateParameters {
String name;
String description;
URL url;
protected URLCreationParameters() {}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
public URL getUrl() {
return url;
}
@Override
protected boolean isValid() {
return Objects.nonNull(name) && Objects.nonNull(description) && Objects.nonNull(url);
}
@Override
public String toString() {
return "URLCreationParameters [name=" + name + ", description=" + description + ", url=" + url + ", parentId="
+ parentId + ", user=" + user + "]";
}
public static URLCreationBuilder builder() {
return new URLCreationBuilder();
}
public static class URLCreationBuilder extends ItemsParameterBuilder<URLCreationParameters>{
private URLCreationBuilder() {
super(new URLCreationParameters());
}
public URLCreationBuilder name(String name) {
parameters.name = name;
return this;
}
public URLCreationBuilder description(String description) {
parameters.description = description;
return this;
}
public URLCreationBuilder url(URL url) {
parameters.url = url;
return this;
}
}
@Override
public ManagedType getMangedType() {
return ManagedType.URL;
}
}

@ -0,0 +1,80 @@
package org.gcube.data.access.storagehub.handlers.plugins;
import java.util.Collections;
import java.util.Map;
import java.util.stream.Collectors;
import javax.enterprise.inject.Default;
import javax.enterprise.inject.Instance;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.jcr.Node;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import org.gcube.common.storagehub.model.Excludes;
import org.gcube.common.storagehub.model.exceptions.BackendGenericError;
import org.gcube.common.storagehub.model.exceptions.PluginInitializationException;
import org.gcube.common.storagehub.model.exceptions.PluginNotFoundException;
import org.gcube.common.storagehub.model.items.ExternalFolder;
import org.gcube.common.storagehub.model.items.Item;
import org.gcube.common.storagehub.model.plugins.FolderManager;
import org.gcube.common.storagehub.model.plugins.FolderManagerConnector;
import org.gcube.data.access.storagehub.handlers.items.Node2ItemConverter;
import org.gcube.data.access.storagehub.storage.backend.impl.GcubeFolderManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Singleton
public class FolderPluginHandler {
private static Logger log = LoggerFactory.getLogger(FolderPluginHandler.class);
@Inject
Node2ItemConverter node2Item;
private GcubeFolderManager defaultManager = new GcubeFolderManager();
public FolderManager getDefault() {
return defaultManager;
}
@Inject
Instance<FolderManagerConnector> connectors;
private Map<String, FolderManagerConnector> connectorsMap;
FolderPluginHandler(){
if (connectors !=null)
connectorsMap = connectors.stream().collect(Collectors.toMap(FolderManagerConnector::getName, e -> e ));
else {
log.info("connectors are null");
connectorsMap = Collections.emptyMap();
}
}
public FolderManagerConnector getConnector(String name) throws PluginNotFoundException {
if (!connectorsMap.containsKey(name)) throw new PluginNotFoundException("plugin "+name+" not found");
return connectorsMap.get(name);
}
public FolderManager getFolderManager(Item item) throws PluginInitializationException, PluginNotFoundException, RepositoryException, BackendGenericError{
if (!item.isExternalManaged())
return defaultManager;
Session session = ((Node)item.getRelatedNode()).getSession();
Item parent = null;
do {
String parentId = item.getParentId();
Node node = session.getNodeByIdentifier(parentId);
parent = node2Item.getItem(node, Excludes.ALL);
if (parent !=null && parent instanceof ExternalFolder) {
ExternalFolder extParent = (ExternalFolder) parent;
String plugin = extParent.getManagedBy();
Map<String, Object> parameters = extParent.getConnectionParameters().getMap();
return getConnector(plugin).connect(extParent, parameters);
}
} while (parent!=null);
throw new BackendGenericError("selected external managed item doesn't have a parent external folder");
}
}

@ -0,0 +1,28 @@
package org.gcube.data.access.storagehub.handlers.plugins;
import javax.inject.Inject;
import javax.inject.Singleton;
@Singleton
public class OperationMediator {
@Inject
FolderPluginHandler folderHandler;
/*
boolean onMove(Item source, Item destination, Session session) throws PluginInitializationException, PluginNotFoundException, BackendGenericError, RepositoryException{
FolderManager sourceFolderManager = folderHandler.getFolderManager(source);
FolderManager destinationFolderManager = folderHandler.getFolderManager(destination);
if (source instanceof FolderItem) {
destinationFolderManager.onCreatedFolder((FolderItem) source);
session.move(source.getPath(), destination.getPath());
sourceFolderManager.onDeletingFolder((FolderItem) source);
} else if (source instanceof AbstractFileItem){
}
}
*/
}

@ -1,4 +1,4 @@
package org.gcube.data.access.storagehub.handlers;
package org.gcube.data.access.storagehub.handlers.vres;
import java.util.List;
import java.util.concurrent.ExecutorService;
@ -9,6 +9,7 @@ import javax.jcr.SimpleCredentials;
import org.gcube.common.storagehub.model.exceptions.BackendGenericError;
import org.gcube.common.storagehub.model.items.Item;
import org.gcube.data.access.storagehub.handlers.items.Node2ItemConverter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -21,11 +22,11 @@ public class VRE {
private VREQueryRetriever vreQueryRetriever;
private ExecutorService executor;
public VRE(Item item, Repository repository, SimpleCredentials credentials, ExecutorService executor) {
protected VRE(Item item, Repository repository, SimpleCredentials credentials, Node2ItemConverter node2Item, ExecutorService executor) {
super();
this.vreFolder = item;
this.executor = executor;
vreQueryRetriever = new VREQueryRetriever(repository, credentials, vreFolder);
vreQueryRetriever = new VREQueryRetriever(repository, credentials, node2Item, vreFolder);
result = executor.submit(vreQueryRetriever);
}

@ -0,0 +1,113 @@
package org.gcube.data.access.storagehub.handlers.vres;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.jcr.Node;
import javax.jcr.RepositoryException;
import javax.jcr.SimpleCredentials;
import javax.servlet.ServletContext;
import org.apache.jackrabbit.api.JackrabbitSession;
import org.apache.jackrabbit.api.security.user.Group;
import org.apache.jackrabbit.api.security.user.User;
import org.apache.jackrabbit.api.security.user.UserManager;
import org.gcube.common.scope.api.ScopeProvider;
import org.gcube.common.scope.impl.ScopeBean;
import org.gcube.common.scope.impl.ScopeBean.Type;
import org.gcube.common.storagehub.model.exceptions.BackendGenericError;
import org.gcube.common.storagehub.model.exceptions.InvalidCallParameters;
import org.gcube.common.storagehub.model.exceptions.StorageHubException;
import org.gcube.common.storagehub.model.items.Item;
import org.gcube.data.access.storagehub.Constants;
import org.gcube.data.access.storagehub.PathUtil;
import org.gcube.data.access.storagehub.handlers.GroupHandler;
import org.gcube.data.access.storagehub.handlers.items.Node2ItemConverter;
import org.gcube.data.access.storagehub.services.RepositoryInitializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Singleton
public class VREManager {
private static final Logger logger = LoggerFactory.getLogger(VREManager.class);
private Map<String, VRE> vreMap = new HashMap<>();
@Inject
RepositoryInitializer repository;
@Inject
Node2ItemConverter node2Item;
@Inject
PathUtil pathUtil;
@Inject
GroupHandler groupHandler;
ExecutorService executor = Executors.newFixedThreadPool(5);
SimpleCredentials credentials;
@Inject
public VREManager(ServletContext context) {
credentials = new SimpleCredentials(context.getInitParameter(Constants.ADMIN_PARAM_NAME),context.getInitParameter(Constants.ADMIN_PARAM_PWD).toCharArray());
}
public synchronized VRE getVRE(String completeName) {
logger.trace("requesting VRE {}",completeName);
if (vreMap.containsKey(completeName))
return vreMap.get(completeName);
else
return null;
}
private synchronized VRE putVRE(Item vreFolder) {
logger.trace("inserting VRE {}",vreFolder.getTitle());
if (vreMap.containsKey(vreFolder.getTitle())) throw new RuntimeException("something went wrong (vre already present in the map)");
else {
VRE toReturn = new VRE(vreFolder, repository.getRepository(), credentials, node2Item, executor);
vreMap.put(vreFolder.getTitle(), toReturn);
return toReturn;
}
}
public synchronized VRE getVreFolderItem(JackrabbitSession ses, String userId, List<String> excludes ) throws RepositoryException, StorageHubException{
ScopeBean bean = new ScopeBean(ScopeProvider.instance.get());
if (!bean.is(Type.VRE)) throw new BackendGenericError("the current scope is not a VRE");
String entireScopeName= bean.toString().replaceAll("^/(.*)/?$", "$1").replaceAll("/", "-");
return getVreFolderItemByGroupName(ses, entireScopeName, userId, excludes);
}
public synchronized VRE getVreFolderItemByGroupName(JackrabbitSession ses, String groupName, String userId, List<String> excludes ) throws RepositoryException, StorageHubException{
UserManager um = ses.getUserManager();
Group groupAuth = um.getAuthorizable(groupName, Group.class);
User userAuth = um.getAuthorizable(userId, User.class);
if (groupAuth == null || userAuth == null || !groupAuth.isMember(userAuth))
throw new InvalidCallParameters("error getting VRE with user "+userId+" for group "+groupName);
VRE vre = this.getVRE(groupName);
if (vre!=null) return vre;
else {
Node vreFolderNode = groupHandler.getFolderNodeRelatedToGroup(ses, groupName);
Item vreFolder = node2Item.getItem(vreFolderNode, excludes);
return this.putVRE(vreFolder);
}
}
}

@ -0,0 +1,274 @@
package org.gcube.data.access.storagehub.handlers.vres;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.stream.Collectors;
import javax.jcr.Credentials;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.Property;
import javax.jcr.Repository;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.observation.Event;
import javax.jcr.observation.EventJournal;
import org.gcube.common.storagehub.model.Excludes;
import org.gcube.common.storagehub.model.exceptions.BackendGenericError;
import org.gcube.common.storagehub.model.items.AbstractFileItem;
import org.gcube.common.storagehub.model.items.FolderItem;
import org.gcube.common.storagehub.model.items.Item;
import org.gcube.data.access.storagehub.handlers.items.Node2ItemConverter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class VREQueryRetriever implements Callable<List<Item>> {
private static final Logger logger = LoggerFactory.getLogger(VREQueryRetriever.class);
private static final int CACHE_DIMENSION = 50;
private Repository repository;
private Credentials credentials;
private Item vreFolder;
Map<String, Long> cachedMap = new HashMap<>(CACHE_DIMENSION);
long lastTimestamp =0;
long doTime= 0;
boolean underRedo = false;
Node2ItemConverter node2Item;
private static int TIME_MEASURE = Calendar.MINUTE;
private static int VALUE_TIME_BEFORE = 30;
public VREQueryRetriever(Repository repository, Credentials credentials, Node2ItemConverter node2Item, Item vreFolder) {
super();
this.repository = repository;
this.credentials = credentials;
this.vreFolder = vreFolder;
this.node2Item = node2Item;
}
public List<Item> call() {
Session ses = null;
try {
ses = repository.login(credentials);
Calendar now = Calendar.getInstance();
now.add(TIME_MEASURE, -1*Math.abs(VALUE_TIME_BEFORE));
if (doTime> now.getTimeInMillis() || underRedo) {
logger.debug("executing recents task for {} (cahced result)",vreFolder.getTitle());
return correctValues(ses, cachedMap);
}else {
logger.debug("executing recents task for {} (redoing it)",vreFolder.getTitle());
List<Item> toReturn = redo(ses);
doTime = System.currentTimeMillis();
return toReturn;
}
}catch (Exception e) {
logger.error("error preparing recents for folder {}", vreFolder.getTitle(),e);
return Collections.emptyList();
}finally{
if (ses!=null)
ses.logout();
logger.debug("recents task finished");
}
}
public synchronized List<Item> redo(Session ses) {
underRedo = true;
try {
Map<String, Long> tempCachedMap = new HashMap<>(cachedMap);
if (cachedMap.isEmpty())
init(ses, tempCachedMap);
else {
logger.debug("redoing recents for {}",vreFolder.getTitle());
update(ses, tempCachedMap);
}
cachedMap = tempCachedMap;
return correctValues(ses, tempCachedMap);
}finally{
underRedo = false;
}
}
private List<Item> correctValues(Session ses, Map<String, Long> tempCachedMap){
logger.debug("preparing returning values for {}",vreFolder.getTitle());
long start = System.currentTimeMillis();
List<Map.Entry<String, Long>> list = new LinkedList<>(tempCachedMap.entrySet());
list.sort((c1, c2) -> c1.getValue().compareTo(c2.getValue())*-1);
if (list.size()>CACHE_DIMENSION)
for (int index = CACHE_DIMENSION-1; index< list.size() ; index++)
tempCachedMap.remove(list.get(index).getKey());
List<String> cachedIds = list.stream().map(m -> m.getKey()).collect(Collectors.toList());
if (cachedIds.size()>10)
cachedIds = cachedIds.subList(0, 10);
List<Item> result = new ArrayList<>(10);
for (String id: cachedIds) {
try {
Item item = node2Item.getItem(id, ses, Excludes.EXCLUDE_ACCOUNTING);
if (item!=null)
result.add(item);
else logger.warn("item with id {} is null",id);
} catch (BackendGenericError | RepositoryException e) { }
}
logger.debug("returning values prepared in {} for {}",System.currentTimeMillis()-start,vreFolder.getTitle());
return result;
}
private void insertItemInTheRightPlace(Item item, Map<String, Long> tempCachedMap) {
long lastModifiedTime = item.getLastModificationTime().getTimeInMillis();
if (tempCachedMap.size()<CACHE_DIMENSION || lastModifiedTime>Collections.min(tempCachedMap.values()))
tempCachedMap.put(item.getId(), lastModifiedTime);
}
private void init(Session ses,Map<String, Long> tempCachedMap){
try {
long start = System.currentTimeMillis();
lastTimestamp = System.currentTimeMillis();
Node vreFolderNode = ses.getNodeByIdentifier(vreFolder.getId());
logger.debug("starting visiting children for {}",vreFolder.getTitle());
visitChildren(vreFolderNode, tempCachedMap);
logger.debug("initializing recents for {} took {}",vreFolder.getTitle(),System.currentTimeMillis()-start);
} catch (Exception e) {
logger.error("error querying vre {}",vreFolder.getTitle(),e);
throw new RuntimeException(e);
}
}
private void update(Session ses, Map<String, Long> tempCachedMap) {
try {
long timestampToUse = lastTimestamp;
lastTimestamp = System.currentTimeMillis();
long start = System.currentTimeMillis();
final String[] types = { "nthl:workspaceLeafItem", "nthl:workspaceItem"};
EventJournal journalChanged = ses.getWorkspace().getObservationManager().getEventJournal(Event.PROPERTY_CHANGED^Event.NODE_REMOVED^Event.NODE_MOVED^Event.NODE_ADDED, vreFolder.getPath(), true, null, types);
journalChanged.skipTo(timestampToUse);
logger.debug("getting the journal took {}",System.currentTimeMillis()-start);
int events = 0;
//TODO: manage hidden nodes
while (journalChanged.hasNext()) {
events++;
Event event = journalChanged.nextEvent();
try {
switch(event.getType()) {
case Event.NODE_ADDED:
if (ses.nodeExists(event.getPath())) {
Node nodeAdded = ses.getNode(event.getPath());
if (nodeAdded.isNodeType("nthl:workspaceLeafItem")) {
logger.debug("node added event received with name {}", nodeAdded.getName());
Item item = node2Item.getItem(nodeAdded, Excludes.ALL);
if (tempCachedMap.get(event.getIdentifier())!=null)
tempCachedMap.remove(event.getIdentifier());
if (item.isHidden())
break;
insertItemInTheRightPlace(item,tempCachedMap);
}
}
break;
case Event.PROPERTY_CHANGED:
if (ses.propertyExists(event.getPath())) {
Property property = ses.getProperty(event.getPath());
if (property.getName().equalsIgnoreCase("jcr:lastModified")) {
logger.debug("event property changed on {} with value {} and parent {}",property.getName(), property.getValue().getString(), property.getParent().getPath());
String identifier = property.getParent().getIdentifier();
tempCachedMap.remove(identifier);
Item item = node2Item.getItem(property.getParent(), Excludes.ALL);
if (item.isHidden())
break;
insertItemInTheRightPlace(item, tempCachedMap);
}
}
break;
case Event.NODE_REMOVED:
logger.trace("node removed event received with type {}", event.getIdentifier());
if (tempCachedMap.get(event.getIdentifier())!=null &&
tempCachedMap.get(event.getIdentifier())<event.getDate())
tempCachedMap.remove(event.getIdentifier());
break;
case Event.NODE_MOVED:
Node nodeMoved = ses.getNode(event.getPath());
logger.trace("node moved event received with type {}", nodeMoved.getPrimaryNodeType());
if (nodeMoved.isNodeType("nthl:workspaceLeafItem")) {
logger.debug("event node moved on {} with path {}",nodeMoved.getName(), nodeMoved.getPath());
String identifier = nodeMoved.getIdentifier();
String nodePath = ses.getNode(identifier).getPath();
if (tempCachedMap.get(event.getIdentifier())!=null &&
!nodePath.startsWith(vreFolder.getPath()))
tempCachedMap.remove(event.getIdentifier());
}
break;
default:
break;
}
}catch (Exception e) {
logger.warn("error handling event {}",event.getType(),e);
}
}
logger.trace("retrieving event took {} with {} events",System.currentTimeMillis()-start, events);
} catch (Exception e) {
logger.error("error getting events for vre {}",vreFolder.getTitle(),e);
throw new RuntimeException(e);
}
}
private void visitChildren(Node node, Map<String, Long> tempCachedMap) throws Exception{
NodeIterator nodeIt = node.getNodes();
while(nodeIt.hasNext()) {
Node child = nodeIt.nextNode();
Item item = node2Item.getItem(child, Excludes.ALL);
if (item==null || item.isHidden()) continue;
if (item instanceof FolderItem)
visitChildren(child,tempCachedMap);
else if(item instanceof AbstractFileItem)
insertItemInTheRightPlace(item,tempCachedMap);
}
}
/* @Override
public void onEvent(EventIterator events) {
logger.trace("on event called");
while (events.hasNext()) {
Event event = events.nextEvent();
try {
logger.trace("new event received of type {} on node {}",event.getType(),event.getIdentifier());
} catch (RepositoryException e) {
logger.error("error reading event",e);
}
}
}*/
}

@ -1,9 +1,6 @@
package org.gcube.data.access.storagehub.services;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Collections;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
@ -11,9 +8,6 @@ import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.security.AccessControlEntry;
import javax.jcr.security.AccessControlManager;
import javax.jcr.security.Privilege;
import javax.servlet.ServletContext;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
@ -22,19 +16,13 @@ import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.apache.jackrabbit.api.JackrabbitSession;
import org.apache.jackrabbit.api.security.JackrabbitAccessControlList;
import org.apache.jackrabbit.api.security.user.Group;
import org.apache.jackrabbit.api.security.user.User;
import org.apache.jackrabbit.commons.jackrabbit.authorization.AccessControlUtils;
import org.gcube.common.gxrest.response.outbound.GXOutboundErrorResponse;
import org.gcube.common.storagehub.model.Excludes;
import org.gcube.common.storagehub.model.acls.ACL;
import org.gcube.common.storagehub.model.acls.AccessType;
import org.gcube.common.storagehub.model.exceptions.BackendGenericError;
import org.gcube.common.storagehub.model.exceptions.InvalidCallParameters;
@ -47,35 +35,51 @@ import org.gcube.common.storagehub.model.items.SharedFolder;
import org.gcube.common.storagehub.model.items.VreFolder;
import org.gcube.common.storagehub.model.types.ACLList;
import org.gcube.data.access.storagehub.AuthorizationChecker;
import org.gcube.data.access.storagehub.Utils;
import org.gcube.data.access.storagehub.PathUtil;
import org.gcube.data.access.storagehub.StorageHubAppllicationManager;
import org.gcube.data.access.storagehub.handlers.CredentialHandler;
import org.gcube.data.access.storagehub.handlers.Node2ItemConverter;
import org.gcube.data.access.storagehub.handlers.UnshareHandler;
import org.gcube.data.access.storagehub.handlers.items.Node2ItemConverter;
import org.gcube.data.access.storagehub.services.interfaces.ACLManagerInterface;
import org.gcube.smartgears.annotations.ManagedBy;
import org.gcube.smartgears.utils.InnerMethodName;
import org.glassfish.jersey.media.multipart.FormDataParam;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.webcohesion.enunciate.metadata.rs.RequestHeader;
import com.webcohesion.enunciate.metadata.rs.RequestHeaders;
@Path("items")
public class ACLManager {
@ManagedBy(StorageHubAppllicationManager.class)
@RequestHeaders({
@RequestHeader( name = "Authorization", description = "Bearer token, see https://dev.d4science.org/how-to-access-resources"),
})
public class ACLManager extends Impersonable {
private static final Logger log = LoggerFactory.getLogger(ACLManager.class);
@Inject
RepositoryInitializer repository;
RepositoryInitializer repository = StorageHubAppllicationManager.repository;
@RequestScoped
@PathParam("id")
String id;
@Inject
AuthorizationChecker authChecker;
@Inject
PathUtil pathUtil;
@Context
ServletContext context;
@Inject Node2ItemConverter node2Item;
@Inject
AuthorizationChecker authChecker;
@Inject UnshareHandler unshareHandler;
@Inject ACLManagerInterface aclManagerDelegate;
/**
* returns the AccessType for all the users in a shared folder
*
@ -88,38 +92,21 @@ public class ACLManager {
public ACLList getACL() {
InnerMethodName.instance.set("getACLById");
Session ses = null;
List<ACL> acls = new ArrayList<>();
try{
ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
authChecker.checkReadAuthorizationControl(ses, id);
String path = ses.getNodeByIdentifier(id).getPath();
log.info("checking acces for path {}",path);
JackrabbitAccessControlList accessControlList = AccessControlUtils.getAccessControlList(ses, path );
for (AccessControlEntry aclEntry : accessControlList.getAccessControlEntries()) {
ACL acl = new ACL();
acl.setPricipal(aclEntry.getPrincipal().getName());
List<AccessType> types = new ArrayList<>();
for (Privilege priv : aclEntry.getPrivileges())
try {
types.add(AccessType.fromValue(priv.getName()));
}catch (Exception e) {
log.warn(priv.getName()+" cannot be mapped to AccessTypes",e);
}
acl.setAccessTypes(types);
acls.add(acl);
}
Item item = node2Item.getItem(ses.getNodeByIdentifier(id), Excludes.ALL);
authChecker.checkReadAuthorizationControl(ses, currentUser, id);
return new ACLList(aclManagerDelegate.get(item, ses));
}catch(RepositoryException re){
log.error("jcr error getting acl", re);
GXOutboundErrorResponse.throwException(new BackendGenericError("jcr error getting acl", re));
throw new WebApplicationException(new BackendGenericError("jcr error getting acl", re));
}catch(StorageHubException she ){
log.error(she.getErrorMessage(), she);
GXOutboundErrorResponse.throwException(she, Response.Status.fromStatusCode(she.getStatus()));
throw new WebApplicationException(she, Response.Status.fromStatusCode(she.getStatus()));
}finally{
if (ses!=null)
ses.logout();
}
return new ACLList(acls);
}
@ -139,89 +126,53 @@ public class ACLManager {
@PUT
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Path("{id}/acls")
public void setACL(@FormDataParam("user") String user, @FormDataParam("access") AccessType accessType) {
public void updateACL(@FormDataParam("user") String user, @FormDataParam("access") AccessType accessType) {
InnerMethodName.instance.set("setACLById");
Session ses = null;
try{
try {
ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
if (user==currentUser) throw new InvalidCallParameters("own ACLs cannot be modified");
ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
Node node = ses.getNodeByIdentifier(id);
Item item = node2Item.getItem(node, Excludes.ALL);
if (!(item instanceof SharedFolder))
throw new InvalidItemException("the item is not a shared folder");
if (item.getOwner().equals(user))
throw new UserNotAuthorizedException("owner acl cannot be changed");
authChecker.checkAdministratorControl(ses, (SharedFolder) item);
SharedFolder folder = ((SharedFolder)item);
SharedFolder folder = (SharedFolder) item;
authChecker.checkAdministratorControl(ses, currentUser, folder);
if (folder.isVreFolder()) {
if (accessType!=AccessType.ADMINISTRATOR)
throw new InvalidCallParameters("acls in vreFolder cannot be changed, only new admin can be set");
org.apache.jackrabbit.api.security.user.UserManager usrManager = ((JackrabbitSession)ses).getUserManager();
String groupId = folder.getTitle();
if (accessType==AccessType.ADMINISTRATOR) throw new InvalidCallParameters("a VRE admin cannot be changed with this method");
Group group = (Group)usrManager.getAuthorizable(groupId);
User authUser = (User)usrManager.getAuthorizable(user);
if (!group.isMember(authUser))
throw new InvalidCallParameters("user "+user+" is not in the group "+groupId);
String path = node.getPath();
AccessControlManager acm = ses.getAccessControlManager();
JackrabbitAccessControlList acls = AccessControlUtils.getAccessControlList(acm, path);
Privilege[] userPrivileges = new Privilege[] { acm.privilegeFromName(AccessType.ADMINISTRATOR.getValue()) };
Principal principal = AccessControlUtils.getPrincipal(ses, user);
acls.addAccessControlEntry(principal, userPrivileges);
acm.setPolicy(path, acls);
ses.save();
if (!user.equals(folder.getTitle())) throw new InvalidCallParameters("the groupId in the argument is different to the one of the VREFolder");
} else {
NodeIterator sharedSet = node.getSharedSet();
boolean found = false;
while (sharedSet.hasNext() && !found) {
Node current = sharedSet.nextNode();
if (current.getPath().startsWith(Utils.getWorkspacePath(user).toPath()))
if (current.getPath().startsWith(pathUtil.getWorkspacePath(user).toPath()))
found = true;
}
if (!found)
throw new InvalidCallParameters("shared folder with id "+item.getId()+" is not shared with user "+user);
String path = node.getPath();
AccessControlManager acm = ses.getAccessControlManager();
JackrabbitAccessControlList acls = AccessControlUtils.getAccessControlList(acm, path);
Privilege[] userPrivileges = new Privilege[] { acm.privilegeFromName(accessType.getValue()) };
AccessControlEntry aceToDelete = null;;
Principal principal = AccessControlUtils.getPrincipal(ses, user);
for (AccessControlEntry ace : acls.getAccessControlEntries())
if (ace.getPrincipal().equals(principal)) {
aceToDelete = ace;
break;
}
if (aceToDelete!= null)
acls.removeAccessControlEntry(aceToDelete);
acls.addAccessControlEntry(principal, userPrivileges);
acm.setPolicy(path, acls);
ses.save();
throw new InvalidCallParameters("shared folder with id "+folder.getId()+" is not shared with user "+user);
}
aclManagerDelegate.update(user, folder, accessType, ses);
}catch(RepositoryException re){
log.error("jcr error extracting archive", re);
GXOutboundErrorResponse.throwException(new BackendGenericError("jcr error setting acl", re));
throw new WebApplicationException(new BackendGenericError("jcr error setting acl", re));
}catch(StorageHubException she ){
log.error(she.getErrorMessage(), she);
GXOutboundErrorResponse.throwException(she, Response.Status.fromStatusCode(she.getStatus()));
throw new WebApplicationException(she, Response.Status.fromStatusCode(she.getStatus()));
}finally{
if (ses!=null)
ses.logout();
@ -230,18 +181,19 @@ public class ACLManager {
}
/**
* remove right for a user only on VRE folder
* remove right for a user only on Shared folder
*
*
* @param String user
* @param accessType accessType
*
*
* @exception {@link RepositoryException} when a generic jcr error occurs
* @exception {@link UserNotAuthorizedException} when the caller is not ADMINISTRATOR of the shared folder
* @exception {@link InvalidCallParameters} when the folder is not shared with the specified user
* @exception {@link InvalidItemException} when the folder is not share
*/
/*@DELETE
//TODO: is this method correct? can ACL be removed, is correct that this means an unshare operation?
@DELETE
@Consumes(MediaType.TEXT_PLAIN)
@Path("{id}/acls/{user}")
public void removeACL(@PathParam("user") String user) {
@ -249,44 +201,24 @@ public class ACLManager {
Session ses = null;
try{
ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
Node node = ses.getNodeByIdentifier(id);
Item item = node2Item.getItem(node, Excludes.ALL);
if (!(item instanceof SharedFolder))
throw new InvalidItemException("the item is not a shared folder");
if (item instanceof VreFolder || ((SharedFolder) item).isVreFolder())
throw new InvalidCallParameters("acls in vreFolder cannot be removed with this method");
authChecker.checkAdministratorControl(ses, currentUser, (SharedFolder) item);
authChecker.checkAdministratorControl(ses, (SharedFolder) item);
SharedFolder folder = ((SharedFolder)item);
if (folder.isVreFolder()) {
AccessControlManager acm = ses.getAccessControlManager();
JackrabbitAccessControlList acls = AccessControlUtils.getAccessControlList(acm, folder.getPath());
AccessControlEntry entryToDelete= null;
for (AccessControlEntry ace :acls.getAccessControlEntries()) {
if (ace.getPrincipal().getName().equals(user)) {
entryToDelete = ace;
break;
}
}
if (entryToDelete!=null)
acls.removeAccessControlEntry(entryToDelete);
else return;
acm.setPolicy(folder.getPath(), acls);
ses.save();
log.debug("removed Access control entry for user {}",user);
} else throw new InvalidCallParameters("remove acl can be called only on VRE folder");
unshareHandler.unshare(ses, Collections.singleton(user), node, currentUser);
}catch(RepositoryException re){
log.error("jcr error extracting archive", re);
GXOutboundErrorResponse.throwException(new BackendGenericError("jcr error setting acl", re));
GXOutboundErrorResponse.throwException(new BackendGenericError("jcr error removing acl", re));
}catch(StorageHubException she ){
log.error(she.getErrorMessage(), she);
GXOutboundErrorResponse.throwException(she, Response.Status.fromStatusCode(she.getStatus()));
@ -294,7 +226,7 @@ public class ACLManager {
if (ses!=null)
ses.logout();
}
}*/
}
@GET
@Path("{id}/acls/write")
@ -310,7 +242,7 @@ public class ACLManager {
throw new InvalidItemException("this method can be applied only to folder");
try {
authChecker.checkWriteAuthorizationControl(ses, id, true);
authChecker.checkWriteAuthorizationControl(ses, currentUser, id, true);
}catch (UserNotAuthorizedException e) {
return false;
}

@ -0,0 +1,47 @@
package org.gcube.data.access.storagehub.services;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Path("api-docs")
public class DocManager {
private static Logger logger = LoggerFactory.getLogger(DocManager.class);
@GET
@Path("/{any: .*}")
public InputStream toDoc(@Context HttpServletRequest req) throws WebApplicationException {
logger.info(DocManager.class.getSimpleName() + " toDoc called");
String pathInfo = req.getPathInfo();
logger.debug("pathInfo {}", pathInfo);
try {
if (pathInfo.endsWith("/api-docs")) {
pathInfo += "index.html";
}
if (pathInfo.endsWith("/api-docs/")) {
pathInfo += "index.html";
}
logger.info("going to {}", pathInfo);
String realPath = req.getServletContext().getRealPath(pathInfo);
return new FileInputStream(new File(realPath));
} catch (Exception e) {
throw new WebApplicationException("error getting docs",e);
}
}
}

@ -1,13 +1,19 @@
package org.gcube.data.access.storagehub.services;
import static org.gcube.data.access.storagehub.Roles.INFRASTRUCTURE_MANAGER_ROLE;
import static org.gcube.data.access.storagehub.Roles.VREMANAGER_ROLE;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import javax.inject.Inject;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.PathNotFoundException;
import javax.jcr.RepositoryException;
import javax.jcr.security.AccessControlEntry;
import javax.jcr.security.AccessControlManager;
import javax.jcr.security.Privilege;
import javax.servlet.ServletContext;
@ -22,6 +28,7 @@ import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.apache.jackrabbit.api.JackrabbitSession;
import org.apache.jackrabbit.api.security.JackrabbitAccessControlList;
@ -34,34 +41,82 @@ import org.apache.jackrabbit.commons.jackrabbit.authorization.AccessControlUtils
import org.gcube.common.authorization.control.annotations.AuthorizationControl;
import org.gcube.common.authorization.library.provider.AuthorizationProvider;
import org.gcube.common.gxrest.response.outbound.GXOutboundErrorResponse;
import org.gcube.common.scope.api.ScopeProvider;
import org.gcube.common.scope.impl.ScopeBean;
import org.gcube.common.scope.impl.ScopeBean.Type;
import org.gcube.common.storagehub.model.Excludes;
import org.gcube.common.storagehub.model.Paths;
import org.gcube.common.storagehub.model.acls.AccessType;
import org.gcube.common.storagehub.model.exceptions.BackendGenericError;
import org.gcube.common.storagehub.model.exceptions.InvalidItemException;
import org.gcube.common.storagehub.model.exceptions.InvalidCallParameters;
import org.gcube.common.storagehub.model.exceptions.StorageHubException;
import org.gcube.common.storagehub.model.exceptions.UserNotAuthorizedException;
import org.gcube.common.storagehub.model.items.Item;
import org.gcube.common.storagehub.model.types.NodeProperty;
import org.gcube.common.storagehub.model.types.PrimaryNodeType;
import org.gcube.data.access.storagehub.AuthorizationChecker;
import org.gcube.data.access.storagehub.Constants;
import org.gcube.data.access.storagehub.PathUtil;
import org.gcube.data.access.storagehub.StorageHubAppllicationManager;
import org.gcube.data.access.storagehub.Utils;
import org.gcube.data.access.storagehub.exception.MyAuthException;
import org.gcube.data.access.storagehub.handlers.CredentialHandler;
import org.gcube.data.access.storagehub.handlers.GroupHandler;
import org.gcube.data.access.storagehub.handlers.TrashHandler;
import org.gcube.data.access.storagehub.handlers.items.Node2ItemConverter;
import org.gcube.data.access.storagehub.handlers.items.builders.FolderCreationParameters;
import org.gcube.data.access.storagehub.handlers.vres.VRE;
import org.gcube.data.access.storagehub.handlers.vres.VREManager;
import org.gcube.smartgears.annotations.ManagedBy;
import org.gcube.smartgears.utils.InnerMethodName;
import org.glassfish.jersey.media.multipart.FormDataParam;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.webcohesion.enunciate.metadata.rs.RequestHeader;
import com.webcohesion.enunciate.metadata.rs.RequestHeaders;
@Path("groups")
@ManagedBy(StorageHubAppllicationManager.class)
@RequestHeaders({
@RequestHeader( name = "Authorization", description = "Bearer token, see https://dev.d4science.org/how-to-access-resources"),
})
public class GroupManager {
@Context ServletContext context;
@Inject
TrashHandler trashHandler;
private static final Logger log = LoggerFactory.getLogger(GroupManager.class);
RepositoryInitializer repository = StorageHubAppllicationManager.repository;
@Inject
VREManager vreManager;
@Inject
GroupHandler groupHandler;
@Inject
RepositoryInitializer repository;
Node2ItemConverter node2Item;
@Inject
PathUtil pathUtil;
@Inject
AuthorizationChecker authChecker;
@GET
@Path("")
@Produces(MediaType.APPLICATION_JSON)
@AuthorizationControl(allowed={"lucio.lelii"}, exception=MyAuthException.class)
public List<String> getGroups(){
InnerMethodName.instance.set("getGroups");
JackrabbitSession session = null;
List<String> groups= new ArrayList<>();
try {
@ -80,9 +135,9 @@ public class GroupManager {
log.info("group {} found",group.getPrincipal().getName());
groups.add(group.getPrincipal().getName());
}
}catch(Exception e) {
log.error("jcr error getting groups", e);
GXOutboundErrorResponse.throwException(new BackendGenericError(e));
}catch(RepositoryException re ){
log.error("jcr error creating item", re);
GXOutboundErrorResponse.throwException(new BackendGenericError("jcr error creating item", re));
} finally {
if (session!=null)
session.logout();
@ -92,24 +147,38 @@ public class GroupManager {
@POST
@Path("")
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@AuthorizationControl(allowed={"lucio.lelii"}, exception=MyAuthException.class)
public String createGroup(@FormParam("group") String group, @FormParam("accessType") AccessType accessType){
@Consumes(MediaType.MULTIPART_FORM_DATA)
@AuthorizationControl(allowedRoles={INFRASTRUCTURE_MANAGER_ROLE}, exception=MyAuthException.class)
public String createGroup(@FormDataParam("group") String group, @FormDataParam("accessType") AccessType accessType, @FormDataParam("folderOwner") String folderOwner){
InnerMethodName.instance.set("createGroup");
JackrabbitSession session = null;
String groupId = null;
try {
log.info("create group called with groupid {} , accessType {} and folderOwner {}",group, accessType, folderOwner);
session = (JackrabbitSession) repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
org.apache.jackrabbit.api.security.user.UserManager usrManager = session.getUserManager();
Group createdGroup = usrManager.createGroup(group);
groupId = createdGroup.getID();
createVreFolder(groupId, session, accessType!=null?accessType:AccessType.WRITE_OWNER);
User user = (User)usrManager.getAuthorizable(folderOwner);
createVreFolder(groupId, session, accessType!=null?accessType:AccessType.WRITE_OWNER, folderOwner);
boolean success = this.internalAddUserToGroup(session, createdGroup, user);
if (!success) log.warn("the user have not been added to the group");
else log.debug("the user have been added to the group");
session.save();
}catch(Exception e) {
}catch(StorageHubException se) {
log.error("error creating group {}", group, se);
GXOutboundErrorResponse.throwException(se);
}catch(Throwable e) {
log.error("jcr error creating group {}", group, e);
GXOutboundErrorResponse.throwException(new BackendGenericError(e));
} finally {
@ -122,80 +191,207 @@ public class GroupManager {
@DELETE
@Path("{group}")
@AuthorizationControl(allowed={"lucio.lelii"}, exception=MyAuthException.class)
@AuthorizationControl(allowedRoles={INFRASTRUCTURE_MANAGER_ROLE}, exception=MyAuthException.class)
public String deleteGroup(@PathParam("group") String group){
InnerMethodName.instance.set("deleteGroup");
JackrabbitSession session = null;
try {
session = (JackrabbitSession) repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
org.apache.jackrabbit.api.security.user.UserManager usrManager = session.getUserManager();
Authorizable authorizable = usrManager.getAuthorizable(group);
if (authorizable!=null && authorizable.isGroup())
authorizable.remove();
try {
getVreFolderNode(session, group).removeSharedSet();
Node node = groupHandler.getFolderNodeRelatedToGroup(session, group);
List<Item> workspaceItems = Utils.getItemList(node, Excludes.GET_ONLY_CONTENT, null, true, null);
trashHandler.removeOnlyNodesContent(session, workspaceItems);
node.removeSharedSet();
}catch (Exception e) {
log.warn("vreFolder {} not found, removing only the group", group);
}
Authorizable authorizable = usrManager.getAuthorizable(group);
if (authorizable.isGroup())
authorizable.remove();
session.save();
}catch(Exception e) {
log.error("jcr error getting users", e);
GXOutboundErrorResponse.throwException(new BackendGenericError(e));
}catch(RepositoryException re ){
log.error("jcr error creating item", re);
GXOutboundErrorResponse.throwException(new BackendGenericError("jcr error creating item", re));
} finally {
if (session!=null)
session.logout();
}
return group;
}
public boolean isInfraManager() { return AuthorizationProvider.instance.get().getClient().getRoles().contains(INFRASTRUCTURE_MANAGER_ROLE); }
public boolean isVREManager() { return AuthorizationProvider.instance.get().getClient().getRoles().contains(VREMANAGER_ROLE); }
@PUT
@Path("{id}")
@Path("{id}/admins")
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@AuthorizationControl(allowed={"lucio.lelii"}, exception=MyAuthException.class)
public boolean addUserToGroup(@PathParam("id") String groupId, @FormParam("userId") String userId){
public void addAdmin(@PathParam("id") String groupId, @FormParam("userId") String userId){
InnerMethodName.instance.set("addAdmin");
JackrabbitSession session = null;
boolean success = false;
try {
Objects.nonNull(groupId);
Objects.nonNull(userId);
session = (JackrabbitSession) repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
org.apache.jackrabbit.api.security.user.UserManager usrManager = session.getUserManager();
Node vreFolder = groupHandler.getFolderNodeRelatedToGroup(session, groupId);
String currentUser = AuthorizationProvider.instance.get().getClient().getId();
if (!isInfraManager() && !(isVREManager() && isValidGroupForContext(groupId) ))
authChecker.checkAdministratorControl(session, currentUser, node2Item.getItem(vreFolder, Excludes.ALL));
org.apache.jackrabbit.api.security.user.UserManager usrManager = ((JackrabbitSession)session).getUserManager();
Group group = (Group)usrManager.getAuthorizable(groupId);
User user = (User)usrManager.getAuthorizable(userId);
User authUser = (User)usrManager.getAuthorizable(userId);
success = group.addMember(user);
String folderName = group.getPrincipal().getName();
Node folder = getVreFolderNode(session, folderName);
if (group ==null)
throw new InvalidCallParameters("invalid group "+groupId);
if (authUser ==null)
throw new InvalidCallParameters("invalid user "+userId);
if (!group.isMember(authUser))
throw new InvalidCallParameters("user "+userId+" is not in the group "+groupId);
String userPath = String.format("%s%s/%s",Utils.getWorkspacePath(user.getPrincipal().getName()).toPath(),Constants.VRE_FOLDER_PARENT_NAME, folderName);
log.debug("creating folder in user path {}", userPath );
session.getWorkspace().clone(session.getWorkspace().getName(), folder.getPath(),userPath , false);
AccessControlManager acm = session.getAccessControlManager();
JackrabbitAccessControlList acls = AccessControlUtils.getAccessControlList(acm, vreFolder.getPath());
Privilege[] userPrivileges = new Privilege[] { acm.privilegeFromName(AccessType.ADMINISTRATOR.getValue()) };
Principal principal = AccessControlUtils.getPrincipal(session, userId);
acls.addAccessControlEntry(principal, userPrivileges);
acm.setPolicy(vreFolder.getPath(), acls);
session.save();
}catch(Exception e) {
log.error("jcr error adding user {} to group {}", userId, groupId, e);
GXOutboundErrorResponse.throwException(new BackendGenericError(e));
}catch(StorageHubException she ){
log.error(she.getErrorMessage(), she);
GXOutboundErrorResponse.throwException(she, Response.Status.fromStatusCode(she.getStatus()));
}catch(Throwable re ){
log.error("adding admin to VREFolder", re);
GXOutboundErrorResponse.throwException(new BackendGenericError("jcr error adding admin to VREFolder", re));
} finally {
if (session!=null)
session.logout();
}
return success;
}
@DELETE
@Path("{groupId}/users/{userId}")
@AuthorizationControl(allowed={"lucio.lelii"}, exception=MyAuthException.class)
public boolean removeUserFromGroup(@PathParam("groupId") String groupId, @PathParam("userId") String userId){
@Path("{id}/admins/{userId}")
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public void removeAdmin(@PathParam("id") String groupId, @PathParam("userId") String userId){
InnerMethodName.instance.set("removeAdmin");
JackrabbitSession session = null;
try {
Objects.nonNull(groupId);
Objects.nonNull(userId);
session = (JackrabbitSession) repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
Node vreFolder = groupHandler.getFolderNodeRelatedToGroup(session, groupId);
String currentUser = AuthorizationProvider.instance.get().getClient().getId();
if (!isInfraManager() && !(isVREManager() && isValidGroupForContext(groupId) ))
authChecker.checkAdministratorControl(session, currentUser, node2Item.getItem(vreFolder, Excludes.ALL));
AccessControlManager acm = session.getAccessControlManager();
JackrabbitAccessControlList acls = AccessControlUtils.getAccessControlList(acm, vreFolder.getPath());
AccessControlEntry toRemove = null;
for (AccessControlEntry acl: acls.getAccessControlEntries())
if (acl.getPrincipal().getName().equals(userId)) {
toRemove = acl;
break;
}
acls.removeAccessControlEntry(toRemove);
acm.setPolicy(vreFolder.getPath(), acls);
session.save();
}catch(StorageHubException she ){
log.error(she.getErrorMessage(), she);
GXOutboundErrorResponse.throwException(she, Response.Status.fromStatusCode(she.getStatus()));
}catch(Throwable re ){
log.error("jcr error creating item", re);
GXOutboundErrorResponse.throwException(new BackendGenericError("jcr error removing admin to VREFolder", re));
}finally {
if (session!=null)
session.logout();
}
}
@GET
@Path("{groupId}/admins")
@Produces(MediaType.APPLICATION_JSON)
public List<String> getAdmins(@PathParam("groupId") String groupId){
InnerMethodName.instance.set("getAdmins");
String login = AuthorizationProvider.instance.get().getClient().getId();
JackrabbitSession session = null;
List<String> users = new ArrayList<>();
try {
session = (JackrabbitSession) repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
VRE vreFolder = vreManager.getVreFolderItemByGroupName(session, groupId, login, Excludes.ALL);
AccessControlManager acm = session.getAccessControlManager();
//authChecker.checkAdministratorControl(session, (VreFolder)vreFolder.getVreFolder());
Node node = session.getNodeByIdentifier(vreFolder.getVreFolder().getId());
JackrabbitAccessControlList acls = AccessControlUtils.getAccessControlList(acm, node.getPath());
for (AccessControlEntry acl: acls.getAccessControlEntries())
for (Privilege pr: acl.getPrivileges()) {
if (pr.getName().equals(AccessType.ADMINISTRATOR.getValue())){
users.add(acl.getPrincipal().getName());
}
}
}catch(StorageHubException she ){
log.error(she.getErrorMessage(), she);
GXOutboundErrorResponse.throwException(she, Response.Status.fromStatusCode(she.getStatus()));
}catch(Exception re ){
log.error("jcr error creating item", re);
GXOutboundErrorResponse.throwException(new BackendGenericError("jcr error creating item", re));
}finally {
if (session!=null)
session.logout();
}
return users;
}
@PUT
@Path("{id}/users")
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@AuthorizationControl(allowedRoles={VREMANAGER_ROLE, INFRASTRUCTURE_MANAGER_ROLE}, exception=MyAuthException.class)
public boolean addUserToGroup(@PathParam("id") String groupId, @FormParam("userId") String userId){
InnerMethodName.instance.set("addUserToGroup");
JackrabbitSession session = null;
boolean success = false;
try {
if (!isInfraManager() && !isValidGroupForContext(groupId))
throw new UserNotAuthorizedException("only VREManager of the selected VRE can execute this operation");
session = (JackrabbitSession) repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
org.apache.jackrabbit.api.security.user.UserManager usrManager = session.getUserManager();
@ -203,25 +399,74 @@ public class GroupManager {
Group group = (Group)usrManager.getAuthorizable(groupId);
User user = (User)usrManager.getAuthorizable(userId);
//delete folder on user
String folderName = group.getPrincipal().getName();
Node folder = getVreFolderNode(session, folderName);
if (user==null)
throw new InvalidCallParameters("user "+userId+" not exists");
if (group.isMember(user))
throw new InvalidCallParameters("user "+userId+" is already member of group "+groupId);
NodeIterator ni = folder.getSharedSet();
while (ni.hasNext()) {
Node node = ni.nextNode();
if (node.getPath().startsWith(Utils.getWorkspacePath(user.getPrincipal().getName()).toPath())) {
node.removeShare();
break;
}
}
this.internalAddUserToGroup(session, group, user);
success = group.removeMember(user);
session.save();
}catch(StorageHubException she ){
log.error(she.getErrorMessage(), she);
GXOutboundErrorResponse.throwException(she, Response.Status.fromStatusCode(she.getStatus()));
}catch(RepositoryException re ){
log.error("jcr error creating item", re);
GXOutboundErrorResponse.throwException(new BackendGenericError("jcr error creating item", re));
}finally {
if (session!=null)
session.logout();
}
return success;
}
private boolean internalAddUserToGroup(JackrabbitSession session, Group group, User user) throws RepositoryException, StorageHubException {
boolean success = group.addMember(user);
session.save();
String folderName = group.getPrincipal().getName();
Node folder = groupHandler.getFolderNodeRelatedToGroup(session, folderName);
String userPath = Paths.append(pathUtil.getVREsPath(user.getPrincipal().getName(), session), folderName).toPath();
log.debug("creating folder in user path {} from {}", userPath, folder.getPath() );
session.getWorkspace().clone(session.getWorkspace().getName(), folder.getPath(),userPath , false);
try {
session.getNode(userPath);
log.debug("the new folder exists ({}) ", userPath );
}catch (PathNotFoundException e) {
log.debug("the new folder doesn't exists ({}) ", userPath );
}
return success;
}
@DELETE
@Path("{groupId}/users/{userId}")
@AuthorizationControl(allowedRoles={VREMANAGER_ROLE, INFRASTRUCTURE_MANAGER_ROLE}, exception=MyAuthException.class)
public boolean removeUserFromGroup(@PathParam("groupId") String groupId, @PathParam("userId") String userId){
InnerMethodName.instance.set("removeUserFromGroup");
JackrabbitSession session = null;
boolean success = false;
try {
if (!isValidGroupForContext(groupId) && !isInfraManager())
throw new UserNotAuthorizedException("only VREManager of the selected VRE can execute this operation");
session = (JackrabbitSession) repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
success = groupHandler.removeUserFromGroup(groupId, userId, session);
session.save();
}catch(Exception e) {
log.error("jcr error adding user {} to group {}", userId, groupId, e);
GXOutboundErrorResponse.throwException(new BackendGenericError(e));
}catch(StorageHubException she ){
log.error(she.getErrorMessage(), she);
GXOutboundErrorResponse.throwException(she, Response.Status.fromStatusCode(she.getStatus()));
}catch(RepositoryException re ){
log.error("jcr error creating item", re);
GXOutboundErrorResponse.throwException(new BackendGenericError("jcr error creating item", re));
} finally {
if (session!=null)
session.logout();
@ -233,12 +478,18 @@ public class GroupManager {
@GET
@Path("{groupId}/users")
@Produces(MediaType.APPLICATION_JSON)
@AuthorizationControl(allowed={"lucio.lelii"}, exception=MyAuthException.class)
@AuthorizationControl(allowedRoles={VREMANAGER_ROLE, INFRASTRUCTURE_MANAGER_ROLE}, exception=MyAuthException.class)
public List<String> getUsersOfGroup(@PathParam("groupId") String groupId){
InnerMethodName.instance.set("getUsersOfGroup");
JackrabbitSession session = null;
List<String> users = new ArrayList<>();
try {
if (!isValidGroupForContext(groupId) && !isInfraManager())
throw new UserNotAuthorizedException("only VREManager of the selected VRE can execute this operation");
session = (JackrabbitSession) repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
org.apache.jackrabbit.api.security.user.UserManager usrManager = session.getUserManager();
@ -253,10 +504,13 @@ public class GroupManager {
}
}catch(Exception e) {
log.error("jcr error getting users of group {}", groupId, e);
GXOutboundErrorResponse.throwException(new BackendGenericError(e));
} finally {
}catch(StorageHubException she ){
log.error(she.getErrorMessage(), she);
GXOutboundErrorResponse.throwException(she, Response.Status.fromStatusCode(she.getStatus()));
}catch(RepositoryException re ){
log.error("jcr error creating item", re);
GXOutboundErrorResponse.throwException(new BackendGenericError("jcr error creating item", re));
}finally {
if (session!=null)
session.logout();
}
@ -264,53 +518,53 @@ public class GroupManager {
return users;
}
private void createVreFolder(String groupId, JackrabbitSession session, AccessType defaultAccessType) throws Exception{
private void createVreFolder(String groupId, JackrabbitSession session, AccessType defaultAccessType, String owner ) throws Exception{
Node sharedRootNode = session.getNode(Constants.SHARED_FOLDER_PATH);
String name = groupId;
String title = groupId.substring(groupId.lastIndexOf("-")+1);
Node folder= Utils.createFolderInternally(session, sharedRootNode, name, "VREFolder for "+groupId, false, AuthorizationProvider.instance.get().getClient().getId(), null);
String currentScope = ScopeProvider.instance.get();
ScopeBean bean = new ScopeBean(currentScope);
while (!bean.is(Type.INFRASTRUCTURE)) {
bean = bean.enclosingScope();
}
String root = bean.toString().replaceAll("/", "");
String displayName = groupId.replaceAll(root+"-[^\\-]*\\-(.*)", "$1");
log.info("creating vreFolder with name {} and title {} and owner {}", name, displayName, owner);
FolderCreationParameters folderParameters = FolderCreationParameters.builder().name(name).description( "VREFolder for "+groupId).author(owner).on(sharedRootNode.getIdentifier()).with(session).build();
Node folder= Utils.createFolderInternally(folderParameters, null);
folder.setPrimaryType(PrimaryNodeType.NT_WORKSPACE_SHARED_FOLDER);
folder.setProperty(NodeProperty.IS_VRE_FOLDER.toString(), true);
folder.setProperty(NodeProperty.TITLE.toString(), name);
folder.setProperty(NodeProperty.DISPLAY_NAME.toString(), title);
folder.setProperty(NodeProperty.DISPLAY_NAME.toString(), displayName);
session.save();
AccessControlManager acm = session.getAccessControlManager();
JackrabbitAccessControlList acls = AccessControlUtils.getAccessControlList(acm, folder.getPath());
Privilege[] adminPrivileges = new Privilege[] { acm.privilegeFromName(AccessType.ADMINISTRATOR.getValue()) };
/*Privilege[] adminPrivileges = new Privilege[] { acm.privilegeFromName(AccessType.ADMINISTRATOR.getValue()) };
acls.addAccessControlEntry(AccessControlUtils.getPrincipal(session, AuthorizationProvider.instance.get().getClient().getId()), adminPrivileges );
*/
Privilege[] usersPrivileges = new Privilege[] { acm.privilegeFromName(defaultAccessType.getValue()) };
acls.addAccessControlEntry(AccessControlUtils.getPrincipal(session,groupId), usersPrivileges );
acm.setPolicy(folder.getPath(), acls);
}
private Node getVreFolderNode(JackrabbitSession session, String name) throws InvalidItemException, Exception {
Node sharedRootNode = session.getNode(Constants.SHARED_FOLDER_PATH);
Node vreFolder = null;
try {
vreFolder = sharedRootNode.getNode(name);
}catch (PathNotFoundException e) {
log.debug("is an old HL VRE");
}
NodeIterator nodes = sharedRootNode.getNodes();
while (nodes.hasNext()) {
Node node = nodes.nextNode();
if (node.getProperty(NodeProperty.TITLE.toString()).getString().equals(name)) {
vreFolder= node;
break;
}
}
log.debug("vrefolder created with id {}",folder.getIdentifier());
}
if (vreFolder==null) throw new InvalidItemException("vre folder not found");
return vreFolder;
private boolean isValidGroupForContext(String group){
String currentContext = ScopeProvider.instance.get();
String expectedGroupId= currentContext.replace("/", "-").substring(1);
return group.equals(expectedGroupId);
}
}

@ -0,0 +1,38 @@
package org.gcube.data.access.storagehub.services;
import static org.gcube.data.access.storagehub.Roles.INFRASTRUCTURE_MANAGER_ROLE;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.Path;
import javax.ws.rs.core.Context;
import org.gcube.common.authorization.library.provider.AuthorizationProvider;
import org.gcube.common.authorization.library.provider.ClientInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Path("")
public abstract class Impersonable {
Logger log = LoggerFactory.getLogger(Impersonable.class);
String currentUser;
@RequestScoped
@Inject
public void setCurrentUser(@Context final HttpServletRequest request) {
String impersonate = request!=null ? request.getParameter("impersonate") : null ;
ClientInfo info = AuthorizationProvider.instance.get().getClient();
if(impersonate!=null && info.getRoles().contains(INFRASTRUCTURE_MANAGER_ROLE)) {
this.currentUser = impersonate;
} else
this.currentUser = info.getId();
log.info("called with login {} and impersonate {}",info.getId(), impersonate);
}
}

@ -1,5 +1,7 @@
package org.gcube.data.access.storagehub.services;
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.Set;
import javax.enterprise.context.RequestScoped;
@ -12,17 +14,18 @@ import javax.jcr.security.AccessControlManager;
import javax.jcr.security.Privilege;
import javax.servlet.ServletContext;
import javax.ws.rs.Consumes;
import javax.ws.rs.FormParam;
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.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.apache.jackrabbit.api.security.JackrabbitAccessControlList;
import org.apache.jackrabbit.commons.jackrabbit.authorization.AccessControlUtils;
import org.gcube.common.authorization.library.provider.AuthorizationProvider;
import org.gcube.com.fasterxml.jackson.databind.ObjectMapper;
import org.gcube.common.gxrest.response.outbound.GXOutboundErrorResponse;
import org.gcube.common.storagehub.model.Excludes;
import org.gcube.common.storagehub.model.NodeConstants;
@ -39,24 +42,33 @@ import org.gcube.common.storagehub.model.types.NodeProperty;
import org.gcube.common.storagehub.model.types.PrimaryNodeType;
import org.gcube.data.access.storagehub.AuthorizationChecker;
import org.gcube.data.access.storagehub.Constants;
import org.gcube.data.access.storagehub.PathUtil;
import org.gcube.data.access.storagehub.StorageHubAppllicationManager;
import org.gcube.data.access.storagehub.Utils;
import org.gcube.data.access.storagehub.accounting.AccountingHandler;
import org.gcube.data.access.storagehub.handlers.CredentialHandler;
import org.gcube.data.access.storagehub.handlers.Item2NodeConverter;
import org.gcube.data.access.storagehub.handlers.Node2ItemConverter;
import org.gcube.data.access.storagehub.handlers.UnshareHandler;
import org.gcube.data.access.storagehub.handlers.items.Item2NodeConverter;
import org.gcube.data.access.storagehub.handlers.items.Node2ItemConverter;
import org.gcube.smartgears.utils.InnerMethodName;
import org.glassfish.jersey.media.multipart.FormDataParam;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.webcohesion.enunciate.metadata.rs.RequestHeader;
import com.webcohesion.enunciate.metadata.rs.RequestHeaders;
@Path("items")
public class ItemSharing {
@RequestHeaders({
@RequestHeader( name = "Authorization", description = "Bearer token, see https://dev.d4science.org/how-to-access-resources"),
})
public class ItemSharing extends Impersonable{
private static final Logger log = LoggerFactory.getLogger(ItemSharing.class);
@Inject
RepositoryInitializer repository;
RepositoryInitializer repository = StorageHubAppllicationManager.repository;
@Inject
AccountingHandler accountingHandler;
@ -65,11 +77,14 @@ public class ItemSharing {
@PathParam("id")
String id;
@Context
@Context
ServletContext context;
@Inject
AuthorizationChecker authChecker;
@Inject
PathUtil pathUtil;
@Inject
UnshareHandler unshareHandler;
@ -77,17 +92,122 @@ public class ItemSharing {
@Inject Node2ItemConverter node2Item;
@Inject Item2NodeConverter item2Node;
@SuppressWarnings("unchecked")
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@POST
@Path("{id}/share")
public String shareWithMap(@FormParam("mapUserPermission") String mapUserPermissionString, @FormParam("defaultAccessType") String defaultAccessTypeString){
InnerMethodName.instance.set("shareFolder");
HashMap<String,String> mapUserPermission;
Session ses = null;
String toReturn = null;
try{
try {
mapUserPermission = (HashMap<String,String>)new ObjectMapper().readValue(mapUserPermissionString, HashMap.class);
}catch (Exception e) {
throw new InvalidCallParameters("invalid map passed");
}
AccessType defaultAccessType;
try {
defaultAccessType = AccessType.fromValue(defaultAccessTypeString);
}catch (IllegalArgumentException e) {
throw new InvalidCallParameters("invalid default accessType");
}
ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
authChecker.checkWriteAuthorizationControl(ses, currentUser, id, false);
Item item = node2Item.getItem(ses.getNodeByIdentifier(id), Excludes.ALL);
if (mapUserPermission==null || mapUserPermission.isEmpty())
throw new InvalidCallParameters("users is empty");
Node nodeToShare = ses.getNodeByIdentifier(id);
boolean alreadyShared = false;
Node sharedFolderNode;
if (!node2Item.checkNodeType(nodeToShare, SharedFolder.class))
sharedFolderNode= shareFolder(nodeToShare, ses);
else {
sharedFolderNode = nodeToShare;
alreadyShared = true;
}
ses.save();
try {
ses.getWorkspace().getLockManager().lock(sharedFolderNode.getPath(), true, true, 0,currentUser);
}catch (LockException e) {
throw new ItemLockedException(e);
}
try {
AccessControlManager acm = ses.getAccessControlManager();
JackrabbitAccessControlList acls = AccessControlUtils.getAccessControlList(acm, sharedFolderNode.getPath());
if (!alreadyShared) {
Privilege[] adminPrivileges = new Privilege[] { acm.privilegeFromName(AccessType.ADMINISTRATOR.getValue()) };
addUserToSharing(sharedFolderNode, ses, currentUser, item, adminPrivileges, acls);
mapUserPermission.remove(currentUser);
}
for (Entry<String, String> entry: mapUserPermission.entrySet() )
try {
AccessType accessType = defaultAccessType;
try {
if (entry.getValue()!=null)
accessType = AccessType.fromValue(entry.getValue());
}catch (IllegalArgumentException e) { log.warn("an illegal accessType passed to sharing ({})", entry.getValue());}
Privilege[] userPrivileges = new Privilege[] { acm.privilegeFromName(accessType.getValue()) };
addUserToSharing(sharedFolderNode, ses, entry.getKey(), null, userPrivileges, acls);
}catch(Exception e){
log.warn("error adding user {} to sharing of folder {}", entry.getKey(), sharedFolderNode.getName());
}
acm.setPolicy(sharedFolderNode.getPath(), acls);
String title = sharedFolderNode.getProperty(NodeProperty.TITLE.toString()).getString();
accountingHandler.createShareFolder(title, mapUserPermission.keySet(), ses, sharedFolderNode, currentUser, false);
ses.save();
toReturn = sharedFolderNode.getIdentifier();
} finally {
if (!ses.hasPendingChanges())
ses.getWorkspace().getLockManager().unlock(sharedFolderNode.getPath());
}
}catch(RepositoryException re){
log.error("jcr sharing", re);
GXOutboundErrorResponse.throwException(new BackendGenericError("jcr error sharing folder", re));
}catch(StorageHubException she ){
log.error(she.getErrorMessage(), she);
GXOutboundErrorResponse.throwException(she, Response.Status.fromStatusCode(she.getStatus()));
}finally{
if (ses!=null)
ses.logout();
}
return toReturn;
}
@PUT
@Path("{id}/share")
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Deprecated
public String share(@FormDataParam("users") Set<String> users, @FormDataParam("defaultAccessType") AccessType accessType){
InnerMethodName.instance.set("shareFolder");
Session ses = null;
String toReturn = null;
try{
String login = AuthorizationProvider.instance.get().getClient().getId();
ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
authChecker.checkWriteAuthorizationControl(ses, id, false);
authChecker.checkWriteAuthorizationControl(ses, currentUser, id, false);
Item item = node2Item.getItem(ses.getNodeByIdentifier(id), Excludes.ALL);
@ -111,7 +231,7 @@ public class ItemSharing {
ses.save();
try {
ses.getWorkspace().getLockManager().lock(sharedFolderNode.getPath(), true, true, 0,login);
ses.getWorkspace().getLockManager().lock(sharedFolderNode.getPath(), true, true, 0,currentUser);
}catch (LockException e) {
throw new ItemLockedException(e);
}
@ -122,8 +242,8 @@ public class ItemSharing {
if (!alreadyShared) {
Privilege[] adminPrivileges = new Privilege[] { acm.privilegeFromName(AccessType.ADMINISTRATOR.getValue()) };
addUserToSharing(sharedFolderNode, ses, login, item, adminPrivileges, acls);
users.remove(login);
addUserToSharing(sharedFolderNode, ses, currentUser, item, adminPrivileges, acls);
users.remove(currentUser);
}
Privilege[] userPrivileges = new Privilege[] { acm.privilegeFromName(accessType.getValue()) };
@ -137,7 +257,7 @@ public class ItemSharing {
acm.setPolicy(sharedFolderNode.getPath(), acls);
accountingHandler.createShareFolder(sharedFolderNode.getProperty(NodeProperty.TITLE.toString()).getString(), users, ses, sharedFolderNode, false);
accountingHandler.createShareFolder(sharedFolderNode.getProperty(NodeProperty.TITLE.toString()).getString(), users, ses, sharedFolderNode, currentUser, false);
ses.save();
@ -164,9 +284,8 @@ public class ItemSharing {
private Node shareFolder(Node node, Session ses) throws RepositoryException, BackendGenericError, StorageHubException{
String login = AuthorizationProvider.instance.get().getClient().getId();
if (!node2Item.checkNodeType(node, FolderItem.class) || Utils.hasSharedChildren(node) || !node.getProperty(NodeProperty.PORTAL_LOGIN.toString()).getString().equals(login))
if (!node2Item.checkNodeType(node, FolderItem.class) || Utils.hasSharedChildren(node) || !node.getProperty(NodeProperty.PORTAL_LOGIN.toString()).getString().equals(currentUser))
throw new InvalidItemException("item with id "+id+" cannot be shared");
String sharedFolderName = node.getIdentifier();
@ -186,7 +305,7 @@ public class ItemSharing {
String userRootWSId;
String userPath;
if (itemToShare==null) {
String userRootWS = Utils.getWorkspacePath(user).toPath();
String userRootWS = pathUtil.getWorkspacePath(user).toPath();
userRootWSId = ses.getNode(userRootWS).getIdentifier();
userPath = String.format("%s%s",userRootWS,sharedFolderNode.getProperty(NodeProperty.TITLE.toString()).getString());
}
@ -215,13 +334,13 @@ public class ItemSharing {
@Consumes(MediaType.MULTIPART_FORM_DATA)
public String unshare(@FormDataParam("users") Set<String> users){
InnerMethodName.instance.set("unshareFolder");
String login = AuthorizationProvider.instance.get().getClient().getId();
Session ses = null;
String toReturn = null;
try {
log.debug("unsharing folder with id {} with users {}", id, users);
ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
Node sharedNode = ses.getNodeByIdentifier(id);
toReturn = unshareHandler.unshare(ses, users, sharedNode, login);
toReturn = unshareHandler.unshare(ses, users, sharedNode, currentUser);
if(toReturn == null ) throw new InvalidItemException("item with id "+id+" cannot be unshared");
}catch(RepositoryException re){
log.error("jcr unsharing", re);

@ -1,25 +1,12 @@
package org.gcube.data.access.storagehub.services;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import javax.inject.Inject;
import javax.jcr.ItemNotFoundException;
import javax.jcr.Node;
import javax.jcr.PathNotFoundException;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.lock.LockException;
import javax.servlet.ServletContext;
import javax.ws.rs.Consumes;
import javax.ws.rs.FormParam;
@ -30,125 +17,105 @@ import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.ArchiveException;
import org.apache.commons.compress.archivers.ArchiveInputStream;
import org.apache.commons.compress.archivers.ArchiveStreamFactory;
import org.apache.tika.config.TikaConfig;
import org.apache.tika.detect.Detector;
import org.apache.tika.io.TikaInputStream;
import org.apache.tika.metadata.Metadata;
import org.gcube.common.authorization.library.AuthorizedTasks;
import org.gcube.common.authorization.library.provider.AuthorizationProvider;
import org.gcube.common.gxrest.response.outbound.GXOutboundErrorResponse;
import org.gcube.common.storagehub.model.NodeConstants;
import org.gcube.common.storagehub.model.exceptions.BackendGenericError;
import org.gcube.common.storagehub.model.exceptions.IdNotFoundException;
import org.gcube.common.storagehub.model.exceptions.InvalidCallParameters;
import org.gcube.common.storagehub.model.exceptions.InvalidItemException;
import org.gcube.common.storagehub.model.exceptions.ItemLockedException;
import org.gcube.common.storagehub.model.exceptions.StorageHubException;
import org.gcube.common.storagehub.model.exceptions.UserNotAuthorizedException;
import org.gcube.common.storagehub.model.items.AbstractFileItem;
import org.gcube.common.storagehub.model.items.FolderItem;
import org.gcube.common.storagehub.model.items.GCubeItem;
import org.gcube.common.storagehub.model.storages.MetaInfo;
import org.gcube.common.storagehub.model.types.ItemAction;
import org.gcube.data.access.storagehub.AuthorizationChecker;
import org.gcube.data.access.storagehub.MultipleOutputStream;
import org.gcube.data.access.storagehub.Utils;
import org.gcube.data.access.storagehub.accounting.AccountingHandler;
import org.gcube.data.access.storagehub.StorageHubAppllicationManager;
import org.gcube.data.access.storagehub.handlers.CredentialHandler;
import org.gcube.data.access.storagehub.handlers.Item2NodeConverter;
import org.gcube.data.access.storagehub.handlers.Node2ItemConverter;
import org.gcube.data.access.storagehub.handlers.StorageBackendHandler;
import org.gcube.data.access.storagehub.handlers.VersionHandler;
import org.gcube.data.access.storagehub.handlers.content.ContentHandler;
import org.gcube.data.access.storagehub.handlers.content.ContentHandlerFactory;
import org.gcube.data.access.storagehub.handlers.items.ItemHandler;
import org.gcube.data.access.storagehub.handlers.items.builders.ArchiveStructureCreationParameter;
import org.gcube.data.access.storagehub.handlers.items.builders.FileCreationParameters;
import org.gcube.data.access.storagehub.handlers.items.builders.FolderCreationParameters;
import org.gcube.data.access.storagehub.handlers.items.builders.GCubeItemCreationParameters;
import org.gcube.data.access.storagehub.handlers.items.builders.ItemsParameterBuilder;
import org.gcube.data.access.storagehub.handlers.items.builders.URLCreationParameters;
import org.gcube.smartgears.annotations.ManagedBy;
import org.gcube.smartgears.utils.InnerMethodName;
import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
import org.glassfish.jersey.media.multipart.FormDataParam;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.webcohesion.enunciate.metadata.rs.RequestHeader;
import com.webcohesion.enunciate.metadata.rs.RequestHeaders;
@Path("items")
public class ItemsCreator {
@ManagedBy(StorageHubAppllicationManager.class)
@RequestHeaders({
@RequestHeader(name = "Authorization", description = "Bearer token, see https://dev.d4science.org/how-to-access-resources"), })
public class ItemsCreator extends Impersonable {
private static final Logger log = LoggerFactory.getLogger(ItemsCreator.class);
private static ExecutorService executor = Executors.newFixedThreadPool(100);
@Context
ServletContext context;
@Context ServletContext context;
@Inject
RepositoryInitializer repository;
@Inject
ContentHandlerFactory contenthandlerFactory;
@Inject
VersionHandler versionHandler;
RepositoryInitializer repository = StorageHubAppllicationManager.repository;
@Inject
AuthorizationChecker authChecker;
@Inject
AccountingHandler accountingHandler;
@Inject Node2ItemConverter node2Item;
@Inject Item2NodeConverter item2Node;
@Inject StorageBackendHandler storageBackend;
ItemHandler itemHandler;
//@Path("/{id}/create/{type:(?!FILE)[^/?$]*}")
@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Path("/{id}/create/FOLDER")
public Response createFolder(@PathParam("id") String id, @FormParam("name") String name, @FormParam("description") String description, @FormParam("hidden") boolean hidden) {
public Response createFolder(@PathParam("id") String id, @FormParam("name") String name,
@FormParam("description") String description, @FormParam("hidden") boolean hidden) {
InnerMethodName.instance.set("createItem(FOLDER)");
log.info("create folder item called");
Session ses = null;
String toReturn = null;
try{
final String login = AuthorizationProvider.instance.get().getClient().getId();
long start = System.currentTimeMillis();
try {
ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
ItemsParameterBuilder<FolderCreationParameters> builder = FolderCreationParameters.builder().name(name)
.description(description).hidden(hidden).on(id).with(ses).author(currentUser);
toReturn = itemHandler.create(builder.build());
} catch (StorageHubException she) {
log.error(she.getErrorMessage(), she);
GXOutboundErrorResponse.throwException(she, Response.Status.fromStatusCode(she.getStatus()));
} catch (RepositoryException re) {
log.error("jcr error creating item", re);
GXOutboundErrorResponse.throwException(new BackendGenericError("jcr error creating item", re));
} catch (Throwable e) {
log.error("unexpected error", e);
GXOutboundErrorResponse.throwException(new BackendGenericError(e));
} finally {
if (ses != null)
ses.logout();
log.info("time to connect to repo {}",(System.currentTimeMillis()-start));
Node destination;
try {
destination = ses.getNodeByIdentifier(id);
}catch(RepositoryException inf) {
throw new IdNotFoundException(id);
}
if (!node2Item.checkNodeType(destination, FolderItem.class))
throw new InvalidItemException("the destination item is not a folder");
authChecker.checkWriteAuthorizationControl(ses, destination.getIdentifier(), true);
Utils.acquireLockWithWait(ses, destination.getPath(), false, login, 10);
Node newNode;
try {
newNode = Utils.createFolderInternally(ses, destination, name, description, hidden, login, accountingHandler);
ses.save();
} finally {
ses.getWorkspace().getLockManager().unlock(destination.getPath());
}
}
return Response.ok(toReturn).build();
}
log.info("item with id {} correctly created",newNode.getIdentifier());
toReturn = newNode.getIdentifier();
}catch(StorageHubException she ){
@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Path("/{id}/create/EXTERNALFOLDER")
public Response createExternalFolder(@PathParam("id") String id, @FormParam("name") String name,
@FormParam("description") String description, @FormParam("hidden") boolean hidden,
@FormParam("pluginName") String pluginName, @FormParam("parameters") String pluginParameters) {
InnerMethodName.instance.set("createItem(EXTERNALFOLDER)");
log.info("create folder item called");
Session ses = null;
String toReturn = null;
try {
// TODO
ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
ItemsParameterBuilder<FolderCreationParameters> builder = FolderCreationParameters.builder().name(name)
.description(description).onRepository(pluginName).withParameters(null).hidden(hidden).on(id)
.with(ses).author(currentUser);
toReturn = itemHandler.create(builder.build());
} catch (StorageHubException she) {
log.error(she.getErrorMessage(), she);
GXOutboundErrorResponse.throwException(she, Response.Status.fromStatusCode(she.getStatus()));
}catch(RepositoryException re ){
} catch (RepositoryException re) {
log.error("jcr error creating item", re);
GXOutboundErrorResponse.throwException(new BackendGenericError("jcr error creating item", re));
}finally{
if (ses!=null)
} catch (Throwable e) {
log.error("unexpected error", e);
GXOutboundErrorResponse.throwException(new BackendGenericError(e));
} finally {
if (ses != null)
ses.logout();
}
@ -158,57 +125,36 @@ public class ItemsCreator {
@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Path("/{id}/create/URL")
public Response createURL(@PathParam("id") String id, @FormParam("name") String name, @FormParam("description") String description, @FormParam("value") URL value) {
public Response createURL(@PathParam("id") String id, @FormParam("name") String name,
@FormParam("description") String description, @FormParam("value") URL value) {
InnerMethodName.instance.set("createItem(URL)");
log.info("create url called");
Session ses = null;
String toReturn = null;
try{
final String login = AuthorizationProvider.instance.get().getClient().getId();
long start = System.currentTimeMillis();
try {
ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
log.info("time to connect to repo {}",(System.currentTimeMillis()-start));
Node destination;
try {
destination = ses.getNodeByIdentifier(id);
}catch(RepositoryException inf) {
throw new IdNotFoundException(id);
}
if (!node2Item.checkNodeType(destination, FolderItem.class))
throw new InvalidItemException("the destination item is not a folder");
authChecker.checkWriteAuthorizationControl(ses, destination.getIdentifier(), true);
Utils.acquireLockWithWait(ses, destination.getPath(), false, login, 10);
Node newNode;
try {
newNode = Utils.createURLInternally(ses, destination, name, value, description, login, accountingHandler);
ses.save();
} finally {
ses.getWorkspace().getLockManager().unlock(destination.getPath());
}
ItemsParameterBuilder<URLCreationParameters> builder = URLCreationParameters.builder().name(name)
.description(description).url(value).on(id).with(ses).author(currentUser);
log.info("item with id {} correctly created",newNode.getIdentifier());
toReturn = newNode.getIdentifier();
}catch(StorageHubException she ){
toReturn = itemHandler.create(builder.build());
} catch (StorageHubException she) {
log.error(she.getErrorMessage(), she);
GXOutboundErrorResponse.throwException(she, Response.Status.fromStatusCode(she.getStatus()));
}catch(RepositoryException re ){
} catch (RepositoryException re) {
log.error("jcr error creating item", re);
GXOutboundErrorResponse.throwException(new BackendGenericError("jcr error creating item", re));
}finally{
if (ses!=null)
} catch (Throwable e) {
log.error("unexpected error", e);
GXOutboundErrorResponse.throwException(new BackendGenericError(e));
} finally {
if (ses != null)
ses.logout();
}
return Response.ok(toReturn).build();
}
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Path("/{id}/create/GCUBEITEM")
@ -218,93 +164,58 @@ public class ItemsCreator {
Session ses = null;
String toReturn = null;
try{
final String login = AuthorizationProvider.instance.get().getClient().getId();
try {
ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
ItemsParameterBuilder<GCubeItemCreationParameters> builder = GCubeItemCreationParameters.builder()
.item(item).on(id).with(ses).author(currentUser);
Node destination;
try {
destination = ses.getNodeByIdentifier(id);
}catch(ItemNotFoundException inf) {
throw new IdNotFoundException(id);
}
if (!node2Item.checkNodeType(destination, FolderItem.class))
throw new InvalidItemException("the destination item is not a folder");
authChecker.checkWriteAuthorizationControl(ses, destination.getIdentifier(), true);
Utils.acquireLockWithWait(ses, destination.getPath(), false, login, 10);
Node newNode;
try {
newNode = Utils.createGcubeItemInternally(ses, destination, item.getName(), item.getDescription(), login, item, accountingHandler);
ses.save();
} finally {
ses.getWorkspace().getLockManager().unlock(destination.getPath());
}
log.info("item with id {} correctly created",newNode.getIdentifier());
toReturn = newNode.getIdentifier();
}catch(StorageHubException she ){
toReturn = itemHandler.create(builder.build());
} catch (StorageHubException she) {
log.error(she.getErrorMessage(), she);
GXOutboundErrorResponse.throwException(she, Response.Status.fromStatusCode(she.getStatus()));
}catch(RepositoryException re ){
} catch (RepositoryException re) {
log.error("jcr error creating item", re);
GXOutboundErrorResponse.throwException(new BackendGenericError("jcr error creating item", re));
}finally{
if (ses!=null)
} catch (Throwable e) {
log.error("unexpected error", e);
GXOutboundErrorResponse.throwException(new BackendGenericError(e));
} finally {
if (ses != null)
ses.logout();
}
return toReturn;
}
@POST
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Path("/{id}/create/FILE")
public String createFileItem(@PathParam("id") String id, @FormDataParam("name") String name,
@FormDataParam("description") String description,
@FormDataParam("file") InputStream stream,
@FormDataParam("file") FormDataContentDisposition fileDetail){
@FormDataParam("description") String description, @FormDataParam("file") InputStream stream,
@FormDataParam("file") FormDataContentDisposition fileDetail) {
InnerMethodName.instance.set("createItem(FILE)");
Session ses = null;
String toReturn = null;
try{
if (name==null || name.trim().isEmpty() || description ==null) throw new InvalidCallParameters("name or description are null or empty");
final String login = AuthorizationProvider.instance.get().getClient().getId();
try {
ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
ItemsParameterBuilder<FileCreationParameters> builder = FileCreationParameters.builder().name(name)
.description(description).stream(stream).fileDetails(fileDetail).on(id).with(ses)
.author(currentUser);
Node destination = ses.getNodeByIdentifier(id);
log.info("create file called with filename {} in dir {} ", name, destination.getPath() );
if (!node2Item.checkNodeType(destination, FolderItem.class))
throw new InvalidItemException("the destination item is not a folder");
toReturn = itemHandler.create(builder.build());
log.info("session: {}",ses.toString());
Node newNode = createFileItemInternally(ses, destination, stream, name, description, login, true);
ses.save();
versionHandler.checkinContentNode(newNode, ses);
log.info("file with id {} correctly created",newNode.getIdentifier());
toReturn = newNode.getIdentifier();
}catch(RepositoryException re ){
} catch (RepositoryException re) {
log.error("jcr error creating file item", re);
GXOutboundErrorResponse.throwException(new BackendGenericError("jcr error creating file item", re));
}catch(StorageHubException she ){
} catch (StorageHubException she) {
log.error(she.getErrorMessage(), she);
GXOutboundErrorResponse.throwException(she, Response.Status.fromStatusCode(she.getStatus()));
}catch(Throwable e ){
} catch (Throwable e) {
log.error("unexpected error", e);
GXOutboundErrorResponse.throwException(new BackendGenericError(e));
}finally{
if (ses!=null && ses.isLive()) {
} finally {
if (ses != null && ses.isLive()) {
log.info("session closed");
ses.logout();
}
@ -313,249 +224,39 @@ public class ItemsCreator {
}
private Node createFileItemInternally(Session ses, Node destinationNode, InputStream stream, String name, String description, String login, boolean withLock) throws RepositoryException, UserNotAuthorizedException, ItemLockedException, BackendGenericError{
//to inherit hidden property
//item.setHidden(destinationItem.isHidden());
Node newNode;
try {
newNode = ses.getNode(org.gcube.common.storagehub.model.Paths.append(org.gcube.common.storagehub.model.Paths.getPath(destinationNode.getPath()), name).toPath());
authChecker.checkWriteAuthorizationControl(ses, newNode.getIdentifier(), false);
AbstractFileItem item = fillItemWithContent(stream, name, description, destinationNode.getPath(), login);
if (withLock) {
try {
ses.getWorkspace().getLockManager().lock(newNode.getPath(), true, true, 0,login);
}catch (LockException le) {
throw new ItemLockedException(le);
}
}
try {
versionHandler.checkoutContentNode(newNode, ses);
log.trace("replacing content of class {}",item.getContent().getClass());
item2Node.replaceContent(newNode,item, ItemAction.UPDATED);
accountingHandler.createFileUpdated(item.getTitle(), ses, newNode, false);
ses.save();
}finally {
if (withLock) ses.getWorkspace().getLockManager().unlock(newNode.getPath());
}
}catch(PathNotFoundException pnf) {
authChecker.checkWriteAuthorizationControl(ses, destinationNode.getIdentifier(), true);
AbstractFileItem item = fillItemWithContent(stream, name, description, destinationNode.getPath(), login);
if (withLock) {
try {
log.debug("trying to acquire lock");
Utils.acquireLockWithWait(ses, destinationNode.getPath(), false, login, 10);
}catch (LockException le) {
throw new ItemLockedException(le);
}
}
try {
newNode = item2Node.getNode(destinationNode, item);
ses.save();
}finally {
if (withLock) ses.getWorkspace().getLockManager().unlock(destinationNode.getPath());
}
versionHandler.makeVersionableContent(newNode, ses);
accountingHandler.createFolderAddObj(name, item.getClass().getSimpleName(), item.getContent().getMimeType(), ses, newNode, false);
}
return newNode;
}
private AbstractFileItem fillItemWithContent(InputStream stream, String name, String description, String path, String login) throws BackendGenericError{
ContentHandler handler = getContentHandler(stream , name, path, login);
AbstractFileItem item =handler.buildItem(name, description, login);
return item ;
}
@POST
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Path("/{id}/create/ARCHIVE")
public String uploadArchive(@PathParam("id") String id, @FormDataParam("parentFolderName") String parentFolderName,
@FormDataParam("file") InputStream stream,
@FormDataParam("file") FormDataContentDisposition fileDetail){
@FormDataParam("file") InputStream stream, @FormDataParam("file") FormDataContentDisposition fileDetail) {
InnerMethodName.instance.set("createItem(ARCHIVE)");
Session ses = null;
String toReturn = null;
try{
if (parentFolderName==null) throw new InvalidCallParameters("new folder name is null");
final String login = AuthorizationProvider.instance.get().getClient().getId();
try {
ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
Node destination = ses.getNodeByIdentifier(id);
ItemsParameterBuilder<ArchiveStructureCreationParameter> builder = ArchiveStructureCreationParameter
.builder().parentName(parentFolderName).stream(stream).fileDetails(fileDetail).on(id).with(ses)
.author(currentUser);
if (!node2Item.checkNodeType(destination, FolderItem.class))
throw new InvalidItemException("the destination item is not a folder");
authChecker.checkWriteAuthorizationControl(ses, destination.getIdentifier() , true);
try {
ses.getWorkspace().getLockManager().lock(destination.getPath(), false, true, 0,login);
}catch (LockException le) {
throw new ItemLockedException(le);
}
toReturn = itemHandler.create(builder.build());
Node parentDirectoryNode = Utils.createFolderInternally(ses, destination, parentFolderName, "", false, login, accountingHandler);
try {
if (ses.getWorkspace().getLockManager().isLocked(destination.getPath()))
ses.getWorkspace().getLockManager().unlock(destination.getPath());
} catch (Throwable t){
log.warn("error unlocking {}", destination.getPath(), t);
}
Set<Node> fileNodes = new HashSet<>();
HashMap<String, Node> directoryNodeMap = new HashMap<>();
try (ArchiveInputStream input = new ArchiveStreamFactory()
.createArchiveInputStream(new BufferedInputStream(stream, 1024*64))){
ArchiveEntry entry;
while ((entry = input.getNextEntry()) != null) {
if (entry.isDirectory()) {
String entirePath = entry.getName();
String name = entirePath.replaceAll("(.*/)*(.*)/", "$2");
String parentPath = entirePath.replaceAll("(.*/)*(.*)/", "$1");
log.debug("creating directory with entire path {}, name {}, parentPath {} ", entirePath, name, parentPath);
Node createdNode;
if (parentPath.isEmpty()) {
createdNode = Utils.createFolderInternally(ses, parentDirectoryNode, name, "", false, login, accountingHandler);
}else {
Node parentNode = directoryNodeMap.get(parentPath);
createdNode = Utils.createFolderInternally(ses, parentNode, name, "", false, login, accountingHandler);
}
directoryNodeMap.put(entirePath, createdNode);
continue;
} else {
try {
String entirePath = entry.getName();
String name = entirePath.replaceAll("(.*/)*(.*)", "$2");
String parentPath = entirePath.replaceAll("(.*/)*(.*)", "$1");
log.debug("creating file with entire path {}, name {}, parentPath {} ", entirePath, name, parentPath);
Node fileNode = null;
if (parentPath.isEmpty())
fileNode = createFileItemInternally(ses, parentDirectoryNode, input, name, "", login, false);
else {
Node parentNode = directoryNodeMap.get(parentPath);
fileNode = createFileItemInternally(ses, parentNode, input, name, "", login, false);
}
fileNodes.add(fileNode);
}catch(Exception e) {
log.warn("error getting file {}",entry.getName(),e);
}
}
}
}
ses.save();
for (Node node : fileNodes)
versionHandler.checkinContentNode(node, ses);
toReturn = parentDirectoryNode.getIdentifier();
}catch(RepositoryException | ArchiveException | IOException re){
} catch (RepositoryException | ArchiveException | IOException re) {
log.error("jcr error extracting archive", re);
GXOutboundErrorResponse.throwException(new BackendGenericError("jcr error extracting archive", re));
}catch(StorageHubException she ){
} catch (StorageHubException she) {
log.error(she.getErrorMessage(), she);
GXOutboundErrorResponse.throwException(she, Response.Status.fromStatusCode(she.getStatus()));
} finally{
if (ses!=null)
} catch (Throwable e) {
log.error("unexpected error", e);
GXOutboundErrorResponse.throwException(new BackendGenericError(e));
} finally {
if (ses != null)
ses.logout();
}
return toReturn;
}
private ContentHandler getContentHandler(InputStream stream , String name, String path, String login) throws BackendGenericError {
final MultipleOutputStream mos;
try{
mos = new MultipleOutputStream(stream, 2);
}catch (IOException e) {
throw new BackendGenericError(e);
}
Callable<ContentHandler> mimeTypeDector = new Callable<ContentHandler>() {
@Override
public ContentHandler call() throws Exception {
ContentHandler handler =null;
long start = System.currentTimeMillis();
log.debug("TIMING: reading the mimetype - start");
try(InputStream is1 = new BufferedInputStream(mos.get(), 1024*64)){
org.apache.tika.mime.MediaType mediaType = null;
TikaConfig config = TikaConfig.getDefaultConfig();
Detector detector = config.getDetector();
TikaInputStream stream = TikaInputStream.get(is1);
Metadata metadata = new Metadata();
metadata.add(Metadata.RESOURCE_NAME_KEY, name);
mediaType = detector.detect(stream, metadata);
String mimeType = mediaType.getBaseType().toString();
handler = contenthandlerFactory.create(mimeType);
is1.reset();
handler.initiliseSpecificContent(is1, name, mimeType);
log.trace("TIMING: reading the mimetype - finished in {}",System.currentTimeMillis()-start);
} catch (Throwable e) {
log.error("error retrieving mimeType",e);
throw new RuntimeException(e);
}
return handler;
}
};
Callable<MetaInfo> uploader = new Callable<MetaInfo>() {
@Override
public MetaInfo call() throws Exception {
try(InputStream is1 = mos.get()){
String uid = UUID.randomUUID().toString();
String remotePath= String.format("%s/%s-%s",path,uid,name);
MetaInfo info = storageBackend.upload(is1, remotePath);
return info;
}catch (Throwable e) {
log.error("error writing content",e );
throw e;
}
}
};
Future<ContentHandler> detectorF = executor.submit(AuthorizedTasks.bind(mimeTypeDector));
Future<MetaInfo> uploaderF = executor.submit(AuthorizedTasks.bind(uploader));
long start = System.currentTimeMillis();
log.debug("TIMING: writing the stream - start");
try {
mos.startWriting();
log.debug("TIMING: writing the stream - finished in {}",System.currentTimeMillis()-start);
ContentHandler handler = detectorF.get();
MetaInfo info = uploaderF.get();
handler.getContent().setData(NodeConstants.CONTENT_NAME);
handler.getContent().setStorageId(info.getStorageId());
handler.getContent().setSize(info.getSize());
handler.getContent().setRemotePath(info.getRemotePath());
return handler;
}catch (Exception e) {
throw new BackendGenericError(e);
}
}
}

@ -44,8 +44,7 @@ import javax.ws.rs.core.StreamingOutput;
import org.apache.commons.io.FilenameUtils;
import org.gcube.common.authorization.control.annotations.AuthorizationControl;
import org.gcube.common.authorization.library.provider.AuthorizationProvider;
import org.gcube.common.encryption.StringEncrypter;
import org.gcube.common.encryption.encrypter.StringEncrypter;
import org.gcube.common.gxrest.response.outbound.GXOutboundErrorResponse;
import org.gcube.common.scope.api.ScopeProvider;
import org.gcube.common.scope.impl.ScopeBean;
@ -58,12 +57,16 @@ import org.gcube.common.storagehub.model.exceptions.IdNotFoundException;
import org.gcube.common.storagehub.model.exceptions.InvalidCallParameters;
import org.gcube.common.storagehub.model.exceptions.InvalidItemException;
import org.gcube.common.storagehub.model.exceptions.ItemLockedException;
import org.gcube.common.storagehub.model.exceptions.PluginInitializationException;
import org.gcube.common.storagehub.model.exceptions.PluginNotFoundException;
import org.gcube.common.storagehub.model.exceptions.StorageHubException;
import org.gcube.common.storagehub.model.items.AbstractFileItem;
import org.gcube.common.storagehub.model.items.FolderItem;
import org.gcube.common.storagehub.model.items.Item;
import org.gcube.common.storagehub.model.items.SharedFolder;
import org.gcube.common.storagehub.model.items.VreFolder;
import org.gcube.common.storagehub.model.items.nodes.Content;
import org.gcube.common.storagehub.model.plugins.FolderManager;
import org.gcube.common.storagehub.model.service.ItemList;
import org.gcube.common.storagehub.model.service.ItemWrapper;
import org.gcube.common.storagehub.model.service.VersionList;
@ -71,30 +74,40 @@ import org.gcube.common.storagehub.model.types.ItemAction;
import org.gcube.common.storagehub.model.types.NodeProperty;
import org.gcube.data.access.storagehub.AuthorizationChecker;
import org.gcube.data.access.storagehub.Constants;
import org.gcube.data.access.storagehub.PathUtil;
import org.gcube.data.access.storagehub.Range;
import org.gcube.data.access.storagehub.SingleFileStreamingOutput;
import org.gcube.data.access.storagehub.StorageHubAppllicationManager;
import org.gcube.data.access.storagehub.Utils;
import org.gcube.data.access.storagehub.accounting.AccountingHandler;
import org.gcube.data.access.storagehub.exception.MyAuthException;
import org.gcube.data.access.storagehub.handlers.ClassHandler;
import org.gcube.data.access.storagehub.handlers.CompressHandler;
import org.gcube.data.access.storagehub.handlers.CredentialHandler;
import org.gcube.data.access.storagehub.handlers.Item2NodeConverter;
import org.gcube.data.access.storagehub.handlers.Node2ItemConverter;
import org.gcube.data.access.storagehub.handlers.StorageBackendHandler;
import org.gcube.data.access.storagehub.handlers.TrashHandler;
import org.gcube.data.access.storagehub.handlers.VersionHandler;
import org.gcube.data.access.storagehub.handlers.items.Item2NodeConverter;
import org.gcube.data.access.storagehub.handlers.items.Node2ItemConverter;
import org.gcube.data.access.storagehub.handlers.plugins.FolderPluginHandler;
import org.gcube.smartgears.annotations.ManagedBy;
import org.gcube.smartgears.utils.InnerMethodName;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.webcohesion.enunciate.metadata.rs.RequestHeader;
import com.webcohesion.enunciate.metadata.rs.RequestHeaders;
@Path("items")
public class ItemsManager {
@ManagedBy(StorageHubAppllicationManager.class)
@RequestHeaders({
@RequestHeader( name = "Authorization", description = "Bearer token, see https://dev.d4science.org/how-to-access-resources"),
})
public class ItemsManager extends Impersonable{
private static final Logger log = LoggerFactory.getLogger(ItemsManager.class);
@Inject
RepositoryInitializer repository;
RepositoryInitializer repository = StorageHubAppllicationManager.repository;
@Inject
AccountingHandler accountingHandler;
@ -108,17 +121,25 @@ public class ItemsManager {
@Inject
AuthorizationChecker authChecker;
@Inject
VersionHandler versionHandler;
@Inject
TrashHandler trashHandler;
@Inject PathUtil pathUtil;
@Inject Node2ItemConverter node2Item;
@Inject Item2NodeConverter item2Node;
@Inject StorageBackendHandler storageBackend;
@Inject
FolderPluginHandler folderPluginHandler;
@Inject
CompressHandler compressHandler;
@GET
@Path("{id}")
@ -130,14 +151,14 @@ public class ItemsManager {
try{
ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
Node node = ses.getNodeByIdentifier(id);
authChecker.checkReadAuthorizationControl(ses, id);
authChecker.checkReadAuthorizationControl(ses, currentUser, id);
toReturn = node2Item.getItem(node, excludes);
}catch (ItemNotFoundException e) {
log.error("id {} not found",id,e);
GXOutboundErrorResponse.throwException(new IdNotFoundException(id, e), Status.NOT_FOUND);
}catch(RepositoryException re){
log.error("jcr error getting item", re);
GXOutboundErrorResponse.throwException(new BackendGenericError("jcr error searching item", re));
GXOutboundErrorResponse.throwException(new BackendGenericError("jcr error getting item", re));
}catch(StorageHubException she ){
log.error(she.getErrorMessage(), she);
GXOutboundErrorResponse.throwException(she, Response.Status.fromStatusCode(she.getStatus()));
@ -149,16 +170,90 @@ public class ItemsManager {
return new ItemWrapper<Item>(toReturn);
}
@GET
@Path("{id}/path")
@Produces(MediaType.APPLICATION_JSON)
public ItemWrapper<Item> getByRelativePath(@QueryParam("path") String path, @QueryParam("exclude") List<String> excludes){
InnerMethodName.instance.set("getByPath");
Session ses = null;
Item toReturn = null;
try{
ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
authChecker.checkReadAuthorizationControl(ses, currentUser, id);
String relativePath = path.startsWith("/")? path.substring(1) : path;
if (path.endsWith("/"))
relativePath.substring(0,relativePath.lastIndexOf("/"));
if (relativePath.isEmpty()) throw new InvalidCallParameters("empty path");
Item item =null;
String nextId = id;
String[] paths = relativePath.split("/");
for (String actualPath: paths) {
item = getChildrenMatchingName(ses, nextId, actualPath, Excludes.ALL);
if (item ==null) throw new InvalidCallParameters("relative path "+actualPath+" not found under item with id "+nextId);
authChecker.checkReadAuthorizationControl(ses, currentUser, item.getId());
nextId = item.getId();
}
if (excludes.containsAll(Excludes.ALL))
return new ItemWrapper<Item>(item);
else
return new ItemWrapper<Item>(node2Item.getItem(ses.getNodeByIdentifier(item.getId()), excludes));
}catch(RepositoryException re){
log.error("jcr error getting item by path", re);
GXOutboundErrorResponse.throwException(new BackendGenericError("jcr error getting item by path", re));
}catch(StorageHubException she ){
log.error(she.getErrorMessage(), she);
GXOutboundErrorResponse.throwException(she, Response.Status.fromStatusCode(she.getStatus()));
}finally{
if (ses!=null)
ses.logout();
}
return new ItemWrapper<Item>(toReturn);
}
private Item getChildrenMatchingName(Session ses, String id, String name, List<String> excludes) throws ItemNotFoundException , RepositoryException, StorageHubException {
NodeIterator it = ses.getNodeByIdentifier(id).getNodes();
while (it.hasNext()) {
Node child= it.nextNode();
String nodeName = child.getName();
if (!child.hasProperty(NodeProperty.TITLE.toString())) continue;
String title = child.getProperty(NodeProperty.TITLE.toString()).getString();
if (nodeName.equals(name) || title.equals(name)){
return node2Item.getItem(child, excludes);
}
}
return null;
}
@Deprecated
@GET
@Path("{id}/items/{name}")
@Produces(MediaType.APPLICATION_JSON)
public ItemList findChildrenByNamePattern(@QueryParam("exclude") List<String> excludes, @PathParam("name") String name){
public ItemList findChildrenByNamePatternInPath(@QueryParam("exclude") List<String> excludes, @PathParam("name") String name){
InnerMethodName.instance.set("findChildrenByNamePattern");
return _findChildrenByNamePattern(excludes, name);
}
@GET
@Path("{id}/items")
@Produces(MediaType.APPLICATION_JSON)
public ItemList findChildrenByNamePattern(@QueryParam("exclude") List<String> excludes, @QueryParam("name") String name){
InnerMethodName.instance.set("findChildrenByNamePattern");
return _findChildrenByNamePattern(excludes, name);
}
public ItemList _findChildrenByNamePattern(List<String> excludes, String name){
Session ses = null;
List<Item> toReturn = new ArrayList<>();
try{
ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
authChecker.checkReadAuthorizationControl(ses, id);
authChecker.checkReadAuthorizationControl(ses, currentUser, id);
//NOT using the internal pattern matching of jcr because of title for shared folder
NodeIterator it = ses.getNodeByIdentifier(id).getNodes();
@ -189,11 +284,11 @@ public class ItemsManager {
if (ses!=null)
ses.logout();
}
return new ItemList(toReturn);
}
@GET
@Path("{id}/children/count")
@Produces(MediaType.APPLICATION_JSON)
@ -204,7 +299,7 @@ public class ItemsManager {
try{
ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
authChecker.checkReadAuthorizationControl(ses, id);
authChecker.checkReadAuthorizationControl(ses, currentUser, id);
toReturn = Utils.getItemCount(ses.getNodeByIdentifier(id), showHidden==null?false:showHidden, nodeType!=null ? ClassHandler.instance().get(nodeType) : null);
}catch (ItemNotFoundException e) {
log.error("id {} not found",id,e);
@ -231,7 +326,7 @@ public class ItemsManager {
List<? extends Item> toReturn = null;
try{
ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
authChecker.checkReadAuthorizationControl(ses, id);
authChecker.checkReadAuthorizationControl(ses, currentUser, id);
toReturn = Utils.getItemList(ses.getNodeByIdentifier(id), excludes, null, showHidden==null?false:showHidden, nodeType!=null ? ClassHandler.instance().get(nodeType) : null);
}catch (ItemNotFoundException e) {
log.error("id {} not found",id,e);
@ -250,6 +345,36 @@ public class ItemsManager {
return new ItemList(toReturn);
}
@GET
@Path("{id}/search")
@Produces(MediaType.APPLICATION_JSON)
public ItemList searchItems(@QueryParam("showHidden") Boolean showHidden, @QueryParam("excludeTrashed") Boolean excludeTrashed, @QueryParam("exclude") List<String> excludes, @QueryParam("onlyType") String nodeType,@QueryParam("name") String name ){
InnerMethodName.instance.set("search");
Session ses = null;
List<? extends Item> toReturn = null;
try{
log.debug("search for node {}",name);
ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
authChecker.checkReadAuthorizationControl(ses, currentUser, id);
toReturn = Utils.searchByNameOnFolder(ses, currentUser, authChecker, ses.getNodeByIdentifier(id), excludes, null, showHidden==null?false:showHidden,excludeTrashed==true?false:excludeTrashed , nodeType!=null ? ClassHandler.instance().get(nodeType) : null, name);
log.debug("search retrieved {} elements",toReturn.size());
}catch (ItemNotFoundException e) {
log.error("id {} not found",id,e);
GXOutboundErrorResponse.throwException(new IdNotFoundException(id, e), Status.NOT_FOUND);
}catch(RepositoryException re){
log.error("jcr error getting children", re);
GXOutboundErrorResponse.throwException(new BackendGenericError(re));
}catch(StorageHubException she ){
log.error(she.getErrorMessage(), she);
GXOutboundErrorResponse.throwException(she, Response.Status.fromStatusCode(she.getStatus()));
}finally{
if (ses!=null)
ses.logout();
}
return new ItemList(toReturn);
}
@GET
@Path("{id}/children/paged")
@Produces(MediaType.APPLICATION_JSON)
@ -259,7 +384,7 @@ public class ItemsManager {
List<? extends Item> toReturn = null;
try{
ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
authChecker.checkReadAuthorizationControl(ses, id);
authChecker.checkReadAuthorizationControl(ses, currentUser, id);
toReturn = Utils.getItemList(ses.getNodeByIdentifier(id), excludes, new Range(start, limit),showHidden==null?false:showHidden, nodeType!=null ? ClassHandler.instance().get(nodeType) : null);
}catch (ItemNotFoundException e) {
log.error("id {} not found",id,e);
@ -280,16 +405,14 @@ public class ItemsManager {
@GET
@Path("publiclink/{id}")
@AuthorizationControl(allowed={"URIResolver"}, exception=MyAuthException.class)
@AuthorizationControl(allowedUsers={"URIResolver"}, exception=MyAuthException.class)
public Response resolvePublicLink() {
InnerMethodName.instance.set("resolvePubliclink");
log.warn("arrived id is {}",id);
Session ses = null;
try{
String login = AuthorizationProvider.instance.get().getClient().getId();
ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
String complexId = id;
if (id.startsWith(enchriptedPrefix)) {
@ -321,16 +444,21 @@ public class ItemsManager {
log.warn("item id to retrieve is {}",itemId);
Node selectedNode = ses.getNodeByIdentifier(itemId);
Node selectedNode;
try {
selectedNode= ses.getNodeByIdentifier(itemId);
}catch (ItemNotFoundException e) {
throw new IdNotFoundException(itemId);
}
Item item = node2Item.getItem(selectedNode, Arrays.asList(NodeConstants.ACCOUNTING_NAME, NodeConstants.METADATA_NAME));
if (!(item instanceof AbstractFileItem)) throw new InvalidCallParameters("the choosen item is not a File");
if (versionName!=null)
return downloadVersionInternal(ses, login, itemId, versionName, false);
return downloadVersionInternal(ses, currentUser, itemId, versionName, false);
else
return downloadFileInternal(ses, (AbstractFileItem) item, login, true);
return downloadFileInternal(ses, (AbstractFileItem) item, currentUser, true);
}catch(RepositoryException re ){
@ -355,7 +483,7 @@ public class ItemsManager {
URL toReturn = null;
try{
ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
authChecker.checkReadAuthorizationControl(ses, id);
authChecker.checkReadAuthorizationControl(ses, currentUser, id);
Node selectedNode = ses.getNodeByIdentifier(id);
@ -428,7 +556,7 @@ public class ItemsManager {
String filePublicUrl = String.format("%s/%s%s",basepath, enchriptedPrefix, enchriptedString);
return filePublicUrl;
}
@PUT
@Path("{id}/publish")
@Produces(MediaType.APPLICATION_JSON)
@ -438,7 +566,7 @@ public class ItemsManager {
Item folder= null;
try{
ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
authChecker.checkWriteAuthorizationControl(ses, id, false);
authChecker.checkWriteAuthorizationControl(ses, currentUser, id, false);
Node currentNode =ses.getNodeByIdentifier(id);
log.trace("current node is {}",currentNode.getPath());
@ -446,11 +574,11 @@ public class ItemsManager {
if (!(folder instanceof FolderItem))
throw new InvalidCallParameters("item is not a folder");
currentNode.setProperty(NodeProperty.IS_PUBLIC.toString(), publish);
ses.save();
}catch(RepositoryException re ){
log.error("jcr error getting rootSharedFolder", re);
GXOutboundErrorResponse.throwException(new BackendGenericError(re));
@ -474,7 +602,7 @@ public class ItemsManager {
Item sharedParent= null;
try{
ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
authChecker.checkReadAuthorizationControl(ses, id);
authChecker.checkReadAuthorizationControl(ses, currentUser, id);
Node currentNode =ses.getNodeByIdentifier(id);
log.trace("current node is {}",currentNode.getPath());
@ -517,7 +645,7 @@ public class ItemsManager {
List<org.gcube.common.storagehub.model.service.Version> versions = new ArrayList<>();
try{
ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
authChecker.checkReadAuthorizationControl(ses, id);
authChecker.checkReadAuthorizationControl(ses, currentUser, id);
Node node = ses.getNodeByIdentifier(id);
@ -525,7 +653,7 @@ public class ItemsManager {
if (!(currentItem instanceof AbstractFileItem))
throw new InvalidItemException("this item is not versioned");
List<Version> jcrVersions = versionHandler.getContentVersionHistory(node, ses);
List<Version> jcrVersions = versionHandler.getContentVersionHistory(node);
for (Version version: jcrVersions) {
boolean currentVersion = ((AbstractFileItem)currentItem).getContent().getStorageId().equals(version.getFrozenNode().getProperty(NodeProperty.STORAGE_ID.toString()).getString());
@ -551,11 +679,10 @@ public class ItemsManager {
InnerMethodName.instance.set("downloadSpecificVersion");
Session ses = null;
try{
String login = AuthorizationProvider.instance.get().getClient().getId();
ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
authChecker.checkReadAuthorizationControl(ses, id);
authChecker.checkReadAuthorizationControl(ses, currentUser, id);
return downloadVersionInternal(ses, login, id, versionName, true);
return downloadVersionInternal(ses, currentUser, id, versionName, true);
}catch(RepositoryException re ){
log.error("jcr error downloading version", re);
@ -576,32 +703,33 @@ public class ItemsManager {
if (!(currentItem instanceof AbstractFileItem))
throw new InvalidItemException("this item is not a file");
List<Version> jcrVersions = versionHandler.getContentVersionHistory(ses.getNodeByIdentifier(id), ses);
List<Version> jcrVersions = versionHandler.getContentVersionHistory(node);
for (Version version: jcrVersions) {
log.debug("retrieved version id {}, name {}", version.getIdentifier(), version.getName());
if (version.getName().equals(versionName)) {
long size = version.getFrozenNode().getProperty(NodeProperty.SIZE.toString()).getLong();
String mimeType = version.getFrozenNode().getProperty(NodeProperty.MIME_TYPE.toString()).getString();
String storageId = version.getFrozenNode().getProperty(NodeProperty.STORAGE_ID.toString()).getString();
final InputStream streamToWrite = storageBackend.download(storageId);
Content content = node2Item.getContentFromVersion(version);
FolderManager folderManager = folderPluginHandler.getFolderManager((AbstractFileItem) currentItem);
final InputStream streamToWrite = folderManager.getStorageBackend().download(content);
log.debug("retrieved storage id is {} with storageBackend {} (stream is null? {})",content.getStorageId(), folderManager.getStorageBackend().getClass().getSimpleName(), streamToWrite==null );
String oldfilename = FilenameUtils.getBaseName(currentItem.getTitle());
String ext = FilenameUtils.getExtension(currentItem.getTitle());
String fileName = String.format("%s_v%s.%s", oldfilename, version.getName(), ext);
if (withAccounting)
accountingHandler.createReadObj(fileName, ses, node, true);
accountingHandler.createReadObj(fileName, ses, node, login, true);
StreamingOutput so = new SingleFileStreamingOutput(streamToWrite);
return Response
.ok(so)
.header("content-disposition","attachment; filename = "+fileName)
.header("Content-Length", size)
.header("Content-Type", mimeType)
.header("Content-Length", content.getSize())
.header("Content-Type", content.getMimeType())
.build();
}
}
@ -613,26 +741,29 @@ public class ItemsManager {
@Produces(MediaType.APPLICATION_JSON)
public ItemList getAnchestors(@QueryParam("exclude") List<String> excludes){
InnerMethodName.instance.set("getAnchestors");
org.gcube.common.storagehub.model.Path absolutePath = Utils.getWorkspacePath();
org.gcube.common.storagehub.model.Path absolutePath = pathUtil.getWorkspacePath(currentUser);
Session ses = null;
List<Item> toReturn = new LinkedList<>();
try{
String login = AuthorizationProvider.instance.get().getClient().getId();
ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
authChecker.checkReadAuthorizationControl(ses, id);
authChecker.checkReadAuthorizationControl(ses, currentUser, id);
Node currentNode = ses.getNodeByIdentifier(id);
Item currentItem = node2Item.getItem(currentNode, excludes);
log.trace("current node is {}",currentNode.getPath());
while (!(currentNode.getPath()+"/").equals(absolutePath.toPath())) {
while (!(currentNode.getPath().matches("/Home/[^/]{1,}/Workspace"))) {
if (currentItem instanceof SharedFolder){
NodeIterator sharedSetIterator = currentNode.getSharedSet();
boolean found = false;
while (sharedSetIterator.hasNext()) {
Node sharedNode = sharedSetIterator.nextNode();
if (sharedNode.getPath().startsWith(Utils.getWorkspacePath(login).toPath())) {
if (sharedNode.getPath().startsWith(absolutePath.toPath())) {
currentNode = sharedNode.getParent();
found=true;
break;
}
}
if (!found) break;
currentItem = node2Item.getItem(currentNode, excludes);
}else {
currentNode = currentNode.getParent();
@ -669,17 +800,16 @@ public class ItemsManager {
Session ses = null;
Response response = null;
try{
final String login = AuthorizationProvider.instance.get().getClient().getId();
ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
final Node node = ses.getNodeByIdentifier(id);
authChecker.checkReadAuthorizationControl(ses, id);
authChecker.checkReadAuthorizationControl(ses, currentUser, id);
final Item item = node2Item.getItem(node, null);
if (item instanceof AbstractFileItem){
return downloadFileInternal(ses, (AbstractFileItem) item, login, true);
return downloadFileInternal(ses, (AbstractFileItem) item, currentUser, true);
} else if (item instanceof FolderItem){
try {
final Deque<Item> allNodes = Utils.getAllNodesForZip((FolderItem)item, ses, accountingHandler, excludes);
final Deque<Item> allNodes = compressHandler.getAllNodesForZip((FolderItem)item, ses, currentUser, accountingHandler, excludes);
final org.gcube.common.storagehub.model.Path originalPath = Paths.getPath(item.getParentPath());
StreamingOutput so = new StreamingOutput() {
@ -690,7 +820,7 @@ public class ItemsManager {
long start = System.currentTimeMillis();
zos.setLevel(Deflater.BEST_COMPRESSION);
log.debug("writing StreamOutput");
Utils.zipNode(zos, allNodes, login, originalPath, storageBackend);
compressHandler.zipNode(zos, allNodes, currentUser, originalPath);
log.debug("StreamOutput written in {}",(System.currentTimeMillis()-start));
} catch (Exception e) {
log.error("error writing stream",e);
@ -706,7 +836,7 @@ public class ItemsManager {
.header("Content-Length", -1l)
.build();
accountingHandler.createReadObj(item.getTitle(), ses, ses.getNodeByIdentifier(item.getId()), false);
accountingHandler.createReadObj(item.getTitle(), ses, (Node) item.getRelatedNode(), currentUser, false);
}finally {
if (ses!=null) ses.save();
}
@ -725,12 +855,14 @@ public class ItemsManager {
return response;
}
private Response downloadFileInternal(Session ses, AbstractFileItem fileItem, String login, boolean withAccounting) throws RepositoryException {
private Response downloadFileInternal(Session ses, AbstractFileItem fileItem, String login, boolean withAccounting) throws RepositoryException, PluginInitializationException, PluginNotFoundException, BackendGenericError {
final InputStream streamToWrite = storageBackend.download(fileItem.getContent().getStorageId());
FolderManager folderManager = folderPluginHandler.getFolderManager(fileItem);
final InputStream streamToWrite = folderManager.getStorageBackend().download(fileItem.getContent());
if (withAccounting)
accountingHandler.createReadObj(fileItem.getTitle(), ses, ses.getNodeByIdentifier(fileItem.getId()), true);
accountingHandler.createReadObj(fileItem.getTitle(), ses, (Node) fileItem.getRelatedNode(), login, true);
StreamingOutput so = new SingleFileStreamingOutput(streamToWrite);
@ -751,13 +883,12 @@ public class ItemsManager {
Session ses = null;
try{
final String login = AuthorizationProvider.instance.get().getClient().getId();
ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
authChecker.checkMoveOpsForProtectedFolders(ses, id);
authChecker.checkWriteAuthorizationControl(ses, destinationId, true);
authChecker.checkWriteAuthorizationControl(ses, id, false);
authChecker.checkWriteAuthorizationControl(ses, currentUser, destinationId, true);
authChecker.checkWriteAuthorizationControl(ses, currentUser, id, false);
final Node nodeToMove = ses.getNodeByIdentifier(id);
final Node destination = ses.getNodeByIdentifier(destinationId);
@ -778,12 +909,11 @@ public class ItemsManager {
if (!(destinationItem instanceof FolderItem))
throw new InvalidItemException("destination item is not a folder");
if (item.isShared() && (!destinationItem.isShared() || !getSharedParentNode(nodeToMove).getIdentifier().equals(getSharedParentNode(destination).getIdentifier())))
throw new InvalidCallParameters("shared Item cannot be moved in a different shared folder or in a private folder");
boolean movingSharedItemOutside = item.isShared() && (!destinationItem.isShared() || !getSharedParentNode(nodeToMove).getIdentifier().equals(getSharedParentNode(destination).getIdentifier()));
try {
ses.getWorkspace().getLockManager().lock(destination.getPath(), false, true, 0,login);
ses.getWorkspace().getLockManager().lock(nodeToMove.getPath(), true, true, 0,login);
ses.getWorkspace().getLockManager().lock(destination.getPath(), false, true, 0,currentUser);
ses.getWorkspace().getLockManager().lock(nodeToMove.getPath(), true, true, 0,currentUser);
}catch (LockException e) {
throw new ItemLockedException(e);
}
@ -791,13 +921,18 @@ public class ItemsManager {
String uniqueName =(Utils.checkExistanceAndGetUniqueName(ses, destination, nodeToMove.getName()));
String newPath = String.format("%s/%s",destination.getPath(), uniqueName);
ses.getWorkspace().move(nodeToMove.getPath(), newPath);
Utils.setPropertyOnChangeNode(ses.getNode(newPath), login, ItemAction.MOVED);
ses.move(nodeToMove.getPath(), newPath);
Utils.setPropertyOnChangeNode(ses.getNode(newPath), currentUser, ItemAction.MOVED);
String mimeTypeForAccounting = (item instanceof AbstractFileItem)? ((AbstractFileItem) item).getContent().getMimeType(): null;
accountingHandler.createFolderAddObj(uniqueName, item.getClass().getSimpleName(), mimeTypeForAccounting , ses, destination, false);
accountingHandler.createFolderRemoveObj(item.getTitle(), item.getClass().getSimpleName(), mimeTypeForAccounting, ses, originalParent, false);
if (movingSharedItemOutside)
item2Node.updateOwnerOnSubTree(nodeToMove, currentUser);
//folderHandler.onMove(source, destination);
accountingHandler.createFolderAddObj(uniqueName, item.getClass().getSimpleName(), mimeTypeForAccounting, ses, currentUser, destination, false);
accountingHandler.createFolderRemoveObj(item.getTitle(), item.getClass().getSimpleName(), mimeTypeForAccounting, ses, currentUser, originalParent, false);
ses.save();
}finally {
ses.getWorkspace().getLockManager().unlock(nodeToMove.getPath());
@ -826,13 +961,11 @@ public class ItemsManager {
Session ses = null;
String newFileIdentifier = null;
try{
final String login = AuthorizationProvider.instance.get().getClient().getId();
//ses = RepositoryInitializer.getRepository().login(new SimpleCredentials(login,Utils.getSecurePassword(login).toCharArray()));
//TODO check if it is possible to change all the ACL on a workspace
ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
authChecker.checkWriteAuthorizationControl(ses, destinationId, true);
authChecker.checkReadAuthorizationControl(ses, id);
authChecker.checkWriteAuthorizationControl(ses, currentUser, destinationId, true);
authChecker.checkReadAuthorizationControl(ses, currentUser, id);
final Node nodeToCopy = ses.getNodeByIdentifier(id);
final Node destination = ses.getNodeByIdentifier(destinationId);
@ -844,8 +977,8 @@ public class ItemsManager {
throw new InvalidItemException("folder cannot be copied");
try {
ses.getWorkspace().getLockManager().lock(destination.getPath(), false, true, 0,login);
ses.getWorkspace().getLockManager().lock(nodeToCopy.getPath(), true, true, 0,login);
ses.getWorkspace().getLockManager().lock(destination.getPath(), false, true, 0,currentUser);
ses.getWorkspace().getLockManager().lock(nodeToCopy.getPath(), true, true, 0,currentUser);
}catch (LockException e) {
throw new ItemLockedException(e);
}
@ -856,20 +989,23 @@ public class ItemsManager {
Node newNode = ses.getNode(newPath);
newFileIdentifier = newNode.getIdentifier();
//TODO: folderHandler.onCopy(source, destination);
if (item instanceof AbstractFileItem) {
FolderManager manager = folderPluginHandler.getFolderManager(item);
((AbstractFileItem) item).getContent().setRemotePath(newPath);
String newStorageID = storageBackend.copy((AbstractFileItem) item);
String newStorageID = manager.getStorageBackend().onCopy((AbstractFileItem) item);
((AbstractFileItem) item).getContent().setStorageId(newStorageID);
item2Node.replaceContent(newNode, (AbstractFileItem) item, ItemAction.CLONED);
}
Utils.setPropertyOnChangeNode(newNode, login, ItemAction.CLONED);
newNode.setProperty(NodeProperty.PORTAL_LOGIN.toString(), login);
Utils.setPropertyOnChangeNode(newNode, currentUser, ItemAction.CLONED);
newNode.setProperty(NodeProperty.PORTAL_LOGIN.toString(), currentUser);
newNode.setProperty(NodeProperty.IS_PUBLIC.toString(), false);
newNode.setProperty(NodeProperty.TITLE.toString(), uniqueName);
String mimeTypeForAccounting = (item instanceof AbstractFileItem)? ((AbstractFileItem) item).getContent().getMimeType(): null;
accountingHandler.createFolderAddObj(uniqueName, item.getClass().getSimpleName(), mimeTypeForAccounting, ses, destination, false);
accountingHandler.createFolderAddObj(uniqueName, item.getClass().getSimpleName(), mimeTypeForAccounting, ses, currentUser, destination, false);
ses.save();
@ -900,12 +1036,11 @@ public class ItemsManager {
Session ses = null;
try{
final String login = AuthorizationProvider.instance.get().getClient().getId();
ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
authChecker.checkMoveOpsForProtectedFolders(ses, id);
authChecker.checkWriteAuthorizationControl(ses, id, false);
authChecker.checkWriteAuthorizationControl(ses, currentUser, id, false);
final Node nodeToMove = ses.getNodeByIdentifier(id);
@ -919,8 +1054,8 @@ public class ItemsManager {
try {
ses.getWorkspace().getLockManager().lock(nodeToMove.getPath(), true, true, 0,login);
ses.getWorkspace().getLockManager().lock(nodeToMove.getParent().getPath(), false, true, 0,login);
ses.getWorkspace().getLockManager().lock(nodeToMove.getPath(), true, true, 0,currentUser);
ses.getWorkspace().getLockManager().lock(nodeToMove.getParent().getPath(), false, true, 0,currentUser);
}catch (LockException e) {
throw new ItemLockedException(e);
}
@ -930,9 +1065,9 @@ public class ItemsManager {
String newPath = String.format("%s/%s", nodeToMove.getParent().getPath(), uniqueName);
nodeToMove.setProperty(NodeProperty.TITLE.toString(), uniqueName);
Utils.setPropertyOnChangeNode(nodeToMove, login, ItemAction.RENAMED);
Utils.setPropertyOnChangeNode(nodeToMove, currentUser, ItemAction.RENAMED);
ses.move(nodeToMove.getPath(), newPath);
accountingHandler.createRename(item.getTitle(), uniqueName, ses.getNode(newPath), ses, false);
accountingHandler.createRename(item.getTitle(), uniqueName, ses.getNode(newPath), currentUser, ses, false);
ses.save();
}finally {
ses.getWorkspace().getLockManager().unlock(nodeToMove.getPath());
@ -954,6 +1089,96 @@ public class ItemsManager {
return Response.ok(id).build();
}
//TODO: transform this and setMetadata in a generic method for all properties
@PUT
@Consumes(MediaType.APPLICATION_JSON)
@Path("/{id}/hidden")
public Response setItemAsHidden(Boolean hidden){
InnerMethodName.instance.set("setHidden");
Session ses = null;
try{
ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
authChecker.checkWriteAuthorizationControl(ses, currentUser, id, false);
final Node nodeToUpdate = ses.getNodeByIdentifier(id);
try {
ses.getWorkspace().getLockManager().lock(nodeToUpdate.getPath(), false, true, 0,currentUser);
}catch (LockException e) {
throw new ItemLockedException(e);
}
try {
item2Node.updateHidden(nodeToUpdate, hidden, currentUser);
ses.save();
}finally {
ses.getWorkspace().getLockManager().unlock(nodeToUpdate.getPath());
}
//TODO: UPDATE accounting
}catch(RepositoryException re ){
log.error("jcr error moving item", re);
GXOutboundErrorResponse.throwException(new BackendGenericError(re));
}catch(StorageHubException she ){
log.error(she.getErrorMessage(), she);
GXOutboundErrorResponse.throwException(she, Response.Status.fromStatusCode(she.getStatus()));
}finally{
if (ses!=null) {
ses.logout();
}
}
return Response.ok(id).build();
}
//TODO: transform this and setMetadata in a generic method for all properties
@PUT
@Consumes(MediaType.APPLICATION_JSON)
@Path("/{id}/description")
public Response setDescription(String description){
InnerMethodName.instance.set("setDescription");
Session ses = null;
try{
ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
authChecker.checkWriteAuthorizationControl(ses, currentUser, id, false);
final Node nodeToUpdate = ses.getNodeByIdentifier(id);
try {
ses.getWorkspace().getLockManager().lock(nodeToUpdate.getPath(), false, true, 0,currentUser);
}catch (LockException e) {
throw new ItemLockedException(e);
}
try {
item2Node.updateDescription(nodeToUpdate, description, currentUser);
ses.save();
}finally {
ses.getWorkspace().getLockManager().unlock(nodeToUpdate.getPath());
}
//TODO: UPDATE accounting
}catch(RepositoryException re ){
log.error("jcr error moving item", re);
GXOutboundErrorResponse.throwException(new BackendGenericError(re));
}catch(StorageHubException she ){
log.error(she.getErrorMessage(), she);
GXOutboundErrorResponse.throwException(she, Response.Status.fromStatusCode(she.getStatus()));
}finally{
if (ses!=null) {
ses.logout();
}
}
return Response.ok(id).build();
}
@PUT
@Consumes(MediaType.APPLICATION_JSON)
@Path("/{id}/metadata")
@ -963,21 +1188,20 @@ public class ItemsManager {
Session ses = null;
try{
final String login = AuthorizationProvider.instance.get().getClient().getId();
ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
authChecker.checkWriteAuthorizationControl(ses, id, false);
authChecker.checkWriteAuthorizationControl(ses, currentUser, id, false);
final Node nodeToUpdate = ses.getNodeByIdentifier(id);
try {
ses.getWorkspace().getLockManager().lock(nodeToUpdate.getPath(), false, true, 0,login);
ses.getWorkspace().getLockManager().lock(nodeToUpdate.getPath(), false, true, 0,currentUser);
}catch (LockException e) {
throw new ItemLockedException(e);
}
try {
item2Node.updateMetadataNode(nodeToUpdate, metadata.getMap(), login);
item2Node.updateMetadataNode(nodeToUpdate, metadata.getMap(), currentUser);
ses.save();
}finally {
ses.getWorkspace().getLockManager().unlock(nodeToUpdate.getPath());
@ -1013,25 +1237,28 @@ public class ItemsManager {
//TODO check if it is possible to change all the ACL on a workspace
ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
authChecker.checkMoveOpsForProtectedFolders(ses, id);
authChecker.checkWriteAuthorizationControl(ses, id, false);
final Node nodeToDelete = ses.getNodeByIdentifier(id);
authChecker.checkWriteAuthorizationControl(ses, currentUser, id, false);
authChecker.checkMoveOpsForProtectedFolders(ses, id);
final Node nodeToDelete = ses.getNodeByIdentifier(id);
Item itemToDelete = node2Item.getItem(nodeToDelete, Excludes.GET_ONLY_CONTENT);
if (itemToDelete instanceof SharedFolder || itemToDelete instanceof VreFolder || (itemToDelete instanceof FolderItem && Utils.hasSharedChildren(nodeToDelete)))
throw new InvalidItemException("SharedFolder, VreFolder or folders with shared children cannot be deleted");
if (itemToDelete.isExternalManaged() && !force)
throw new InvalidItemException("External managed Items cannot be moved to Trash");
log.debug("item is trashed? {}", itemToDelete.isTrashed());
if (!itemToDelete.isTrashed() && !force)
trashHandler.moveToTrash(ses, nodeToDelete, itemToDelete);
else
if (!itemToDelete.isTrashed() && !force) {
trashHandler.moveToTrash(ses, nodeToDelete, itemToDelete, currentUser);
}else
trashHandler.removeNodes(ses, Collections.singletonList(itemToDelete));
}catch (LockException e) {
}catch(RepositoryException re ){
log.error("jcr error moving item", re);
GXOutboundErrorResponse.throwException(new BackendGenericError(re));

@ -0,0 +1,434 @@
package org.gcube.data.access.storagehub.services;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.jcr.ItemNotFoundException;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.servlet.ServletContext;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import org.apache.jackrabbit.api.JackrabbitSession;
import org.apache.jackrabbit.api.security.user.User;
import org.gcube.common.gxrest.response.outbound.GXOutboundErrorResponse;
import org.gcube.common.storagehub.model.Excludes;
import org.gcube.common.storagehub.model.Paths;
import org.gcube.common.storagehub.model.exceptions.BackendGenericError;
import org.gcube.common.storagehub.model.exceptions.IdNotFoundException;
import org.gcube.common.storagehub.model.exceptions.InvalidCallParameters;
import org.gcube.common.storagehub.model.exceptions.StorageHubException;
import org.gcube.common.storagehub.model.exceptions.UserNotAuthorizedException;
import org.gcube.common.storagehub.model.items.AbstractFileItem;
import org.gcube.common.storagehub.model.items.Item;
import org.gcube.common.storagehub.model.items.nodes.Owner;
import org.gcube.common.storagehub.model.messages.Message;
import org.gcube.common.storagehub.model.service.ItemList;
import org.gcube.common.storagehub.model.types.ItemAction;
import org.gcube.common.storagehub.model.types.MessageList;
import org.gcube.common.storagehub.model.types.NodeProperty;
import org.gcube.data.access.storagehub.Constants;
import org.gcube.data.access.storagehub.PathUtil;
import org.gcube.data.access.storagehub.StorageHubAppllicationManager;
import org.gcube.data.access.storagehub.Utils;
import org.gcube.data.access.storagehub.accounting.AccountingHandler;
import org.gcube.data.access.storagehub.handlers.CredentialHandler;
import org.gcube.data.access.storagehub.handlers.TrashHandler;
import org.gcube.data.access.storagehub.handlers.items.Item2NodeConverter;
import org.gcube.data.access.storagehub.handlers.items.Item2NodeConverter.Values;
import org.gcube.data.access.storagehub.handlers.items.Node2ItemConverter;
import org.gcube.data.access.storagehub.handlers.plugins.FolderPluginHandler;
import org.gcube.data.access.storagehub.types.MessageSharable;
import org.gcube.smartgears.annotations.ManagedBy;
import org.gcube.smartgears.utils.InnerMethodName;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.webcohesion.enunciate.metadata.rs.RequestHeader;
import com.webcohesion.enunciate.metadata.rs.RequestHeaders;
@Path("messages")
@ManagedBy(StorageHubAppllicationManager.class)
@RequestHeaders({
@RequestHeader( name = "Authorization", description = "Bearer token, see https://dev.d4science.org/how-to-access-resources"),
})
public class MessageManager extends Impersonable{
private static final Logger log = LoggerFactory.getLogger(MessageManager.class);
RepositoryInitializer repository = StorageHubAppllicationManager.repository;
@Inject
AccountingHandler accountingHandler;
@RequestScoped
@PathParam("id")
String id;
@Context
ServletContext context;
@Inject PathUtil pathUtil;
@Inject Node2ItemConverter node2Item;
@Inject Item2NodeConverter item2Node;
@Inject TrashHandler trashHandler;
@GET
@Path("{id}")
@Produces(MediaType.APPLICATION_JSON)
public Message getById(){
InnerMethodName.instance.set("getMessageById");
Session ses = null;
Message toReturn = null;
try{
ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
Node messageNode = ses.getNodeByIdentifier(id);
toReturn = node2Item.getMessageItem(messageNode);
checkRights(currentUser, toReturn);
}catch (ItemNotFoundException e) {
log.error("id {} not found",id,e);
GXOutboundErrorResponse.throwException(new IdNotFoundException(id, e), Status.NOT_FOUND);
}catch(RepositoryException re){
log.error("jcr error getting item", re);
GXOutboundErrorResponse.throwException(new BackendGenericError("jcr error getting item", re));
}catch(StorageHubException she ){
log.error(she.getErrorMessage(), she);
GXOutboundErrorResponse.throwException(she, Response.Status.fromStatusCode(she.getStatus()));
}finally{
if (ses!=null)
ses.logout();
}
return toReturn;
}
@DELETE
@Path("{id}")
public void deleteById(){
InnerMethodName.instance.set("deleteMessageById");
Session ses = null;
try{
ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
Node messageNode = ses.getNodeByIdentifier(id);
Message message = node2Item.getMessageItem(messageNode);
Node personalNode = checkRights(currentUser, message);
if (countSharedSet(messageNode)>1)
personalNode.removeShare();
else {
if (message.isWithAttachments()) {
Node attachmentNode = messageNode.getNode(Constants.ATTACHMENTNODE_NAME);
List<Item> attachments = Utils.getItemList(attachmentNode, Excludes.GET_ONLY_CONTENT, null, true, AbstractFileItem.class);
trashHandler.removeOnlyNodesContent(ses, attachments);
}
messageNode.removeSharedSet();
}
ses.save();
}catch (ItemNotFoundException e) {
log.error("id {} not found",id,e);
GXOutboundErrorResponse.throwException(new IdNotFoundException(id, e), Status.NOT_FOUND);
}catch(RepositoryException re){
log.error("jcr error getting item", re);
GXOutboundErrorResponse.throwException(new BackendGenericError("jcr error getting item", re));
}catch(StorageHubException she ){
log.error(she.getErrorMessage(), she);
GXOutboundErrorResponse.throwException(she, Response.Status.fromStatusCode(she.getStatus()));
}finally{
if (ses!=null)
ses.logout();
}
}
@GET
@Path("{id}/attachments")
@Produces(MediaType.APPLICATION_JSON)
public ItemList getAttachments(){
InnerMethodName.instance.set("getAttachmentsByMessageId");
Session ses = null;
List<Item> attachments = new ArrayList<>();
try{
ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
Node messageNode = ses.getNodeByIdentifier(id);
Message messageItem = node2Item.getMessageItem(messageNode);
checkRights(currentUser, messageItem);
Node attachmentNode = messageNode.getNode(Constants.ATTACHMENTNODE_NAME);
attachments = Utils.getItemList(attachmentNode, Excludes.GET_ONLY_CONTENT, null, true, AbstractFileItem.class);
}catch (ItemNotFoundException e) {
log.error("id {} not found",id,e);
GXOutboundErrorResponse.throwException(new IdNotFoundException(id, e), Status.NOT_FOUND);
}catch(RepositoryException re){
log.error("jcr error getting item", re);
GXOutboundErrorResponse.throwException(new BackendGenericError("jcr error getting item", re));
}catch(StorageHubException she ){
log.error(she.getErrorMessage(), she);
GXOutboundErrorResponse.throwException(she, Response.Status.fromStatusCode(she.getStatus()));
}finally{
if (ses!=null)
ses.logout();
}
return new ItemList(attachments);
}
@GET
@Path("inbox")
@Produces(MediaType.APPLICATION_JSON)
public MessageList getReceivedMessages(@QueryParam("reduceBody") Integer reduceBody){
InnerMethodName.instance.set("getReceivedMessages");
Session ses = null;
List<Message> toReturn = null;
try{
ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
Node node = ses.getNode(pathUtil.getInboxPath(currentUser).toPath());
//return sorted for createdTime
toReturn = getMessages(node, reduceBody);
}catch(RepositoryException re){
log.error("jcr error getting item", re);
GXOutboundErrorResponse.throwException(new BackendGenericError("jcr error getting item", re));
}finally{
if (ses!=null)
ses.logout();
}
return new MessageList(toReturn);
}
@GET
@Path("sent")
@Produces(MediaType.APPLICATION_JSON)
public MessageList getSentMessages(@QueryParam("reduceBody") Integer reduceBody){
InnerMethodName.instance.set("getSentMessages");
Session ses = null;
List<Message> toReturn = null;
try{
ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
Node node = ses.getNode(pathUtil.getOutboxPath(currentUser).toPath());
toReturn = getMessages(node, reduceBody);
}catch(RepositoryException re){
log.error("jcr error getting item", re);
GXOutboundErrorResponse.throwException(new BackendGenericError("jcr error getting item", re));
}finally{
if (ses!=null)
ses.logout();
}
return new MessageList(toReturn);
}
@PUT
@Path("{id}/{prop}")
@Consumes(MediaType.APPLICATION_JSON)
public void setProperty(@PathParam("prop") String property,Object value){
InnerMethodName.instance.set("setPropertyOnMessage("+property+")");
Session ses = null;
try{
ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
Node messageNode = ses.getNodeByIdentifier(id);
Message messageItem = node2Item.getMessageItem(messageNode);
checkRights(currentUser, messageItem);
Values val = Item2NodeConverter.getObjectValue(value.getClass(), value);
messageNode.setProperty(property, val.getValue());
ses.save();
}catch (ItemNotFoundException e) {
log.error("id {} not found",id,e);
GXOutboundErrorResponse.throwException(new IdNotFoundException(id, e), Status.NOT_FOUND);
}catch(StorageHubException she ){
log.error(she.getErrorMessage(), she);
GXOutboundErrorResponse.throwException(she, Response.Status.fromStatusCode(she.getStatus()));
}catch( Exception re){
log.error("jcr error getting item", re);
GXOutboundErrorResponse.throwException(new BackendGenericError("jcr error getting item", re));
}finally{
if (ses!=null)
ses.logout();
}
}
@POST
@Path("send")
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public String sendMessage(@FormParam("to[]") List<String> addresses,
@FormParam("subject") String subject, @FormParam("body") String body,
@FormParam("attachments[]") List<String> attachments){
InnerMethodName.instance.set("sendMessage");
JackrabbitSession ses = null;
String messageId = null;
try{
if (addresses.size()==0 || body==null || subject==null)
throw new InvalidCallParameters();
log.debug("attachments send are {}",attachments);
ses = (JackrabbitSession) repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
Message message = new MessageSharable();
message.setAddresses(addresses.toArray(new String[0]));
message.setSubject(subject);
message.setBody(body);
message.setName(UUID.randomUUID().toString());
User user = ses.getUserManager().getAuthorizable(currentUser, User.class);
Owner owner = new Owner();
owner.setUserId(user.getID());
owner.setUserName(user.getPrincipal().getName());
message.setSender(owner);
Node outbox = ses.getNode(pathUtil.getOutboxPath(currentUser).toPath());
Node messageNode = item2Node.getNode(outbox, message);
ses.save();
if (attachments!=null && !attachments.isEmpty()) {
saveAttachments(ses, messageNode, attachments);
ses.save();
}
for (String to: addresses)
try {
String userMessagePath = Paths.append(pathUtil.getInboxPath(to), messageNode.getName()).toPath();
ses.getWorkspace().clone(ses.getWorkspace().getName(), messageNode.getPath(), userMessagePath, false);
}catch (Exception e) {
log.warn("message not send to {}",to,e);
}
ses.save();
messageId = messageNode.getIdentifier();
}catch(RepositoryException re){
log.error("jcr error getting item", re);
GXOutboundErrorResponse.throwException(new BackendGenericError("jcr error getting item", re));
}catch(StorageHubException she ){
log.error(she.getErrorMessage(), she);
GXOutboundErrorResponse.throwException(she, Response.Status.fromStatusCode(she.getStatus()));
}finally{
if (ses!=null)
ses.logout();
}
return messageId;
}
private Node saveAttachments(Session ses, Node messageNode , List<String> attachments) throws RepositoryException, BackendGenericError{
Node attachmentNode = messageNode.getNode(Constants.ATTACHMENTNODE_NAME);
for (String itemId: attachments) {
Node node = ses.getNodeByIdentifier(itemId);
Item item = node2Item.getItem(node, Excludes.GET_ONLY_CONTENT);
Node newNode = copyNode(ses, attachmentNode, item);
//removes accounting if exists
if (newNode.hasNode(NodeProperty.ACCOUNTING.toString()))
newNode.getNode(NodeProperty.ACCOUNTING.toString()).remove();
}
return messageNode;
}
//returns Messages sorted by createdTime
private List<Message> getMessages(Node node, Integer reduceBody) throws RepositoryException{
List<Message> messages = new ArrayList<Message>();
NodeIterator nodeIt = node.getNodes();
while(nodeIt.hasNext()) {
Node child = nodeIt.nextNode();
log.info("message type "+child.getPrimaryNodeType().getName());
Message message = node2Item.getMessageItem(child);
if (message == null) {
log.info("message discarded");
continue;
}
if (reduceBody != null && reduceBody>0 && message.getBody().length()>reduceBody )
message.setBody(message.getBody().substring(0, reduceBody));
insertOrdered(messages, message);
}
return messages;
}
private void insertOrdered(List<Message> messages, Message toInsert) {
if (messages.isEmpty())
messages.add(toInsert);
else {
int i;
for ( i=0 ; i<messages.size(); i++)
if (messages.get(i).getCreationTime().getTimeInMillis()<=toInsert.getCreationTime().getTimeInMillis())
break;
messages.add(i, toInsert);
}
}
private Node checkRights(String user, Message messageItem) throws RepositoryException, StorageHubException{
Node personalNode = null;
Node messageNode = (Node) messageItem.getRelatedNode();
if (messageNode.getPath().startsWith(pathUtil.getInboxPath(user).toPath()))
return messageNode;
NodeIterator nodeIt = messageNode.getSharedSet();
while (nodeIt.hasNext()) {
Node node = nodeIt.nextNode();
if (node.getPath().startsWith(pathUtil.getInboxPath(user).toPath()))
personalNode = node;
}
if (personalNode == null &&
!messageItem.getSender().getUserName().equals(user) && !Arrays.asList(messageItem.getAddresses()).contains(user))
throw new UserNotAuthorizedException("user "+currentUser+"cannot read message with id "+id);
return personalNode== null ? messageNode : personalNode;
}
//TODO: move in a common place
@Inject FolderPluginHandler folderPluginHandler;
private Node copyNode(Session session, Node destination, Item itemToCopy) throws RepositoryException, BackendGenericError{
//it needs to be locked ??
Node nodeToCopy = ((Node)itemToCopy.getRelatedNode());
String uniqueName = Utils.checkExistanceAndGetUniqueName(session, destination,itemToCopy.getName() );
String newPath= String.format("%s/%s", destination.getPath(), uniqueName);
session.getWorkspace().copy(nodeToCopy.getPath(), newPath);
Node newNode = session.getNode(newPath);
if (itemToCopy instanceof AbstractFileItem) {
AbstractFileItem newNodeItem = node2Item.getItem(newNode, Excludes.EXCLUDE_ACCOUNTING);
newNodeItem.getContent().setRemotePath(newPath);
String newStorageID = folderPluginHandler.getDefault().getStorageBackend().onCopy(newNodeItem);
newNodeItem.getContent().setStorageId(newStorageID);
item2Node.replaceContent(newNode, newNodeItem, ItemAction.CLONED);
}
Utils.setPropertyOnChangeNode(newNode, currentUser, ItemAction.CLONED);
newNode.setProperty(NodeProperty.PORTAL_LOGIN.toString(), currentUser);
newNode.setProperty(NodeProperty.IS_PUBLIC.toString(), false);
newNode.setProperty(NodeProperty.TITLE.toString(), uniqueName);
return newNode;
}
private int countSharedSet(Node node) throws RepositoryException{
int count =0;
NodeIterator it = node.getSharedSet();
while (it.hasNext()) {
count ++;
it.next();
}
return count;
}
}

@ -5,4 +5,6 @@ import javax.jcr.Repository;
public interface RepositoryInitializer {
Repository getRepository();
void shutdown();
}

@ -2,13 +2,17 @@ package org.gcube.data.access.storagehub.services;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import javax.inject.Inject;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.query.QueryResult;
import javax.jcr.PathNotFoundException;
import javax.jcr.RepositoryException;
import javax.servlet.ServletContext;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
@ -20,49 +24,86 @@ import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.apache.jackrabbit.api.JackrabbitSession;
import org.apache.jackrabbit.api.security.user.Authorizable;
import org.apache.jackrabbit.api.security.user.Group;
import org.apache.jackrabbit.api.security.user.Query;
import org.apache.jackrabbit.api.security.user.QueryBuilder;
import org.apache.jackrabbit.api.security.user.User;
import org.apache.jackrabbit.core.security.principal.PrincipalImpl;
import org.gcube.common.authorization.control.annotations.AuthorizationControl;
import org.gcube.common.gxrest.response.outbound.GXOutboundErrorResponse;
import org.gcube.common.storagehub.model.Excludes;
import org.gcube.common.storagehub.model.exceptions.BackendGenericError;
import org.gcube.common.storagehub.model.types.NodeProperty;
import org.gcube.common.storagehub.model.exceptions.IdNotFoundException;
import org.gcube.common.storagehub.model.exceptions.StorageHubException;
import org.gcube.common.storagehub.model.exceptions.UserNotAuthorizedException;
import org.gcube.common.storagehub.model.items.Item;
import org.gcube.common.storagehub.model.items.SharedFolder;
import org.gcube.data.access.storagehub.AuthorizationChecker;
import org.gcube.data.access.storagehub.Constants;
import org.gcube.data.access.storagehub.PathUtil;
import org.gcube.data.access.storagehub.StorageHubAppllicationManager;
import org.gcube.data.access.storagehub.Utils;
import org.gcube.data.access.storagehub.exception.MyAuthException;
import org.gcube.data.access.storagehub.handlers.CredentialHandler;
import org.gcube.data.access.storagehub.handlers.GroupHandler;
import org.gcube.data.access.storagehub.handlers.TrashHandler;
import org.gcube.data.access.storagehub.handlers.UnshareHandler;
import org.gcube.data.access.storagehub.handlers.items.builders.FolderCreationParameters;
import org.gcube.smartgears.annotations.ManagedBy;
import org.gcube.smartgears.utils.InnerMethodName;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.webcohesion.enunciate.metadata.rs.RequestHeader;
import com.webcohesion.enunciate.metadata.rs.RequestHeaders;
@Path("users")
@ManagedBy(StorageHubAppllicationManager.class)
@RequestHeaders({
@RequestHeader( name = "Authorization", description = "Bearer token, see https://dev.d4science.org/how-to-access-resources"),
})
public class UserManager {
private static final String INFRASTRUCTURE_MANAGER_ROLE = "Infrastructure-Manager";
@Context ServletContext context;
private static final Logger log = LoggerFactory.getLogger(UserManager.class);
@Inject
RepositoryInitializer repository;
RepositoryInitializer repository = StorageHubAppllicationManager.repository;
@Inject
UnshareHandler unshareHandler;
@Inject
AuthorizationChecker authChecker;
@Inject
TrashHandler trashHandler;
@Inject
GroupHandler groupHandler;
@Inject
PathUtil pathUtil;
@GET
@Path("")
@Produces(MediaType.APPLICATION_JSON)
@AuthorizationControl(allowed={"lucio.lelii"}, exception=MyAuthException.class)
public List<String> getUsers(){
InnerMethodName.instance.set("getUsers");
JackrabbitSession session = null;
List<String> users= new ArrayList<>();
List<String> users = null;
try {
session = (JackrabbitSession) repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
Iterator<Authorizable> result = session.getUserManager().findAuthorizables(new Query() {
@Override
@ -70,12 +111,20 @@ public class UserManager {
builder.setSelector(User.class);
}
});
Set<String> usersSet= new HashSet<>();
String adminUser = context.getInitParameter(Constants.ADMIN_PARAM_NAME);
while (result.hasNext()) {
Authorizable user = result.next();
log.debug("user {} found",user.getPrincipal().getName());
users.add(user.getPrincipal().getName());
if (user.getPrincipal().getName().equals(adminUser)) continue;
usersSet.add(user.getPrincipal().getName());
}
users = new ArrayList<>(usersSet);
Collections.sort(users);
}catch(Exception e) {
log.error("jcr error getting users", e);
GXOutboundErrorResponse.throwException(new BackendGenericError(e));
@ -86,36 +135,87 @@ public class UserManager {
return users;
}
@GET
@Path("{user}")
public String getUser(@PathParam("user") String user){
InnerMethodName.instance.set("getUser");
JackrabbitSession session = null;
try {
session = (JackrabbitSession) repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
org.apache.jackrabbit.api.security.user.UserManager usrManager = session.getUserManager();
Authorizable authorizable = usrManager.getAuthorizable(user);
if (authorizable != null && !authorizable.isGroup())
return authorizable.getPrincipal().getName();
log.debug("user {} not found", user);
}catch(Exception e) {
log.error("jcr error getting user", e);
GXOutboundErrorResponse.throwException(new BackendGenericError(e));
} finally {
if (session!=null)
session.logout();
}
GXOutboundErrorResponse.throwException(new IdNotFoundException(user));
return null;
}
@POST
@Path("")
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@AuthorizationControl(allowed={"lucio.lelii"}, exception=MyAuthException.class)
@AuthorizationControl(allowedRoles={INFRASTRUCTURE_MANAGER_ROLE}, exception=MyAuthException.class)
public String createUser(@FormParam("user") String user, @FormParam("password") String password){
InnerMethodName.instance.set("createUser");
JackrabbitSession session = null;
String userId = null;
try {
session = (JackrabbitSession) repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
org.apache.jackrabbit.api.security.user.UserManager usrManager = session.getUserManager();
User createdUser = usrManager.createUser(user, password);
userId = createdUser.getID();
Node homeNode = session.getNode("/Home");
Node userHome = homeNode.addNode(user, "nthl:home");
userHome.setProperty(Constants.HOME_VERSION_PROP, 1l);
//creating workspace folder
Node workspaceFolder = Utils.createFolderInternally(session, userHome, Constants.WORKSPACE_ROOT_FOLDER_NAME, "workspace of "+user, false, user, null);
FolderCreationParameters wsFolderParameters = FolderCreationParameters.builder().name(Constants.WORKSPACE_ROOT_FOLDER_NAME).description("workspace of "+user).author(user).on(userHome.getIdentifier()).with(session).build();
Utils.createFolderInternally(wsFolderParameters, null);
//creating thrash folder
Utils.createFolderInternally(session, workspaceFolder, Constants.TRASH_ROOT_FOLDER_NAME, "trash of "+user, false, user, null);
FolderCreationParameters trashFolderParameters = FolderCreationParameters.builder().name(Constants.TRASH_ROOT_FOLDER_NAME).description("trash of "+user).author(user).on(userHome.getIdentifier()).with(session).build();
Utils.createFolderInternally(trashFolderParameters, null);
//creating Vre container folder
Utils.createFolderInternally(session, workspaceFolder, Constants.VRE_FOLDER_PARENT_NAME, "special folder container of "+user, false, user, null);
FolderCreationParameters vreFolderParameters = FolderCreationParameters.builder().name(Constants.PERSONAL_VRES_FOLDER_PARENT_NAME).description("vre folder container of "+user).author(user).on(userHome.getIdentifier()).with(session).build();
Utils.createFolderInternally(vreFolderParameters, null);
//creating inbox folder
FolderCreationParameters inboxFolderParameters = FolderCreationParameters.builder().name(Constants.INBOX_FOLDER_NAME).description("inbox of "+user).author(user).on(userHome.getIdentifier()).with(session).build();
Utils.createFolderInternally(inboxFolderParameters, null);
//creating outbox folder
FolderCreationParameters outboxFolderParameters = FolderCreationParameters.builder().name(Constants.OUTBOX_FOLDER_NAME).description("outbox of "+user).author(user).on(userHome.getIdentifier()).with(session).build();
Utils.createFolderInternally(outboxFolderParameters, null);
session.save();
}catch(Exception e) {
log.error("jcr error creating user {}", user, e);
GXOutboundErrorResponse.throwException(new BackendGenericError(e));
}catch(StorageHubException she ){
log.error(she.getErrorMessage(), she);
GXOutboundErrorResponse.throwException(she, Response.Status.fromStatusCode(she.getStatus()));
}catch(RepositoryException re ){
log.error("jcr error creating item", re);
GXOutboundErrorResponse.throwException(new BackendGenericError("jcr error creating item", re));
} finally {
if (session!=null)
session.logout();
@ -126,51 +226,144 @@ public class UserManager {
@DELETE
@Path("{id}")
@AuthorizationControl(allowed={"lucio.lelii"}, exception=MyAuthException.class)
public String deleteUser(@PathParam("id") String id){
@Path("{user}")
@AuthorizationControl(allowedRoles={INFRASTRUCTURE_MANAGER_ROLE}, exception=MyAuthException.class)
public String deleteUser(@PathParam("user") final String user){
InnerMethodName.instance.set("deleteUser");
JackrabbitSession session = null;
String userId = null;
try {
session = (JackrabbitSession) repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
org.apache.jackrabbit.api.security.user.UserManager usrManager = session.getUserManager();
org.gcube.common.storagehub.model.Path path = Utils.getWorkspacePath(id);
String sql2Query = String.format("SELECT * FROM [nthl:workspaceSharedItem] AS node WHERE ISDESCENDANTNODE('%s')", path.toPath());
log.info("query sent is {}",sql2Query);
javax.jcr.query.Query jcrQuery = session.getWorkspace().getQueryManager().createQuery(sql2Query, Constants.QUERY_LANGUAGE);
QueryResult result = jcrQuery.execute();
NodeIterator nodeIt = result.getNodes();
while (nodeIt.hasNext()) {
Node rNode = nodeIt.nextNode();
String title = rNode.hasProperty(NodeProperty.TITLE.toString()) ? rNode.getProperty(NodeProperty.TITLE.toString()).getString():"unknown";
log.debug("removing sharing for folder name {} with title {} and path {} ",rNode.getName(), title, rNode.getPath());
unshareHandler.unshare(session, Collections.singleton(id), rNode, id);
}
Authorizable authorizable = usrManager.getAuthorizable(new PrincipalImpl(id));
if (!authorizable.isGroup()) {
log.info("removing user {}", id);
User authorizable = (User) usrManager.getAuthorizable(new PrincipalImpl(user));
if (authorizable!=null)
removeUserFromBelongingGroup(session, authorizable, usrManager);
else log.warn("user was already deleted from jackrabbit, trying to delete folders");
unshareUsersFolders(session, user);
removeUserHomeAndDeleteFiles(session, user);
//FINALIZE user removal
if (authorizable!=null && !authorizable.isGroup()) {
log.info("removing user {}", user);
authorizable.remove();
}
} else log.warn("the user {} was already deleted, it should never happen", user);
session.save();
}catch(Exception e) {
log.error("jcr error getting users", e);
GXOutboundErrorResponse.throwException(new BackendGenericError(e));
}catch(StorageHubException she ){
log.error(she.getErrorMessage(), she);
GXOutboundErrorResponse.throwException(she, Response.Status.fromStatusCode(she.getStatus()));
}catch(RepositoryException re ){
log.error("jcr error creating item", re);
GXOutboundErrorResponse.throwException(new BackendGenericError("jcr error creating item", re));
} finally {
if (session!=null)
session.logout();
}
return userId;
return user;
}
private void removeUserFromBelongingGroup(JackrabbitSession session, User authorizable, org.apache.jackrabbit.api.security.user.UserManager usrManager) throws RepositoryException, StorageHubException {
Iterator<Authorizable> groups = session.getUserManager().findAuthorizables(new Query() {
@Override
public <T> void build(QueryBuilder<T> builder) {
builder.setSelector(Group.class);
}
});
String user = authorizable.getPrincipal().getName();
while(groups.hasNext()) {
Authorizable group = groups.next();
log.info("group found {}", group.getPrincipal().getName() );
if (group.isGroup() && ((Group)group).isMember(authorizable)) {
boolean success = groupHandler.removeUserFromGroup(group.getPrincipal().getName(), user, session);
log.warn("user {} {} removed from vre {}",user,success?"":"not" ,group.getPrincipal().getName());
}
}
}
private void unshareUsersFolders(JackrabbitSession session, String user){
try {
Node sharedFolderNode = session.getNode(Constants.SHARED_FOLDER_PATH);
Predicate<Node> sharedWithUserChecker = new Predicate<Node>() {
@Override
public boolean test(Node t) {
try {
authChecker.checkReadAuthorizationControl(t.getSession(), user, t.getIdentifier());
return true;
} catch (UserNotAuthorizedException | BackendGenericError | RepositoryException e) {
return false;
}
}
};
List<SharedFolder> items = Utils.getItemList(sharedWithUserChecker, sharedFolderNode, Excludes.ALL, null, false, SharedFolder.class);
log.debug(" Shared folder to unshare found are {}", items.size());
for (SharedFolder item: items) {
String title = item.getTitle();
log.debug("in list folder name {} with title {} and path {} ",item.getName(), title, item.getPath());
if (item.isPublicItem() && !item.getUsers().getMap().containsKey(user)) continue;
if (item.isVreFolder()) continue;
log.info("removing sharing for folder name {} with title {} and path {} ",item.getName(), title, item.getPath());
String owner = item.getOwner();
Set<String> usersToUnshare= owner.equals(user)? Collections.emptySet():Collections.singleton(user);
try {
unshareHandler.unshareForRemoval(session, usersToUnshare, session.getNodeByIdentifier(item.getId()), user);
}catch (Throwable e) {
log.warn("error unsharing folder with title '{}' and id {} ", title, item.getId(), e);
}
}
} catch (Throwable t) {
log.warn("error getting folder shared with {}",user, t);
}
}
private void removeUserHomeAndDeleteFiles(JackrabbitSession session, String user) throws RepositoryException, StorageHubException {
org.gcube.common.storagehub.model.Path homePath = pathUtil.getHome(user);
org.gcube.common.storagehub.model.Path workspacePath = pathUtil.getWorkspacePath(user);
org.gcube.common.storagehub.model.Path trashPath = pathUtil.getTrashPath(user, session);
try {
Node workspaceNode = session.getNode(workspacePath.toPath());
List<Item> workspaceItems = Utils.getItemList(workspaceNode, Excludes.GET_ONLY_CONTENT, null, true, null).stream().filter(i -> !i.isShared()).collect(Collectors.toList());
trashHandler.removeOnlyNodesContent(session, workspaceItems);
} catch (PathNotFoundException e) {
log.warn("{} workspace dir {} was already deleted", user, homePath.toPath());
}
try {
Node trashNode = session.getNode(trashPath.toPath());
List<Item> trashItems = Utils.getItemList(trashNode, Excludes.ALL, null, true, null);
trashHandler.removeOnlyNodesContent(session, trashItems);
} catch (PathNotFoundException e) {
log.warn("{} trash dir {} was already deleted", user, homePath.toPath());
}
try {
Node homeNode = session.getNode(homePath.toPath());
homeNode.remove();
} catch (PathNotFoundException e) {
log.warn("{} home dir {} was already deleted", user, homePath.toPath());
}
}
}

@ -1,86 +1,90 @@
package org.gcube.data.access.storagehub.services;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.query.Query;
import javax.jcr.query.QueryResult;
import javax.servlet.ServletContext;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.gcube.common.authorization.library.provider.AuthorizationProvider;
import org.apache.jackrabbit.api.JackrabbitSession;
import org.gcube.common.gxrest.response.outbound.GXOutboundErrorResponse;
import org.gcube.common.scope.api.ScopeProvider;
import org.gcube.common.scope.impl.ScopeBean;
import org.gcube.common.scope.impl.ScopeBean.Type;
import org.gcube.common.storagehub.model.Excludes;
import org.gcube.common.storagehub.model.Paths;
import org.gcube.common.storagehub.model.exceptions.BackendGenericError;
import org.gcube.common.storagehub.model.exceptions.InvalidCallParameters;
import org.gcube.common.storagehub.model.exceptions.InvalidItemException;
import org.gcube.common.storagehub.model.exceptions.StorageHubException;
import org.gcube.common.storagehub.model.expressions.Expression;
import org.gcube.common.storagehub.model.expressions.logical.And;
import org.gcube.common.storagehub.model.expressions.logical.ISDescendant;
import org.gcube.common.storagehub.model.exceptions.UserNotAuthorizedException;
import org.gcube.common.storagehub.model.items.FolderItem;
import org.gcube.common.storagehub.model.items.Item;
import org.gcube.common.storagehub.model.items.TrashItem;
import org.gcube.common.storagehub.model.service.ItemList;
import org.gcube.common.storagehub.model.service.ItemWrapper;
import org.gcube.data.access.storagehub.AuthorizationChecker;
import org.gcube.data.access.storagehub.Constants;
import org.gcube.data.access.storagehub.PathUtil;
import org.gcube.data.access.storagehub.Range;
import org.gcube.data.access.storagehub.StorageHubAppllicationManager;
import org.gcube.data.access.storagehub.Utils;
import org.gcube.data.access.storagehub.handlers.CredentialHandler;
import org.gcube.data.access.storagehub.handlers.Item2NodeConverter;
import org.gcube.data.access.storagehub.handlers.Node2ItemConverter;
import org.gcube.data.access.storagehub.handlers.StorageBackendHandler;
import org.gcube.data.access.storagehub.handlers.TrashHandler;
import org.gcube.data.access.storagehub.handlers.VRE;
import org.gcube.data.access.storagehub.handlers.VREManager;
import org.gcube.data.access.storagehub.handlers.items.Item2NodeConverter;
import org.gcube.data.access.storagehub.handlers.items.Node2ItemConverter;
import org.gcube.data.access.storagehub.handlers.items.builders.FolderCreationParameters;
import org.gcube.data.access.storagehub.handlers.plugins.FolderPluginHandler;
import org.gcube.data.access.storagehub.handlers.vres.VRE;
import org.gcube.data.access.storagehub.handlers.vres.VREManager;
import org.gcube.data.access.storagehub.query.sql2.evaluators.Evaluators;
import org.gcube.smartgears.annotations.ManagedBy;
import org.gcube.smartgears.utils.InnerMethodName;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.webcohesion.enunciate.metadata.rs.RequestHeader;
import com.webcohesion.enunciate.metadata.rs.RequestHeaders;
@Path("")
public class WorkspaceManager {
private static final Logger log = LoggerFactory.getLogger(WorkspaceManager.class);
@Path("/")
@ManagedBy(StorageHubAppllicationManager.class)
@RequestHeaders({
@RequestHeader( name = "Authorization", description = "Bearer token, see https://dev.d4science.org/how-to-access-resources"),
})
public class WorkspaceManager extends Impersonable{
@Inject
RepositoryInitializer repository;
private static final Logger log = LoggerFactory.getLogger(WorkspaceManager.class);
RepositoryInitializer repository = StorageHubAppllicationManager.repository;
@Inject
Evaluators evaluator;
@Inject
AuthorizationChecker authChecker;
PathUtil pathUtil;
@Inject
ServletContext context;
VREManager vreManager;
@Context
ServletContext context;
@Inject
VREManager vreManager;
AuthorizationChecker authChecker;
@Inject
TrashHandler trashHandler;
@ -92,9 +96,11 @@ public class WorkspaceManager {
@Inject Node2ItemConverter node2Item;
@Inject Item2NodeConverter item2Node;
@Inject StorageBackendHandler storageBackend;
@Inject
FolderPluginHandler folderHandler;
@Path("")
@Path("/")
@GET
@Produces(MediaType.APPLICATION_JSON)
public ItemWrapper<Item> getWorkspace(@QueryParam("relPath") String relPath){
@ -102,25 +108,24 @@ public class WorkspaceManager {
Session ses = null;
org.gcube.common.storagehub.model.Path absolutePath;
if (relPath==null)
absolutePath = Utils.getWorkspacePath();
else absolutePath = Paths.append(Utils.getWorkspacePath(), relPath);
absolutePath = pathUtil.getWorkspacePath(currentUser);
else absolutePath = Paths.append(pathUtil.getWorkspacePath(currentUser), relPath);
Item toReturn = null;
try{
long start = System.currentTimeMillis();
ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
//TODO: remove it when users will be created via storageHub
String user = AuthorizationProvider.instance.get().getClient().getId();
org.gcube.common.storagehub.model.Path trashPath = Paths.append(Utils.getWorkspacePath(), Constants.TRASH_ROOT_FOLDER_NAME);
//TODO: remove when all user will have TRASH
org.gcube.common.storagehub.model.Path trashPath = pathUtil.getTrashPath(currentUser, ses);
if (!ses.nodeExists(trashPath.toPath())) {
Utils.createFolderInternally(ses, ses.getNode(Utils.getWorkspacePath().toPath()) , Constants.TRASH_ROOT_FOLDER_NAME, "trash of "+user, false, user, null);
Node wsNode = ses.getNode(pathUtil.getWorkspacePath(currentUser).toPath());
FolderCreationParameters trashFolderParameters = FolderCreationParameters.builder().name(Constants.TRASH_ROOT_FOLDER_NAME)
.description("trash of "+currentUser)
.author(currentUser).on(wsNode.getIdentifier()).with(ses).build();
Utils.createFolderInternally(trashFolderParameters, null);
ses.save();
}
log.trace("time to connect to repo {}",(System.currentTimeMillis()-start));
Node node = ses.getNode(absolutePath.toPath());
authChecker.checkReadAuthorizationControl(ses, node.getIdentifier());
authChecker.checkReadAuthorizationControl(ses, currentUser, node.getIdentifier());
toReturn = node2Item.getItem(node, excludes);
}catch(RepositoryException re ){
log.error("jcr error getting workspace item", re);
@ -136,38 +141,17 @@ public class WorkspaceManager {
return new ItemWrapper<Item>(toReturn);
}
private synchronized VRE getVreFolderItem(Session ses) throws RepositoryException, BackendGenericError{
org.gcube.common.storagehub.model.Path vrePath = Paths.append(Utils.getWorkspacePath(), Constants.VRE_FOLDER_PARENT_NAME);
ScopeBean bean = new ScopeBean(ScopeProvider.instance.get());
if (!bean.is(Type.VRE)) throw new BackendGenericError("the current scope is not a VRE");
String entireScopeName= bean.toString().replaceAll("^/(.*)/?$", "$1").replaceAll("/", "-");
VRE vre = vreManager.getVRE(entireScopeName);
if (vre!=null) return vre;
else {
String query = String.format("SELECT * FROM [nthl:workspaceItem] As node WHERE node.[jcr:title] like '%s' AND ISDESCENDANTNODE('%s')",entireScopeName, vrePath.toPath());
Query jcrQuery = ses.getWorkspace().getQueryManager().createQuery(query, Constants.QUERY_LANGUAGE);
NodeIterator it = jcrQuery.execute().getNodes();
if (!it.hasNext()) throw new BackendGenericError("vre folder not found for context "+entireScopeName);
Node folder = it.nextNode();
Item vreFolder = node2Item.getItem(folder, excludes);
return vreManager.putVRE(vreFolder);
}
}
@Path("vrefolder")
@GET
@Produces(MediaType.APPLICATION_JSON)
public ItemWrapper<Item> getVreRootFolder(){
InnerMethodName.instance.set("getVreRootFolder");
Session ses = null;
JackrabbitSession ses = null;
Item vreItem = null;
try {
ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
vreItem = getVreFolderItem(ses).getVreFolder();
ses = (JackrabbitSession) repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
vreItem = vreManager.getVreFolderItem(ses, currentUser, excludes).getVreFolder();
}catch(RepositoryException re ){
log.error("jcr error getting vrefolder", re);
GXOutboundErrorResponse.throwException(new BackendGenericError(re));
@ -186,13 +170,12 @@ public class WorkspaceManager {
@Produces(MediaType.APPLICATION_JSON)
public ItemList getVreFolderRecentsDocument(){
InnerMethodName.instance.set("getVreFolderRecents");
Session ses = null;
JackrabbitSession ses = null;
List<Item> recentItems = Collections.emptyList();
try{
String login = AuthorizationProvider.instance.get().getClient().getId();
ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
ses = (JackrabbitSession) repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
VRE vre = getVreFolderItem(ses);
VRE vre = vreManager.getVreFolderItem(ses, currentUser, excludes);
log.trace("VRE retrieved {}",vre.getVreFolder().getTitle());
recentItems = vre.getRecents();
log.trace("recents retrieved {}",vre.getVreFolder().getTitle());
@ -220,12 +203,12 @@ public class WorkspaceManager {
public ItemWrapper<Item> getTrashRootFolder(){
InnerMethodName.instance.set("getTrashRootFolder");
Session ses = null;
String user = AuthorizationProvider.instance.get().getClient().getId();
org.gcube.common.storagehub.model.Path trashPath = Paths.append(Utils.getWorkspacePath(), Constants.TRASH_ROOT_FOLDER_NAME);
Item item = null;
try{
long start = System.currentTimeMillis();
ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
org.gcube.common.storagehub.model.Path trashPath = pathUtil.getTrashPath(currentUser, ses);
log.info("time to connect to repo {}",(System.currentTimeMillis()-start));
Node folder = ses.getNode(trashPath.toPath());
@ -242,18 +225,18 @@ public class WorkspaceManager {
}
return new ItemWrapper<Item>(item);
}
@Path("trash/empty")
@DELETE
public String emptyTrash(){
InnerMethodName.instance.set("emptyTrash");
Session ses = null;
org.gcube.common.storagehub.model.Path trashPath = Paths.append(Utils.getWorkspacePath(), Constants.TRASH_ROOT_FOLDER_NAME);
String toReturn = null;
try{
ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
org.gcube.common.storagehub.model.Path trashPath = pathUtil.getTrashPath(currentUser, ses);
Node trashNode = ses.getNode(trashPath.toPath());
List<Item> itemsToDelete = Utils.getItemList(trashNode, Excludes.ALL, null, true, null);
trashHandler.removeNodes(ses, itemsToDelete);
@ -273,32 +256,39 @@ public class WorkspaceManager {
}
@PUT
@Consumes(MediaType.TEXT_PLAIN)
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Path("trash/restore")
public String restoreItem(String identifier){
public String restoreItem(@FormParam("trashedItemId") String trashedItemId,@FormParam("destinationId") String destinationFolderId){
InnerMethodName.instance.set("restoreItem");
Session ses = null;
String toReturn = null;
try{
log.info("restoring node with id {}", identifier);
//TODO check if it is possible to change all the ACL on a workspace
log.info("restoring node with id {}", trashedItemId);
ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
authChecker.checkWriteAuthorizationControl(ses, identifier, false);
final Node nodeToRestore = ses.getNodeByIdentifier(identifier);
final Node nodeToRestore = ses.getNodeByIdentifier(trashedItemId);
Item itemToRestore = node2Item.getItem(nodeToRestore, Excludes.ALL);
if (!(itemToRestore instanceof TrashItem))
throw new InvalidItemException("Only trash items can be restored");
toReturn = trashHandler.restoreItem(ses, (TrashItem)itemToRestore);
org.gcube.common.storagehub.model.Path trashPath = pathUtil.getTrashPath(currentUser, ses);
if (!itemToRestore.getPath().startsWith(trashPath.toPath()))
throw new UserNotAuthorizedException("this item is not in the user "+currentUser+" trash");
Item destinationItem = null;
if (destinationFolderId!=null ) {
destinationItem = node2Item.getItem(ses.getNodeByIdentifier(destinationFolderId), Excludes.ALL);
if (!(destinationItem instanceof FolderItem))
throw new InvalidCallParameters("destintation item is not a folder");
toReturn = trashHandler.restoreItem(ses, (TrashItem)itemToRestore, (FolderItem) destinationItem, currentUser);
} else
toReturn = trashHandler.restoreItem(ses, (TrashItem)itemToRestore, null, currentUser);
}catch(RepositoryException re ){
log.error("error restoring item with id {}",identifier, re);
log.error("error restoring item with id {}",trashedItemId, re);
GXOutboundErrorResponse.throwException(new BackendGenericError(re));
}catch(StorageHubException she ){
log.error(she.getErrorMessage(), she);
@ -319,12 +309,13 @@ public class WorkspaceManager {
public ItemList getVreFolders(){
InnerMethodName.instance.set("getVreFolders");
Session ses = null;
org.gcube.common.storagehub.model.Path vrePath = Paths.append(Utils.getWorkspacePath(), Constants.VRE_FOLDER_PARENT_NAME);
List<? extends Item> toReturn = null;
org.gcube.common.storagehub.model.Path vrePath = null;
try{
log.info("vres folder path is {}",vrePath.toPath());
ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
vrePath = pathUtil.getVREsPath(currentUser, ses);
log.info("vres folder path is {}",vrePath.toPath());
toReturn = Utils.getItemList(ses.getNode(vrePath.toPath()) , excludes, null, false, null);
}catch(RepositoryException re ){
log.error("error reading the node children of {}",vrePath, re);
@ -346,10 +337,11 @@ public class WorkspaceManager {
public ItemList getVreFoldersPaged(@QueryParam("start") Integer start, @QueryParam("limit") Integer limit){
InnerMethodName.instance.set("getVreFoldersPaged");
Session ses = null;
org.gcube.common.storagehub.model.Path vrePath = Paths.append(Utils.getWorkspacePath(), Constants.VRE_FOLDER_PARENT_NAME);
org.gcube.common.storagehub.model.Path vrePath = null;
List<? extends Item> toReturn = null;
try{
ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
vrePath = pathUtil.getVREsPath(currentUser, ses);
toReturn = Utils.getItemList(ses.getNode(vrePath.toPath()) , excludes, new Range(start, limit), false, null);
}catch(RepositoryException re ){
log.error("(paged) error reading the node children of {}",vrePath, re);
@ -366,65 +358,12 @@ public class WorkspaceManager {
}
@Path("query")
@GET
@Produces(MediaType.APPLICATION_JSON)
public ItemList searchItems(@QueryParam("n") String node, @QueryParam("e") String jsonExpr, @QueryParam("o") List<String> orderField, @QueryParam("l") Integer limit, @QueryParam("f") Integer offset){
InnerMethodName.instance.set("searchItems");
Session ses = null;
List<? extends Item> toReturn = new ArrayList<>();
try{
ObjectMapper mapper = new ObjectMapper();
Expression<Boolean> expression = mapper.readValue(jsonExpr, Expression.class);
String stringExpression = evaluator.evaluate(new And(new ISDescendant(Utils.getWorkspacePath()), expression));
String orderBy = "";
if (orderField!=null && orderField.size()>0)
orderBy= String.format("ORDER BY %s", orderField.stream().collect(Collectors.joining(",")).toString());
String sql2Query = String.format("SELECT * FROM [%s] AS node WHERE %s %s ",node, stringExpression,orderBy);
log.info("query sent is {}",sql2Query);
ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
Query jcrQuery = ses.getWorkspace().getQueryManager().createQuery(sql2Query, Constants.QUERY_LANGUAGE);
if (limit!=null && limit!=-1 )
jcrQuery.setLimit(limit);
if (offset!=null && offset!=-1 )
jcrQuery.setOffset(offset);
QueryResult result = jcrQuery.execute();
NodeIterator it = result.getNodes();
while (it.hasNext())
toReturn.add(node2Item.getItem(it.nextNode(), null));
}catch(RepositoryException | IOException re ){
log.error("error executing the query", re);
GXOutboundErrorResponse.throwException(new BackendGenericError(re));
}catch(StorageHubException she ){
log.error(she.getErrorMessage(), she);
GXOutboundErrorResponse.throwException(she, Response.Status.fromStatusCode(she.getStatus()));
}finally{
if (ses!=null)
ses.logout();
}
return new ItemList(toReturn);
}
@Path("count")
@GET
public String getTotalItemsCount(){
InnerMethodName.instance.set("getTotalItemsCount");
return storageBackend.getTotalItemsCount();
return folderHandler.getDefault().getStorageBackend().getTotalItemsCount();
}
@ -432,8 +371,8 @@ public class WorkspaceManager {
@GET
public String getTotalVolume(){
InnerMethodName.instance.set("getTotalSize");
return storageBackend.getTotalVolume();
return folderHandler.getDefault().getStorageBackend().getTotalSizeStored();
}
}

@ -0,0 +1,200 @@
package org.gcube.data.access.storagehub.services.admin;
import static org.gcube.data.access.storagehub.Roles.INFRASTRUCTURE_MANAGER_ROLE;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.StringReader;
import java.io.StringWriter;
import javax.inject.Inject;
import javax.jcr.Node;
import javax.servlet.ServletContext;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import org.apache.cxf.io.ReaderInputStream;
import org.apache.jackrabbit.api.JackrabbitSession;
import org.gcube.common.authorization.control.annotations.AuthorizationControl;
import org.gcube.common.authorization.library.AuthorizedTasks;
import org.gcube.common.authorization.library.provider.AuthorizationProvider;
import org.gcube.common.storagehub.model.Paths;
import org.gcube.data.access.storagehub.PathUtil;
import org.gcube.data.access.storagehub.StorageHubAppllicationManager;
import org.gcube.data.access.storagehub.accounting.AccountingHandler;
import org.gcube.data.access.storagehub.exception.MyAuthException;
import org.gcube.data.access.storagehub.handlers.CredentialHandler;
import org.gcube.data.access.storagehub.handlers.items.ItemHandler;
import org.gcube.data.access.storagehub.handlers.items.builders.FileCreationParameters;
import org.gcube.data.access.storagehub.handlers.items.builders.ItemsParameterBuilder;
import org.gcube.data.access.storagehub.scripting.AbstractScript;
import org.gcube.data.access.storagehub.scripting.ScriptUtil;
import org.gcube.data.access.storagehub.services.RepositoryInitializer;
import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
import org.glassfish.jersey.media.multipart.FormDataParam;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.webcohesion.enunciate.metadata.rs.RequestHeader;
import com.webcohesion.enunciate.metadata.rs.RequestHeaders;
@Path("admin/script")
@RequestHeaders({
@RequestHeader( name = "Authorization", description = "Bearer token, see https://dev.d4science.org/how-to-access-resources"),
})
public class ScriptManager {
private static Logger log = LoggerFactory.getLogger(ScriptManager.class);
private RepositoryInitializer repository = StorageHubAppllicationManager.repository;
@Inject
AccountingHandler accountingHandler;
@Context
ServletContext context;
@Inject
ScriptUtil scriptUtil;
@Inject
ItemHandler itemHandler;
@Inject
PathUtil pathUtil;
@POST
@Path("execute")
@AuthorizationControl(allowedRoles = {INFRASTRUCTURE_MANAGER_ROLE},exception=MyAuthException.class)
@Consumes(MediaType.MULTIPART_FORM_DATA)
public String run( @FormDataParam("name") String name,
@FormDataParam("asynch") Boolean asynch,
@FormDataParam("destinationFolderId") String destinationFolderId,
@FormDataParam("file") InputStream stream,
@FormDataParam("file") FormDataContentDisposition fileDetail) {
try {
ScriptClassLoader scriptClassLoader = new ScriptClassLoader(Thread.currentThread().getContextClassLoader());
Class<?> scriptClass = uploadClass(stream, scriptClassLoader, fileDetail.getFileName().replace(".class", ""));
return run(scriptClass, name, destinationFolderId, asynch!=null? asynch : false);
}catch(Throwable e) {
log.error("error executing script {}", name,e);
throw new WebApplicationException("error loading class",e);
}
}
private Class<?> uploadClass(InputStream stream, ScriptClassLoader classLoader, String name) throws Throwable {
try(ByteArrayOutputStream buffer = new ByteArrayOutputStream()){
int nRead;
byte[] data = new byte[1024];
while ((nRead = stream.read(data, 0, data.length)) != -1)
buffer.write(data, 0, nRead);
buffer.flush();
byte[] byteArray = buffer.toByteArray();
return classLoader.findClass(name, byteArray);
}
}
private String run(Class<?> clazz, String name, String destinationFolderId, boolean asynch) throws Throwable {
String login = AuthorizationProvider.instance.get().getClient().getId();
log.info("script {} called by {}", clazz.getSimpleName(), login);
JackrabbitSession ses = null;
try {
ses = (JackrabbitSession) repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
String parentId = destinationFolderId!=null ? destinationFolderId : ses.getNode(pathUtil.getWorkspacePath(login).toPath()).getIdentifier();
Node parentNode = ses.getNodeByIdentifier(parentId);
String parentPath = parentNode.getPath();
if (AbstractScript.class.isAssignableFrom(clazz)) {
AbstractScript scriptInstance = (AbstractScript) clazz.newInstance();
RealRun realRun = new RealRun(ses, scriptInstance, login, parentId, name);
if (asynch) {
new Thread(AuthorizedTasks.bind(realRun)).start();
}else realRun.run();
} else throw new Exception("class "+clazz.getSimpleName()+" not implements AbstractScript");
return Paths.append(Paths.getPath(parentPath), name).toPath();
}catch (Throwable e) {
if (ses !=null && ses.isLive())
ses.logout();
throw e;
}
}
class RealRun implements Runnable{
private JackrabbitSession ses;
AbstractScript instance;
String login;
String parentId;
String name;
public RealRun(JackrabbitSession ses, AbstractScript instance, String login, String parentId, String name) {
super();
this.ses = ses;
this.instance = instance;
this.login = login;
this.parentId = parentId;
this.name = name;
}
@Override
public void run() {
try{
String result ="";
try {
result = instance.run(ses, null, scriptUtil);
log.info("result is {}",result);
}catch(Throwable t) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw, true);
t.printStackTrace(pw);
result+= "\n"+sw.toString();
}
try( InputStream stream = new ReaderInputStream(new StringReader(result))){
ItemsParameterBuilder<FileCreationParameters> builder = FileCreationParameters.builder().name(name).description("result of script execution "+name)
.stream(stream).on(parentId).with(ses).author(login);
itemHandler.create(builder.build());
} catch (Throwable e) {
log.error("error saving script result {} in the Workspace",name, e);
}
} finally {
if (ses!=null)
ses.logout();
}
}
}
class ScriptClassLoader extends ClassLoader{
public ScriptClassLoader(ClassLoader parent) {
super(parent);
}
public Class<?> findClass(String name, byte[] b) {
return defineClass(name, b, 0, b.length);
}
}
}

@ -0,0 +1,32 @@
package org.gcube.data.access.storagehub.services.admin;
import java.util.List;
import java.util.function.Predicate;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.jcr.Node;
import javax.jcr.RepositoryException;
import org.gcube.common.storagehub.model.exceptions.BackendGenericError;
import org.gcube.common.storagehub.model.items.Item;
import org.gcube.data.access.storagehub.Utils;
import org.gcube.data.access.storagehub.handlers.items.Node2ItemConverter;
import org.gcube.data.access.storagehub.scripting.ScriptUtil;
@Singleton
public class ScriptUtilImpl implements ScriptUtil {
@Inject Node2ItemConverter node2Item;
@Override
public List<Item> getChildren(Predicate<Node> checker, Node parent, List<String> excludes, boolean showHidden, Class<? extends Item> nodeTypeToInclude) throws RepositoryException, BackendGenericError {
return Utils.getItemList(checker, parent, excludes, null, showHidden, nodeTypeToInclude);
}
@Override
public Item getItem(Node node, List<String> excludes) throws RepositoryException, BackendGenericError {
return node2Item.getItem(node, excludes);
}
}

@ -0,0 +1,107 @@
package org.gcube.data.access.storagehub.services.delegates;
import java.security.Principal;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.jcr.Node;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.security.AccessControlEntry;
import javax.jcr.security.AccessControlManager;
import javax.jcr.security.Privilege;
import org.apache.jackrabbit.api.security.JackrabbitAccessControlList;
import org.apache.jackrabbit.commons.jackrabbit.authorization.AccessControlUtils;
import org.gcube.common.storagehub.model.acls.ACL;
import org.gcube.common.storagehub.model.acls.AccessType;
import org.gcube.common.storagehub.model.exceptions.BackendGenericError;
import org.gcube.common.storagehub.model.exceptions.StorageHubException;
import org.gcube.common.storagehub.model.items.Item;
import org.gcube.common.storagehub.model.items.SharedFolder;
import org.gcube.data.access.storagehub.handlers.items.Node2ItemConverter;
import org.gcube.data.access.storagehub.services.interfaces.ACLManagerInterface;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Singleton
public class ACLManagerDelegate implements ACLManagerInterface {
@Inject
Node2ItemConverter node2Item;
private static final Logger log = LoggerFactory.getLogger(ACLManagerDelegate.class);
@Override
public List<ACL> get(Item item, Session session) throws RepositoryException, BackendGenericError {
List<ACL> acls = new ArrayList<>();
if (!item.isShared()) return acls;
Node toRetrieve = (Node) item.getRelatedNode();
if (!(item instanceof SharedFolder))
toRetrieve = retrieveSharedFolderParent(toRetrieve, session);
JackrabbitAccessControlList accessControlList = AccessControlUtils.getAccessControlList(session, toRetrieve.getPath());
for (AccessControlEntry aclEntry : accessControlList.getAccessControlEntries()) {
ACL acl = new ACL();
acl.setPricipal(aclEntry.getPrincipal().getName());
List<AccessType> types = new ArrayList<>();
for (Privilege priv : aclEntry.getPrivileges())
try {
types.add(AccessType.fromValue(priv.getName()));
}catch (Exception e) {
log.warn(priv.getName()+" cannot be mapped to AccessTypes",e);
}
acl.setAccessTypes(types);
acls.add(acl);
}
return acls;
}
private Node retrieveSharedFolderParent(Node node, Session session) throws BackendGenericError, RepositoryException{
if (node2Item.checkNodeType(node, SharedFolder.class)) return node;
else
return retrieveSharedFolderParent(node.getParent(), session);
}
@Override
public void update(String targetUser, SharedFolder folder, AccessType accessType, Session session) throws RepositoryException, StorageHubException {
AccessControlManager acm = session.getAccessControlManager();
JackrabbitAccessControlList acls = AccessControlUtils.getAccessControlList(acm, folder.getPath());
Principal principal = AccessControlUtils.getPrincipal(session, targetUser);
_remove(acls, principal);
Privilege[] userPrivileges = new Privilege[] { acm.privilegeFromName(accessType.getValue()) };
acls.addAccessControlEntry(principal, userPrivileges);
acm.setPolicy(folder.getPath(), acls);
session.save();
}
@Override
public void delete(String targetUser, SharedFolder folder, Session session) throws RepositoryException {
AccessControlManager acm = session.getAccessControlManager();
JackrabbitAccessControlList acls = AccessControlUtils.getAccessControlList(acm, folder.getPath());
Principal principal = AccessControlUtils.getPrincipal(session, targetUser);
_remove(acls, principal);
acm.setPolicy(folder.getPath(), acls);
}
private void _remove(JackrabbitAccessControlList acls, Principal principal) throws RepositoryException {
AccessControlEntry aceToDelete = null;
for (AccessControlEntry ace : acls.getAccessControlEntries())
if (ace.getPrincipal().equals(principal)) {
aceToDelete = ace;
break;
}
if (aceToDelete!= null)
acls.removeAccessControlEntry(aceToDelete);
}
}

@ -0,0 +1,42 @@
package org.gcube.data.access.storagehub.services.interfaces;
import java.util.List;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import org.gcube.common.storagehub.model.acls.ACL;
import org.gcube.common.storagehub.model.acls.AccessType;
import org.gcube.common.storagehub.model.exceptions.BackendGenericError;
import org.gcube.common.storagehub.model.exceptions.StorageHubException;
import org.gcube.common.storagehub.model.items.Item;
import org.gcube.common.storagehub.model.items.SharedFolder;
public interface ACLManagerInterface {
/**
* returns the AccessType for all the users in a shared folder
*
* @exception {@link RepositoryException} when a generic jcr error occurs
* @exception {@link UserNotAuthorizedException} when the caller is not authorized to access to the shared folder
*/
List<ACL> get(Item item, Session session) throws RepositoryException, BackendGenericError;
/**
* Set a new AccessType for a user in a shared folder or VRE folder
*
*
* @param String user
* @param accessType accessType
*
* @exception {@link RepositoryException} when a generic jcr error occurs
* @exception {@link UserNotAuthorizedException} when the caller is not ADMINISTRATOR of the shared folder
* @exception {@link InvalidCallParameters} when the folder is not shared with the specified user
* @exception {@link InvalidItemException} when the folder is not share
*/
void update(String targetUser, SharedFolder folder, AccessType accessType, Session session) throws RepositoryException, StorageHubException;
void delete(String targetUser, SharedFolder folder, Session session)
throws RepositoryException, StorageHubException;
}

@ -0,0 +1,22 @@
package org.gcube.data.access.storagehub.storage.backend.impl;
import java.util.Map;
import javax.inject.Singleton;
import org.gcube.common.storagehub.model.exceptions.PluginInitializationException;
import org.gcube.common.storagehub.model.items.FolderItem;
import org.gcube.common.storagehub.model.plugins.FolderManager;
import org.gcube.common.storagehub.model.plugins.FolderManagerConnector;
@Singleton
public class GCubeFolderManagerConnector implements FolderManagerConnector {
@Override
public FolderManager connect(FolderItem item, Map<String, Object> parameters) throws PluginInitializationException {
return new GcubeFolderManager();
}
}

@ -1,10 +1,11 @@
package org.gcube.data.access.storagehub.storage.backend.impl;
import java.io.InputStream;
import javax.inject.Singleton;
import java.util.UUID;
import org.gcube.common.authorization.library.provider.AuthorizationProvider;
import org.gcube.common.storagehub.model.items.AbstractFileItem;
import org.gcube.common.storagehub.model.items.nodes.Content;
import org.gcube.common.storagehub.model.storages.MetaInfo;
import org.gcube.common.storagehub.model.storages.StorageBackend;
import org.gcube.contentmanagement.blobstorage.service.IClient;
@ -14,7 +15,6 @@ import org.gcube.contentmanager.storageclient.wrapper.StorageClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Singleton
public class GCubeStorageBackend implements StorageBackend {
private static final Logger log = LoggerFactory.getLogger(GCubeStorageBackend.class);
@ -22,9 +22,13 @@ public class GCubeStorageBackend implements StorageBackend {
private final static String SERVICE_NAME = "home-library";
private final static String SERVICE_CLASS = "org.gcube.portlets.user";
protected GCubeStorageBackend() {}
@Override
public InputStream getContent(String id) {
return getStorageClient(AuthorizationProvider.instance.get().getClient().getId()).getClient().get().RFileAsInputStream(id);
public InputStream download(Content content) {
return getStorageClient(AuthorizationProvider.instance.get().getClient().getId()).getClient().get().RFileAsInputStream(content.getStorageId());
}
@Override
@ -33,35 +37,38 @@ public class GCubeStorageBackend implements StorageBackend {
}
@Override
public String copy(String idToCopy, String path) {
String newStorageID = getStorageClient(AuthorizationProvider.instance.get().getClient().getId()).getClient().copyFile(true).from(idToCopy).to(path);
log.info("copying storage Id {} to newPath {} and the id returned by storage is {}", idToCopy, path, newStorageID);
public String onCopy(AbstractFileItem item) {
log.info("copying storage Id {} to newPath {}", item.getContent().getStorageId(), item.getPath());
String newStorageID = getStorageClient(AuthorizationProvider.instance.get().getClient().getId()).getClient().copyFile(true).from(item.getContent().getStorageId()).to(item.getPath());
log.info("The id returned by storage is {}", newStorageID);
return newStorageID;
}
@Override
public String move(String idToMove) {
return idToMove;
public String onMove(AbstractFileItem item) {
return item.getContent().getStorageId();
}
@Override
public MetaInfo upload(InputStream stream, String itemPath) {
public MetaInfo upload(InputStream stream, String relPath, String name) {
log.debug("uploading file");
IClient storageClient = getStorageClient(AuthorizationProvider.instance.get().getClient().getId()).getClient();
String storageId =storageClient.put(true).LFile(stream).RFile(itemPath);
String uid = UUID.randomUUID().toString();
String remotePath= String.format("%s/%s-%s",relPath,uid,name);
String storageId =storageClient.put(true).LFile(stream).RFile(remotePath);
long size = storageClient.getSize().RFileById(storageId);
MetaInfo info = new MetaInfo();
info.setSize(size);
info.setStorageId(storageId);
info.setRemotePath(itemPath);
info.setRemotePath(remotePath);
return info;
}
@Override
public void delete(String id) {
public void onDelete(Content content) {
log.debug("deleting");
IClient storageClient = getStorageClient(AuthorizationProvider.instance.get().getClient().getId()).getClient();
storageClient.remove().RFileById(id);
storageClient.remove().RFileById(content.getStorageId());
}
private static StorageClient getStorageClient(String login){
@ -81,8 +88,5 @@ public class GCubeStorageBackend implements StorageBackend {
return storageClient.getUserTotalItems();
}
}

@ -0,0 +1,48 @@
package org.gcube.data.access.storagehub.storage.backend.impl;
import javax.inject.Singleton;
import org.gcube.common.storagehub.model.items.FolderItem;
import org.gcube.common.storagehub.model.plugins.FolderManager;
import org.gcube.common.storagehub.model.storages.StorageBackend;
@Singleton
public class GcubeFolderManager implements FolderManager {
@Override
public StorageBackend getStorageBackend() {
return new GCubeStorageBackend();
}
@Override
public boolean manageVersion() {
return true;
}
@Override
public void onCreatedFolder(FolderItem folder) {
}
@Override
public void onDeletingFolder(FolderItem folder) {
}
@Override
public void onMovedFolder(FolderItem movedFolder) {
// TODO Auto-generated method stub
}
@Override
public void onCopiedFolder(FolderItem copiedFolder) {
// TODO Auto-generated method stub
}
@Override
public FolderItem getRootFolder() {
return null;
}
}

@ -0,0 +1,47 @@
package org.gcube.data.access.storagehub.types;
import org.gcube.common.storagehub.model.items.nodes.Content;
import org.gcube.common.storagehub.model.storages.StorageBackend;
public class ContentPair {
private Content content;
private StorageBackend storageBackend;
public ContentPair(Content content, StorageBackend storageBackend) {
super();
this.content = content;
this.storageBackend = storageBackend;
}
public Content getContent() {
return content;
}
public StorageBackend getStorageBackend() {
return storageBackend;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((content.getStorageId() == null) ? 0 : content.getStorageId().hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ContentPair other = (ContentPair) obj;
if (content.getStorageId() == null) {
if (other.content.getStorageId() != null)
return false;
} else if (!content.getStorageId().equals(other.content.getStorageId()))
return false;
return true;
}
}

@ -0,0 +1,16 @@
package org.gcube.data.access.storagehub.types;
import org.gcube.common.storagehub.model.annotations.RootNode;
import org.gcube.common.storagehub.model.messages.Message;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@NoArgsConstructor
@RootNode("nthl:itemSentRequestSH")
public class MessageSharable extends Message {
}

@ -1,3 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
bean-discovery-mode="all">
</beans>

File diff suppressed because it is too large Load Diff

@ -1,313 +1,297 @@
European Union Public Licence V. 1.1
EUPL © the European Community 2007
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).
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:
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
Licensed under the EUPL V.1.1
or has expressed by any other mean his willingness to license under the EUPL.
or has expressed by any other mean his willingness to license under the EUPL.
1. Definitions
1. Definitions
In this Licence, the following terms have the following meaning:
In this Licence, the following terms have the following meaning:
- The Licence: this Licence.
- 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.
- 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.
- 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 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 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 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.
- 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.
- 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.
- 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.
- 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:
2. Scope of the rights granted by the Licence
- 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.
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:
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.
- 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.
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.
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.
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.
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.
3. Communication of the Source Code
4. Limitations on copyright
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.
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
4. Limitations on copyright
The grant of the rights mentioned above is subject to some restrictions and
obligations imposed on the Licensee. Those obligations are the following:
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.
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.
5. Obligations of the Licensee
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.
The grant of the rights mentioned above is subject to some restrictions and
obligations imposed on the Licensee. Those obligations are the following:
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.
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.
6. Chain of Authorship
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.
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.
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.
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.
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.
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
6. Chain of Authorship
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.
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.
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.
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.
This disclaimer of warranty is an essential part of the Licence and a condition
for the grant of any rights to the Work.
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.
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.
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.
9. Additional agreements
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.
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.
This disclaimer of warranty is an essential part of the Licence and a condition
for the grant of any rights to the Work.
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.
8. Disclaimer of Liability
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.
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.
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.
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.
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.
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.
13. Miscellaneous
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.
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.
11. Information to the public
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.
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.
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.
12. Termination of the Licence
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.
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.
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:
13. Miscellaneous
- 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.
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.
Appendix
“Compatible Licences” according to article 5 EUPL are:
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.
- GNU General Public License (GNU GPL) v. 2
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.
- Open Software License (OSL) v. 2.1, v. 3.0
- Common Public License v. 1.0
- Eclipse Public License v. 1.0
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
- Cecill v. 2.0

@ -5,27 +5,27 @@ REST web service for Jackrabbit
This software is part of the gCube Framework (https://www.gcube-system.org/): 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.
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 including:
* the Sixth Framework Programme for Research and Technological Development -
DILIGENT (grant no. 004260);
* the Seventh Framework Programme for research, technological development and
demonstration - D4Science (grant no. 212488), D4Science-II (grant no.
239019),ENVRI (grant no. 283465), EUBrazilOpenBio (grant no. 288754), iMarine
(grant no. 283644);
* the H2020 research and innovation programme - BlueBRIDGE (grant no. 675680),
EGIEngage (grant no. 654142), ENVRIplus (grant no. 654182), Parthenos (grant
no. 654119), SoBigData (grant no. 654024), AGINFRA PLUS (grant no. 731001).
European Union programmes including:
* the Sixth Framework Programme for Research and Technological Development -
DILIGENT (grant no. 004260);
* the Seventh Framework Programme for research, technological development and
demonstration - D4Science (grant no. 212488), D4Science-II (grant no.
239019),ENVRI (grant no. 283465), EUBrazilOpenBio (grant no. 288754), iMarine
(grant no. 283644);
* the H2020 research and innovation programme - BlueBRIDGE (grant no. 675680),
EGIEngage (grant no. 654142), ENVRIplus (grant no. 654182), Parthenos (grant
no. 654119), SoBigData (grant no. 654024), AGINFRA PLUS (grant no. 731001).
Version
--------------------------------------------------
1.0.7-SNAPSHOT (2019-09-16)
1.4.2 (20230524-150450)
Please see the file named "changelog.xml" in this directory for the release notes.
@ -48,7 +48,7 @@ Download information
--------------------------------------------------
Source code is available from SVN:
${scm.url}
https://code-repo.d4science.org/gCubeSystem/storagehub
Binaries can be downloaded from the gCube website:
https://www.gcube-system.org/

@ -0,0 +1,8 @@
<application mode='online'>
<name>StorageHub</name>
<group>DataAccess</group>
<version>1.4.2</version>
<description>Storage Hub webapp</description>
<local-persistence location='target' />
<exclude>/workspace/api-docs/*</exclude>
</application>

@ -0,0 +1,26 @@
.d4science_intro {
top: 0;
z-index: 2000;
position: fixed;
display: block ruby;
padding: 10px;
background: white;
width: 100%;
height: 100px;
}
.navbar-fixed-top {
top: 100px !important;
}
.sidebar {
top: 160px !important;
}
.navbar {
margin-bottom: 40px !important;
}
.main {
top: 90px;
}

@ -9,15 +9,15 @@ import org.gcube.common.storagehub.model.expressions.GenericSearchableItem;
import org.gcube.common.storagehub.model.expressions.date.Before;
import org.gcube.common.storagehub.model.expressions.logical.And;
import org.gcube.common.storagehub.model.expressions.text.Contains;
import org.gcube.data.access.storagehub.Constants;
import org.gcube.data.access.storagehub.query.sql2.evaluators.Evaluators;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
//@RunWith(WeldJunit4Runner.class)
public class Expressions {
/*
private static Logger log = LoggerFactory.getLogger(Expression.class);
@Inject
@ -36,12 +36,21 @@ public class Expressions {
}
*/
@Test
public void test2() {
String groupId ="/gcube/devsec/devVre";
String title = groupId.substring(groupId.lastIndexOf("/")+1);
System.out.println(title);
public void test() {
String entirePath = "sp2/comic/";
/*String[] parentPathSplit = entirePath.split("/");
System.out.println(parentPathSplit.length);
for (String v: parentPathSplit)
System.out.println(v);
*/
String name = entirePath.replaceAll("([^/]*/)*(.*)", "$2");
String parentPath = entirePath.replaceAll("(([^/]*/)*)(.*)", "$1");
System.out.println(entirePath+" --"+name+"-- "+parentPath);
}
}

@ -5,6 +5,7 @@ import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import javax.jcr.Node;
@ -13,18 +14,23 @@ import javax.jcr.nodetype.NodeType;
import org.gcube.common.storagehub.model.items.Item;
import org.gcube.common.storagehub.model.types.ItemAction;
import org.gcube.data.access.storagehub.handlers.Node2ItemConverter;
import org.gcube.data.access.storagehub.handlers.items.Node2ItemConverter;
import org.junit.Assert;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.vlkan.rfos.RotatingFileOutputStream;
import com.vlkan.rfos.RotationConfig;
import com.vlkan.rfos.policy.DailyRotationPolicy;
import com.vlkan.rfos.policy.SizeBasedRotationPolicy;
public class TestFields {
Logger logger = LoggerFactory.getLogger(TestFields.class);
//@Test
@Test
public void iterateOverFields() throws Exception{
Property prop = mock(Property.class);
@ -63,4 +69,21 @@ public class TestFields {
}
@Test
public void print() throws Exception{
RotationConfig config = RotationConfig
.builder()
.file("/tmp/app.log")
.filePattern("/tmp/app-%d{yyyyMMdd-HHmmss.SSS}.log")
.policy(new SizeBasedRotationPolicy(1024 * 1024 * 100 /* 100MiB */))
.policy(DailyRotationPolicy.getInstance())
.build();
try (RotatingFileOutputStream stream = new RotatingFileOutputStream(config)) {
for (int i =0 ; i<1000 ; i++)
stream.write("Operation: done".getBytes(StandardCharsets.UTF_8));
}
}
}

@ -1,19 +1,5 @@
package org.gcube.data.access.fs;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.awt.image.DataBufferInt;
import java.awt.image.ImageObserver;
import java.io.File;
import java.util.Base64;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.imageio.ImageIO;
import org.junit.Test;
public class TestNode {
/*@Test

Loading…
Cancel
Save