Moved repository master into its own directory

This commit is contained in:
Chris Shields
2020-07-21 10:43:33 +01:00
parent a7afb73e58
commit cbd58ea958
6316 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="org.eclipse.ant.AntBuilderLaunchConfigurationType">
<booleanAttribute key="org.eclipse.ant.ui.ATTR_TARGETS_UPDATED" value="true"/>
<booleanAttribute key="org.eclipse.ant.ui.DEFAULT_VM_INSTALL" value="true"/>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
<listEntry value="/Repository/build.xml"/>
</listAttribute>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
<listEntry value="1"/>
</listAttribute>
<booleanAttribute key="org.eclipse.debug.core.appendEnvironmentVariables" value="true"/>
<booleanAttribute key="org.eclipse.debug.ui.ATTR_LAUNCH_IN_BACKGROUND" value="false"/>
<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry id=&quot;org.eclipse.ant.ui.classpathentry.antHome&quot;&gt;&#13;&#10;&lt;memento default=&quot;true&quot;/&gt;&#13;&#10;&lt;/runtimeClasspathEntry&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry id=&quot;org.eclipse.ant.ui.classpathentry.extraClasspathEntries&quot;&gt;&#13;&#10;&lt;memento/&gt;&#13;&#10;&lt;/runtimeClasspathEntry&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/3rd Party/lib/jibx-run-1.2.5.jar&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/3rd Party/lib/jibx-bind-1.2.5.jar&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/3rd Party/lib/bcel.jar&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/3rd Party/lib/xpp3-1.1.3_8.jar&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
</listAttribute>
<stringAttribute key="org.eclipse.jdt.launching.CLASSPATH_PROVIDER" value="org.eclipse.ant.ui.AntClasspathProvider"/>
<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="org.eclipse.ant.internal.ui.antsupport.InternalAntRunner"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="Repository"/>
<stringAttribute key="org.eclipse.jdt.launching.WORKING_DIRECTORY" value="${workspace_loc:Repository}"/>
<booleanAttribute key="org.eclipse.ui.externaltools.ATTR_BUILDER_ENABLED" value="false"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_BUILD_SCOPE" value="${working_set:&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#13;&#10;&lt;launchConfigurationWorkingSet editPageId=&quot;org.eclipse.ui.resourceWorkingSetPage&quot; factoryID=&quot;org.eclipse.ui.internal.WorkingSetFactory&quot; id=&quot;1264001019976_1&quot; label=&quot;workingSet&quot; name=&quot;workingSet&quot;&gt;&#13;&#10;&lt;item factoryID=&quot;org.eclipse.ui.internal.model.ResourceFactory&quot; path=&quot;/Repository/source/java/org/alfresco/repo/dictionary&quot; type=&quot;2&quot;/&gt;&#13;&#10;&lt;/launchConfigurationWorkingSet&gt;}"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="${workspace_loc:/Repository/build.xml}"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_RUN_BUILD_KINDS" value="full,incremental,auto,clean"/>
<booleanAttribute key="org.eclipse.ui.externaltools.ATTR_TRIGGERS_CONFIGURED" value="true"/>
<stringAttribute key="process_factory_id" value="org.eclipse.ant.ui.remoteAntProcessFactory"/>
</launchConfiguration>

4
repository/.gitbugtraq Normal file
View File

@@ -0,0 +1,4 @@
# For SmartGit
[bugtraq "jira"]
url = https://issues.alfresco.com/jira/browse/%BUGID%
logRegex = ([A-Z]+-\\d+)

40
repository/.gitignore vendored Normal file
View File

@@ -0,0 +1,40 @@
*.class
# Eclipse
.classpath
.settings
.project
# Intellij
.idea/
*.iml
*.iws
#VSCode
/.vscode
# Mac
.DS_Store
# Maven
target
*.log
*.log.*
# Mobile Tools for Java (J2ME)
.mtj
.tmp/
# Package Files #
*.jar
*.war
*.ear
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
alf_data
/src/main/resources/alfresco-global.properties
/src/main/resources/alfresco/extension/custom-log4j.properties

View File

@@ -0,0 +1,15 @@
<settings>
<!-- required to push artifacts to repositories -->
<servers>
<server>
<id>alfresco-public</id>
<username>${env.MAVEN_USERNAME}</username>
<password>${env.MAVEN_PASSWORD}</password>
</server>
<server>
<id>quay.io</id>
<username>${env.QUAY_USERNAME}</username>
<password>${env.QUAY_PASSWORD}</password>
</server>
</servers>
</settings>

122
repository/.travis.yml Normal file
View File

@@ -0,0 +1,122 @@
dist: xenial
sudo: required
language: java
jdk:
- openjdk11
services:
- docker
cache:
directories:
- $HOME/.m2
# the cache can grow constantly
before_cache:
- rm -rf $HOME/.m2/repository/org/alfresco/alfresco-repository
branches:
only:
- master
- /support\/.*/
stages:
- test
- release
install: travis_retry mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V
jobs:
include:
- stage: test
name: "AllUnitTestsSuite"
script: mvn test -B -Dtest=AllUnitTestsSuite
- name: "WhiteSource scan"
# only on support branches or master and if it is not a PR
if: fork = false AND (branch = master OR branch =~ /support\/SP\/.*/) AND type != pull_request
script:
# Download the latest version of WhiteSource Unified Agent
- curl -LJO https://github.com/whitesource/unified-agent-distribution/releases/latest/download/wss-unified-agent.jar
# Run WhiteSource Unified Agent
- java -jar wss-unified-agent.jar -apiKey ${WHITESOURCE_API_KEY} -c .wss-unified-agent.config
- name: "AppContext01TestSuite"
before_install:
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:11.7 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.15.8
script: travis_wait 20 mvn test -B -Dtest=AppContext01TestSuite -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco
- name: "AppContext02TestSuite"
before_install:
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:11.7 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.15.8
script: travis_wait 20 mvn test -B -Dtest=AppContext02TestSuite -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco
- name: "AppContext03TestSuite"
before_install:
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:11.7 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.15.8
- docker run -d -p 8090:8090 -e JAVA_OPTS=" -Xms256m -Xmx256m" alfresco/alfresco-transform-core-aio:2.3.2
script: travis_wait 20 mvn test -B -Dtest=AppContext03TestSuite -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco -Dalfresco-pdf-renderer.url=http://localhost:8090/ -Djodconverter.url=http://localhost:8090/ -Dimg.url=http://localhost:8090/ -Dtika.url=http://localhost:8090/ -Dtransform.misc.url=http://localhost:8090/
- name: "AppContext04TestSuite"
before_install:
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:11.7 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.15.8
- docker run -d -p 8090:8090 -e JAVA_OPTS=" -Xms256m -Xmx256m" alfresco/alfresco-transform-core-aio:2.3.2
script: travis_wait 20 mvn test -B -Dtest=AppContext04TestSuite -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco
- name: "AppContext05TestSuite"
before_install:
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:11.7 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.15.8
- "mkdir -p $HOME/tmp"
- "cp src/test/resources/realms/alfresco-realm.json $HOME/tmp"
- docker login quay.io -u ${QUAY_USERNAME} -p ${QUAY_PASSWORD}
- "export HOST_IP=$(ip address show | grep -E \"([0-9]{1,3}\\.){3}[0-9]{1,3}\" | grep -v 127.0.0.1 | awk '{ print $2 }' | head -n 1 )"
- "export HOST_IP=$(echo ${HOST_IP%/*})"
- docker run -d -e KEYCLOAK_USER=admin -e KEYCLOAK_PASSWORD=admin -e DB_VENDOR=h2 -p 8999:8080 -e KEYCLOAK_IMPORT=/tmp/alfresco-realm.json -v $HOME/tmp/alfresco-realm.json:/tmp/alfresco-realm.json quay.io/alfresco/alfresco-identity-service:1.2
script: travis_wait 20 mvn test -B -Dtest=AppContext05TestSuite -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco "-Didentity-service.auth-server-url=http://${HOST_IP}:8999/auth"
- name: "AppContext06TestSuite"
before_install:
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:11.7 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.15.8
- docker run -d -p 8090:8090 -e JAVA_OPTS=" -Xms256m -Xmx256m" alfresco/alfresco-transform-core-aio:2.3.2
script: travis_wait 20 mvn test -B -Dtest=AppContext06TestSuite -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco -Dalfresco-pdf-renderer.url=http://localhost:8090/ -Djodconverter.url=http://localhost:8090/ -Dimg.url=http://localhost:8090/ -Dtika.url=http://localhost:8090/ -Dtransform.misc.url=http://localhost:8090/
- name: "AppContextExtraTestSuite"
before_install:
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:11.7 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.15.8
script: travis_wait 20 mvn test -B -Dtest=AppContextExtraTestSuite -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco
- name: "MiscContextTestSuite"
before_install:
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:11.7 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.15.8
- docker run -d -p 8090:8090 -e JAVA_OPTS=" -Xms256m -Xmx256m" alfresco/alfresco-transform-core-aio:2.3.2
script: travis_wait 20 mvn test -B -Dtest=MiscContextTestSuite -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco -Dalfresco-pdf-renderer.url=http://localhost:8090/ -Djodconverter.url=http://localhost:8090/ -Dimg.url=http://localhost:8090/ -Dtika.url=http://localhost:8090/ -Dtransform.misc.url=http://localhost:8090/
- name: "MySQL tests"
before_install:
- docker run -d -p 3307:3306 -e MYSQL_ROOT_PASSWORD=alfresco -e MYSQL_USER=alfresco -e MYSQL_DATABASE=alfresco -e MYSQL_PASSWORD=alfresco mysql:5.7.23 --transaction-isolation='READ-COMMITTED'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.15.8
script: travis_wait 20 mvn test -B -Dtest=AllDBTestsTestSuite -Ddb.driver=com.mysql.jdbc.Driver -Ddb.name=alfresco -Ddb.url=jdbc:mysql://localhost:3307/alfresco -Ddb.username=alfresco -Ddb.password=alfresco
- name: "PostgreSQL 10 tests"
before_install:
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:10.9 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.15.8
script: travis_wait 20 mvn test -B -Dtest=AllDBTestsTestSuite -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco
- name: "PostgreSQL 11 tests"
before_install:
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:11.7 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.15.8
script: travis_wait 20 mvn test -B -Dtest=AllDBTestsTestSuite -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco
- name: "MariaDB tests"
before_install:
- docker run -d -p 3307:3306 --name mariadb -e MYSQL_ROOT_PASSWORD=alfresco -e MYSQL_USER=alfresco -e MYSQL_DATABASE=alfresco -e MYSQL_PASSWORD=alfresco mariadb:10.2.18 --transaction-isolation=READ-COMMITTED --max-connections=300 --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.15.8
script: travis_wait 20 mvn test -B -Dtest=AllDBTestsTestSuite -Ddb.name=alfresco -Ddb.url=jdbc:mariadb://localhost:3307/alfresco?useUnicode=yes\&characterEncoding=UTF-8 -Ddb.username=alfresco -Ddb.password=alfresco -Ddb.driver=org.mariadb.jdbc.Driver
- stage: release
name: "Push to Nexus"
if: fork = false AND (branch = master OR branch =~ /support\/.*/) AND type != pull_request AND commit_message !~ /\[no-release\]/
before_install:
- "cp .travis.settings.xml $HOME/.m2/settings.xml"
script:
# Use full history for release
- git checkout -B "${TRAVIS_BRANCH}"
# Add email to link commits to user
- git config user.email "${GIT_EMAIL}"
# Skip building of release commits
- mvn --batch-mode -q -DscmCommentPrefix="[maven-release-plugin][skip ci] " -Dusername="${GIT_USERNAME}" -Dpassword="${GIT_PASSWORD}" -DskipTests -Darguments=-DskipTests release:clean release:prepare release:perform

8
repository/.whitesource Normal file
View File

@@ -0,0 +1,8 @@
{
"generalSettings": {
"shouldScanRepo": true
},
"checkRunSettings": {
"vulnerableCheckRunConclusionLevel": "failure"
}
}

View File

@@ -0,0 +1,228 @@
####################################################################
# WhiteSource Unified-Agent configuration file
####################################################################
##########################################
# GENERAL SCAN MODE: Files and Package Managers
##########################################
checkPolicies=true
forceCheckAllDependencies=true
forceUpdate=true
forceUpdate.failBuildOnPolicyViolation=true
offline=false
#ignoreSourceFiles=true
#scanComment=
#updateInventory=false
#resolveAllDependencies=false
#failErrorLevel=ALL
#requireKnownSha1=false
#generateScanReport=true
#scanReportTimeoutMinutes=10
#excludeDependenciesFromNodes=.*commons-io.*,.*maven-model
#projectPerFolder=true
#projectPerFolderIncludes=
#projectPerFolderExcludes=
#wss.connectionTimeoutMinutes=60
# Change the below URL to your WhiteSource server.
# Use the 'WhiteSource Server URL' which can be retrieved
# from your 'Profile' page on the 'Server URLs' panel.
# Then, add the '/agent' path to it.
wss.url=https://saas.whitesourcesoftware.com/agent
#npm.resolveDependencies=false
#npm.ignoreSourceFiles=false
#npm.includeDevDependencies=true
#npm.runPreStep=true
#npm.ignoreNpmLsErrors=true
#npm.ignoreScripts=true
#npm.yarnProject=true
#npm.accessToken=
#npm.identifyByNameAndVersion=true
#bower.resolveDependencies=false
#bower.ignoreSourceFiles=true
#bower.runPreStep=true
#nuget.resolvePackagesConfigFiles=false
#nuget.resolveCsProjFiles=false
#nuget.resolveDependencies=false
#nuget.restoreDependencies=true
#nuget.ignoreSourceFiles=true
#nuget.runPreStep=true
#nuget.resolveNuspecFiles=false
#python.resolveDependencies=false
#python.ignoreSourceFiles=false
#python.ignorePipInstallErrors=true
#python.installVirtualenv=true
#python.resolveHierarchyTree=false
#python.requirementsFileIncludes=requirements.txt
#python.resolveSetupPyFiles=true
#python.runPipenvPreStep=true
#python.pipenvDevDependencies=true
#python.IgnorePipenvInstallErrors=true
#maven.ignoredScopes=test provided
maven.resolveDependencies=true
#maven.ignoreSourceFiles=true
#maven.aggregateModules=true
maven.ignorePomModules=false
#maven.runPreStep=true
#maven.ignoreMvnTreeErrors=true
#maven.environmentPath=
#maven.m2RepositoryPath=
#gradle.ignoredScopes=
#gradle.resolveDependencies=false
#gradle.runAssembleCommand=false
#gradle.runPreStep=true
#gradle.ignoreSourceFiles=true
#gradle.aggregateModules=true
#gradle.preferredEnvironment=wrapper
#gradle.localRepositoryPath=
#paket.resolveDependencies=false
#paket.ignoredGroups=
#paket.ignoreSourceFiles=false
#paket.runPreStep=true
#paket.exePath=
#go.resolveDependencies=false
#go.collectDependenciesAtRuntime=true
#go.dependencyManager=
#go.ignoreSourceFiles=true
#go.glide.ignoreTestPackages=false
#go.gogradle.enableTaskAlias=true
#ruby.resolveDependencies = false
#ruby.ignoreSourceFiles = false
#ruby.installMissingGems = true
#ruby.runBundleInstall = true
#ruby.overwriteGemFile = true
#sbt.resolveDependencies=false
#sbt.ignoreSourceFiles=true
#sbt.aggregateModules=true
#sbt.runPreStep=true
#sbt.targetFolder=
#php.resolveDependencies=false
#php.runPreStep=true
#php.includeDevDependencies=true
#html.resolveDependencies=false
#cocoapods.resolveDependencies=false
#cocoapods.runPreStep=true
#cocoapods.ignoreSourceFiles=false
#hex.resolveDependencies=false
#hex.runPreStep=true
#hex.ignoreSourceFiles=false
#hex.aggregateModules=true
##################################
# Organization tokens:
##################################
apiKey=
#userKey is required if WhiteSource administrator has enabled "Enforce user level access" option
#userKey=
projectName=alfresco-repository
projectVersion=
projectToken=
productName=ACS Community
productVersion=
productToken=
#updateType=APPEND
#requesterEmail=user@provider.com
#########################################################################################
# Includes/Excludes Glob patterns - PLEASE USE ONLY ONE EXCLUDE LINE AND ONE INCLUDE LINE
#########################################################################################
#includes=**/*.c **/*.cc **/*.cp **/*.cpp **/*.cxx **/*.c++ **/*.h **/*.hpp **/*.hxx
#includes=**/*.m **/*.mm **/*.js **/*.php
includes=**/*.jar
#includes=**/*.gem **/*.rb
#includes=**/*.dll **/*.cs **/*.nupkg
#includes=**/*.tgz **/*.deb **/*.gzip **/*.rpm **/*.tar.bz2
#includes=**/*.zip **/*.tar.gz **/*.egg **/*.whl **/*.py
## Exclude file extensions or specific directories by adding **/*.<extension> or **<excluded_dir>/**
excludes=**/*sources.jar **/*javadoc.jar
case.sensitive.glob=false
followSymbolicLinks=true
##################################
# Archive properties
##################################
#archiveExtractionDepth=2
#archiveIncludes=**/*.war **/*.ear
#archiveExcludes=**/*sources.jar
##################################
# Proxy settings
##################################
#proxy.host=
#proxy.port=
#proxy.user=
#proxy.pass=
##################################
# SCM settings
##################################
#scm.type=
#scm.user=
#scm.pass=
#scm.ppk=
#scm.url=
#scm.branch=
#scm.tag=
#scm.npmInstall=
#scm.npmInstallTimeoutMinutes=
#scm.repositoriesFile=
##############################################
# SCAN MODE: Linux package manager settings
##############################################
#scanPackageManager=true
##################################
# SCAN MODE: Docker images
##################################
#docker.scanImages=true
#docker.includes=.*.*
#docker.excludes=
#docker.pull.enable=true
#docker.pull.images=.*.*
#docker.pull.maxImages=10
#docker.pull.tags=.*.*
#docker.pull.digest=
#docker.delete.force=true
#docker.login.sudo=false
#docker.aws.enable=true
#docker.aws.registryIds=
##################################
# SCAN MODE: Docker containers
##################################
#docker.scanContainers=true
#docker.containerIncludes=.*.*
#docker.containerExcludes=
################################
# Serverless settings
################################
#serverless.provider=
#serverless.scanFunctions=true
#serverless.includes=
#serverless.excludes=
#serverless.region=
#serverless.maxFunctions=10

View File

@@ -0,0 +1,16 @@
### Contributing
Thanks for your interest in contributing to this project!
The following is a set of guidelines for contributing to this library. Most of them will make the life of the reviewer easier and therefore decrease the time required for the patch be included in the next version.
Because this project forms a part of Alfresco Content Services, the guidelines are hosted in the [Alfresco Social Community](http://community.alfresco.com/community/ecm) where they can be referenced from multiple projects.
Read an [overview on how this project is goverened](https://community.alfresco.com/docs/DOC-6385-project-overview-repository).
You can report an issue in the ALF project of the [Alfresco issue tracker](http://issues.alfresco.com).
Read [instructions for a good issue report](https://community.alfresco.com/docs/DOC-6263-reporting-an-issue).
Read [instructions for making a contribution](https://community.alfresco.com/docs/DOC-6269-submitting-contributions).
Please follow [the coding standards](https://community.alfresco.com/docs/DOC-4658-coding-standards).

165
repository/LICENSE Normal file
View File

@@ -0,0 +1,165 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.

41
repository/README.md Normal file
View File

@@ -0,0 +1,41 @@
### Alfresco Repository
[![Build Status](https://travis-ci.com/Alfresco/alfresco-repository.svg?branch=master)](https://travis-ci.com/Alfresco/alfresco-repository)
Repository is a library packaged as a jar file which is part of [Alfresco Content Services Repository](https://community.alfresco.com/docs/DOC-6385-project-overview-repository).
The library contains the following:
* DAOs and SQL scripts
* Various Service implementations
* Utility classes
### Building and testing
The project can be built by running Maven command:
~~~
mvn clean install
~~~
The tests are combined in test classes split by test type or Spring application context used in the test, see classes in _src/test/java/org/alfresco_. All of these classes as well as individual tests can be run by specifying the test class name and a set of DB connection properties, for example:
~~~
mvn clean test -Dtest=SomeRepoTest -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql:alfresco -Ddb.username=alfresco -Ddb.password=alfresco
~~~
### Artifacts
The artifacts can be obtained by:
* downloading from [Alfresco repository](https://artifacts.alfresco.com/nexus/content/groups/public)
* getting as Maven dependency by adding the dependency to your pom file:
~~~
<dependency>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-repository</artifactId>
<version>version</version>
</dependency>
~~~
and Alfresco Maven repository:
~~~
<repository>
<id>alfresco-maven-repo</id>
<url>https://artifacts.alfresco.com/nexus/content/groups/public</url>
</repository>
~~~
The SNAPSHOT version of the artifact is **never** published.
### Contributing guide
Please use [this guide](CONTRIBUTING.md) to make a contribution to the project.

66
repository/docs/README.md Normal file
View File

@@ -0,0 +1,66 @@
# Community Technical Documentation Index
## Repository
### Meta-data Services
* [Node Storage and Retrieval](./meta-data-services/node-storage-and-retrieval)
* Dictionary
* Files and Folders
* Smart Folders
* [Versions](./meta-data-services/versions)
* Permissions
* Tagging
* Multilingual Content
### Content Store
* Content Storage and Retrieval
### Rules Engine
* Rules
* Actions
### Workflow
* Embedded Activiti
### Transformation
* Renditions
### Content Analysis
* Meta-data Extraction
### File Protocols
* FTP Protocol
* WebDAV Protocol
### Application Protocols
* IMAP Protocol
* Office Protocols
### Scripting
* JavaScript API
* Freemarker API
### Sync and Transfer
* Content Replication
* Content Transfer
### Event Log
* Audit
* Log Messages
### Identity Provider
* [Authentication](./identity-provider/authentication)
* LDAP Sync
### Deployment
* Installer
* MMT
* Patch
### Messaging
* Messages and Topics
### Infrastructure
* Module Framework
* Policies and Behaviours
* Multi-tenancy

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View File

@@ -0,0 +1,41 @@
@startuml
title Audit and Attributes - High Level Entities
skinparam linetype ortho
rectangle AuditDAO #lightgrey {
rectangle AuditApplication #orange
rectangle AuditModel #white
rectangle ModelContent #white
rectangle AuditEntry #white
}
rectangle PropertyValueDAO #lightgrey {
rectangle Attribute #orange
rectangle ComplexProperty #white
rectangle SimpleProperty #white
rectangle Value #white
rectangle Caches #white {
rectangle PropertyUniqueContextSharedCache #white
rectangle PropertyClassSharedCache #white
rectangle PropertyValueSharedCache #white
}
}
AuditApplication --* AuditModel
AuditApplication --* AuditEntry
AuditModel -- ModelContent
PropertyClassSharedCache ->SimpleProperty
PropertyValueSharedCache ->SimpleProperty
PropertyUniqueContextSharedCache ->Attribute
SimpleProperty .. Value :value
SimpleProperty -- Attribute: keys (1 to 3)
ComplexProperty -- SimpleProperty
ComplexProperty -- AuditEntry :audit values
ComplexProperty -- AuditApplication :disabled paths
Attribute -- ComplexProperty
center footer Copyright 2016 Alfresco Software Inc
@enduml

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 KiB

View File

@@ -0,0 +1,86 @@
@startuml
' Generated using https://github.com/juanmf/Java2PlantUML
left to right direction
' Participants
interface org.alfresco.jlan.ftp.FTPAuthenticator {
--
+ authenticateUser(c ClientInfo, c FTPSrvSession) : boolean
+ closeAuthenticator() : void
+ initialize(c ServerConfiguration, i ConfigElement) : void
}
interface org.springframework.beans.factory.DisposableBean {
--
+ destroy() : void
}
interface org.alfresco.filesys.auth.ftp.package-info {
--
}
interface org.alfresco.repo.management.subsystems.ActivateableBean {
--
+ isActive() : boolean
}
class org.alfresco.filesys.auth.ftp.FTPAuthenticatorBase {
# logger : i Log
# serverConfiguration : i ServerConfigurationAccessor
- active : boolean
- authenticationComponent : i AuthenticationComponent
- authenticationService : i AuthenticationService
- authorityService : i AuthorityService
- transactionService : i TransactionService
--
+ FTPAuthenticatorBase()
# checkForAdminUserName(c ClientInfo) : void
# createTransaction() : UserTransaction
# getAuthenticationComponent() : AuthenticationComponent
# getAuthenticationService() : AuthenticationService
# getAuthorityService() : AuthorityService
# getTransactionService() : TransactionService
+ authenticateUser(c ClientInfo, c FTPSrvSession) : boolean
+ closeAuthenticator() : void
+ destroy() : void
+ initialize() : void
+ initialize(c ServerConfiguration, i ConfigElement) : void
+ isActive() : boolean
+ setActive(boolean) : void
+ setAuthenticationComponent(i AuthenticationComponent) : void
+ setAuthenticationService(i AuthenticationService) : void
+ setAuthorityService(i AuthorityService) : void
+ setConfig(i ServerConfigurationAccessor) : void
+ setTransactionService(i TransactionService) : void
}
class org.alfresco.filesys.auth.ftp.AlfrescoFtpAuthenticator {
# m_encryptor : c PasswordEncryptor
# m_md4Encoder : i MD4PasswordEncoder
--
+ AlfrescoFtpAuthenticator()
# doGuestLogon(c AlfrescoClientInfo, c SrvSession) : void
+ authenticateUser(c ClientInfo, c FTPSrvSession) : boolean
}
' Relations
org.alfresco.filesys.auth.ftp.AlfrescoFtpAuthenticator "1" o-left- "1" org.alfresco.repo.security.authentication.MD4PasswordEncoder : m_md4Encoder: i MD4PasswordEncoder
org.alfresco.filesys.auth.ftp.FTPAuthenticatorBase "1" o-left- "1" org.alfresco.service.transaction.TransactionService : transactionService: i TransactionService
org.alfresco.filesys.auth.ftp.AlfrescoFtpAuthenticator "1" o-left- "1" org.alfresco.jlan.server.auth.PasswordEncryptor : m_encryptor: c PasswordEncryptor
org.alfresco.filesys.auth.ftp.FTPAuthenticatorBase "1" o-left- "1" org.apache.commons.logging.Log : logger: i Log
org.alfresco.filesys.auth.ftp.FTPAuthenticatorBase "1" o-left- "1" org.alfresco.jlan.server.config.ServerConfigurationAccessor : serverConfiguration: i ServerConfigurationAccessor
org.alfresco.filesys.auth.ftp.FTPAuthenticatorBase "1" o-left- "1" org.alfresco.repo.security.authentication.AuthenticationComponent : authenticationComponent: i AuthenticationComponent
org.alfresco.filesys.auth.ftp.FTPAuthenticatorBase "1" o-left- "1" org.alfresco.service.cmr.security.AuthorityService : authorityService: i AuthorityService
org.alfresco.filesys.auth.ftp.FTPAuthenticatorBase "1" o-left- "1" org.alfresco.service.cmr.security.AuthenticationService : authenticationService: i AuthenticationService
org.alfresco.filesys.auth.ftp.AlfrescoFtpAuthenticator -up|> org.alfresco.filesys.auth.ftp.FTPAuthenticatorBase
org.alfresco.filesys.auth.ftp.FTPAuthenticatorBase ..up|> org.alfresco.jlan.ftp.FTPAuthenticator
org.alfresco.filesys.auth.ftp.FTPAuthenticatorBase ..up|> org.alfresco.repo.management.subsystems.ActivateableBean
org.alfresco.filesys.auth.ftp.FTPAuthenticatorBase ..up|> org.springframework.beans.factory.DisposableBean
' Notes
@enduml

Binary file not shown.

After

Width:  |  Height:  |  Size: 808 KiB

View File

@@ -0,0 +1,165 @@
## Authentication
![Completeness Badge](https://img.shields.io/badge/Document_Level-In_Progress-yellow.svg?style=flat-square)
### Purpose
The purpose of this sub-component is to authenticate users.
***
### Overview
In order to use any service in Alfresco, a user must be authenticated.
Alfresco provides a default Authentication implementation that uses userid's and passwored
managed by Alfresco. But, importantly, Alfresco also allows the customer
to integrate with a number of external Authentication providers including
* Active Directory
* Kerberos
* LDAP
***
### Artifacts and Guidance
* Source Code Links:
* https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root/enterpriseprojects/repository/source/java/
* https://github.com/Alfresco/alfresco-data-model/tree/master/src/main/java/org/alfresco/repo/security/authentication
* License: LGPL
* Issue Tracker Link: https://issues.alfresco.com/jira/issues/?jql=project%3DREPO
* Documentation Link: http://docs.alfresco.com/5.2/concepts/auth-intro.html
* Contribution Model: Alfresco Open Source
***
### Prerequisite Knowledge
* [Acegi](http://springinpractice.com/2008/02/26/acegi-overview)
* [CAS](https://en.wikipedia.org/wiki/Central_Authentication_Service)
* [JAAS](http://docs.oracle.com/javase/8/docs/technotes/guides/security/jaas/JAASRefGuide.html)
* [Kerberos](https://msdn.microsoft.com/en-us/library/bb742516.aspx)
* [LDAP](https://en.wikipedia.org/wiki/Lightweight_Directory_Access_Protocol)
***
### Design
#### Default Authentication
In order to use any service in Alfresco, a user or client must be authenticated.
There is a default implementation provided that authenticates users based on a userid and password, where the userid's and passwords
are stored in the Alfresco repository.
#### Chaining
Most production systems that use Alfresco will rely upon more secure approaches, so Alfresco also allows the
customer to integrate a choice of existing authentication providers, including *Active Directory*, *Kerberos* and *LDAP*.
The implementation of each such Authorization provider is delivered as a separate Alfresco Subsystem.
The Subsystems are chained together as an ordered list of providers each of which, in turn, will be given
a chance to authenticate the user, until the user is authenticated or there are no providers, in
which case the authentication of the user fails.
#### Component Model
#### Data Model
![Data Model](../resource/class/org.alfresco.repo.security.authentication.png)
#### Data Dictionary
#### Flows
##### Login Flow
##### Default Authentication Login Flow
This flow starts when the Login Post request
depicted in [Client Login](../../../share/share-app/resource/sequence/client-login-sequence.png) reaches the repository tier.
![Default Authentication Login Flow](./resource/sequence/login-using-default-authentication.png)
##### Default Authentication Logoff Flow
![Note](https://img.shields.io/badge/Editor-TODO-yellow.svg?&style=flat-square?colorB=2196f3&style=flat-square)
##### Kerberos Authentication Login Flow
![Note](https://img.shields.io/badge/Editor-TODO-yellow.svg?&style=flat-square?colorB=2196f3&style=flat-square)
#### Class Diagram
![Authentication](../resource/class/org.alfresco.service.cmr.security.class.png)
### APIs and Interfaces
#### Java
The sub-component provides a definition and implementation of the following
Java interfaces.
* **Authentication Service**
**Note**: This service is part of the Public API
* authenticate using a user name and password
* authenticate using a ticket
* create, update and delete authentication information
* clear the current authentication
* invalidate a ticket
* get the username for who is currently authenticated
* get a ticket for subsequent re-authentication
* determine if the current user is 'the system user
* **MutableAuthenticationService*
In addition, there are a number of related interfaces that provide
a way to manage users and groups and permissions
![Note](https://img.shields.io/badge/Editor-Note-yellow.svg?&style=flat-square?colorB=2196f3&style=flat-square)
*This information should probably move to the Identity sub-component after the content already there (future design thoughts)
is relocated*
* **Authority Service**
**Note**: This service is part of the Public API
* create authority identifiers
* query for authority identifiers
* delete authority identifiers
* organize authority identifiers into hierarchies
* query against authority identifiers hierarchies
* find all the authorties that apply to the current authenticated user
* determine if the current authenticated user has admin rights
* **Ownable Service**
**Note**: This service is **not** part of the Public API
* determine the owner of a node;
* set the owner of a node;
* determine if a node has an owner
* allow the current user to take ownership of a node
* **Person Service**
**Note**: This service is part of the Public API
* obtain a reference to the Person node for a given user name
* determine if a person entry exists for a user
* create missing people entries, with default settings, on demand
* supply a list of mutable properties for each person
* create, delete and update personal information
***
#### REST
The sub-component provides the following REST API
* https://api-explorer.alfresco.com/api-explorer/#/authentication
### Configuration
***
### Performance Considerations
![Note](https://img.shields.io/badge/Editor-TODO-yellow.svg?&style=flat-square?colorB=2196f3&style=flat-square)
Describe Caching of tickets
As mentioned in the *Security Considerations* section, Bcrypt can be used to encrypt passwords. Note that is
is much slower than MD4 or SHA-256.
***
### Security Considerations
***
In the case of the default Authentication provider, Alfresco is responsible for storing
userid's and passwords. The passwords are stored as hashed values. The default hashing
algorithm used is MD4, but the customer can also elect to use more secure hashing algorithms
include SHA-256 and Bcrypt. The system property *system.preferred.password.encoding* is used
to select which algorithm is used.
### Cloud Considerations
![Note](https://img.shields.io/badge/Editor-TODO-yellow.svg?&style=flat-square?colorB=2196f3&style=flat-square)
Describe OAuth2 for protection of Cloud REST API's.
***

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 KiB

View File

@@ -0,0 +1,117 @@
@startuml
Title: Default Authentication Login Flow
participant "Repository\nContainer" as W
participant "LoginPost\nbean" as LB
participant "SubsystemChaining\nAuthenticationService" as SCAS
participant "MutableAuthentication\nServiceImpl" as AS
participant "Authentication\nComponentImpl" as AC
participant "PersonServiceImpl" as PS
participant "NodeService" as NS
participant "Authentication\nContextImpl" as ACX
participant "Authentication\nUtil" as AU
participant "InMemoryTicketComponentImpl" as ITC
participant "AuthorityServiceImpl" as AUS
participant "RepositoryAuthenticatedUser" as RAU
participant "RepositoryAuthenticationDao" as RAD
participant "MD4PasswordEncoderImpl" as MPE
participant "RepositoryAuthenticationProvider" as RAP
participant "CompositePasswordEncoder" as CPE
activate W
W->LB: login request <username> <password>
LB->LB: login <username> <password>
LB->SCAS:authenticate <username> <password>
note right of SCAS
allowedUsers and maxUsers check
end note
SCAS->SCAS: preAuthenticationCheck
note right of SCAS
get list of usableAuthenticationServices
and iterates over this list. In
this default case there is a single
service 'alfrescoNtlm'
end note
SCAS->AS: authenticate\n<username> <password>
AS->AC:clearCurrentSecurityContext
AC->ACX:clearCurrentSecurityContext
ACX->AU:clearCurrentSecurityContext
AU->ACX: done
ACX->AC: done
AC->AS: done
AS->ITC:clearCurrentTicket
ITC->AS: done
note right of AS
allowedUsers and maxUsers check
end note
AS->AS: preAuthenticationCheck
note right of AS
Checks cache to detect
Brute Force attack
end note
AS->AS: isUserProtected <username>
AS->AC: authenticate\n<username> <password>
AC->PS:getUserIdentifier <username>
PS->PS:getPersonOrNullImpl
PS->PS:looks for Person\nin Cache
PS->NS:get properties of Person\nand checks if\nneeds to add to cache
PS->AC: username
note right of AC
Checks for guest
end note
AC->AC:isGuestUserName
AC->AC: authenticateImpl <username> <password>
AC->AU: getUserTenant
AU->AC: tenant
group Retrying Transaction
AC->RAD: loadUserByUsername <username>
RAD->RAU: constructor <username> <hashedpassword>
RAD->AC: user details
AC->RAP: isPasswordCorrect\n(UsernamePasswordAuthenticationToken, user details)
RAP->CPE: matches
CPE->MPE:isPasswordValid
MPE->MPE:encodeInternal
MPE->MPE: true
MPE->RAP: true
RAP->AC: true
AC->AUS: isAdminAuthority
AUS->AC: false
AC->PS:getPersonOrNullImpl
PS->PS:looks for Person\nin Cache
PS->NS:get properties of Person\nand checks if\nneeds to add to cache
NS->PS: properties
PS->AC: username
end
group setCurrentUser <username>
AC->ACX: isSystemUserName <username>
ACX->AC: false
AC->AC: setUserDetails (role=<b>ROLE_AUTHENTICATED</b>)
end
note right of AC
Increment numberSuccessfulAuthentications
end note
AC->AC: onAuthenticate
AC->AS: succeeded
AS->ITC: clearCurrentTicket
ITC->AS: done
AS->ITC: getCurrentTicket (auto create off)
ITC->AS
AS->AS: getNewTicket
AS->AS: preAuthenticationCheck
AS->ITC: getNewTicket
AS->SCAS
SCAS->LB
note right of LB
Put ticket into the model
end note
LB->AS: getCurrentTicket
AS->LB: ticket
LB->W: Login Request Response
note right of W
Authentication TICKET
returned in model
end note
deactivate W
@enduml

Binary file not shown.

After

Width:  |  Height:  |  Size: 383 KiB

View File

@@ -0,0 +1,310 @@
@startuml
' Generated using https://github.com/juanmf/Java2PlantUML
left to right direction
' Participants
interface org.alfresco.repo.security.authentication.AlfrescoSecureContext {
--
+ getEffectiveAuthentication() : Authentication
+ getRealAuthentication() : Authentication
+ setEffectiveAuthentication(i Authentication) : void
+ setRealAuthentication(i Authentication) : void
}
interface net.sf.acegisecurity.context.security.SecureContext {
--
+ getAuthentication() : Authentication
+ setAuthentication(i Authentication) : void
}
class org.alfresco.repo.security.authentication.AuthenticationUtil$ThreadLocalStack {
--
~ AuthenticationUtil$ThreadLocalStack()
# initialValue() : Stack
}
class org.alfresco.repo.security.authentication.TicketExpiredException {
- serialVersionUID : long
--
+ TicketExpiredException(c String)
+ TicketExpiredException(c String, c Throwable)
}
class org.alfresco.repo.security.authentication.InMemoryTicketComponentImpl$Ticket {
- expires : c InMemoryTicketComponentImpl$ExpiryMode
- expiryDate : c Date
- serialVersionUID : long
- testDuration : c Duration
- ticketId : c String
- userName : c String
- validDuration : c Duration
--
- InMemoryTicketComponentImpl$Ticket(c InMemoryTicketComponentImpl$ExpiryMode, c Date, c String, c Duration, c String)
~ InMemoryTicketComponentImpl$Ticket(c InMemoryTicketComponentImpl$ExpiryMode, c Date, c String, c Duration)
# getExpires() : InMemoryTicketComponentImpl$ExpiryMode
# getExpiryDate() : Date
# getTicketId() : String
# getUserName() : String
+ equals(c Object) : boolean
+ hashCode() : int
~ getNewEntry() : InMemoryTicketComponentImpl$Ticket
~ hasExpired(c Date) : boolean
}
class org.alfresco.repo.security.authentication.InMemoryTicketComponentImpl {
+ GRANTED_AUTHORITY_TICKET_PREFIX : c String
- currentTicket : ThreadLocal< String>
- expiryMode : c InMemoryTicketComponentImpl$ExpiryMode
- guid : c String
- oneOff : boolean
- ticketsCache : SimpleCache< String, InMemoryTicketComponentImpl$Ticket>
- ticketsExpire : boolean
- useSingleTicketPerUser : boolean
- validDuration : c Duration
--
+ InMemoryTicketComponentImpl()
+ clearCurrentSecurityContext() : void
+ clearCurrentTicket() : void
+ countTickets(boolean) : int
+ equals(c Object) : boolean
+ getAuthorityForTicket(c String) : String
+ getCurrentTicket(c String, boolean) : String
+ getNewTicket(c String) : String
+ getUseSingleTicketPerUser() : boolean
+ getUsersWithTickets(boolean) : Set
+ hashCode() : int
+ invalidateTicketById(c String) : void
+ invalidateTicketByUser(c String) : void
+ invalidateTickets(boolean) : int
+ setExpiryMode(c String) : void
+ setOneOff(boolean) : void
+ setTicketsCache( SimpleCache< String, InMemoryTicketComponentImpl$Ticket>) : void
+ setTicketsExpire(boolean) : void
+ setUseSingleTicketPerUser(boolean) : void
+ setValidDuration(c String) : void
+ validateTicket(c String) : String
- findNonExpiredUserTicket(c String) : InMemoryTicketComponentImpl$Ticket
- getTicketByTicketString(c String) : InMemoryTicketComponentImpl$Ticket
- getTicketKey(c String) : String
}
interface org.springframework.beans.factory.InitializingBean {
--
+ afterPropertiesSet() : void
}
interface org.alfresco.repo.security.authentication.AuthenticationStep {
--
+ getArgs() : Object;
+ getKey() : String
+ getMessage() : String
+ isSuccess() : boolean
}
interface org.alfresco.repo.security.authentication.AuthenticationUtil$RunAsWork <Result extends c Object> {
--
+ doWork() : Object
}
class org.alfresco.repo.security.authentication.AuthenticationUtil {
+ SYSTEM_USER_NAME : c String
- defaultAdminUserName : c String
- defaultGuestUserName : c String
- initialized : boolean
- mtEnabled : boolean
- threadLocalFullAuthenticationStack : ThreadLocal< Stack< Authentication>>
- threadLocalRunAsAuthenticationStack : ThreadLocal< Stack< Authentication>>
- threadLocalTenantDomainStack : ThreadLocal< Stack< String>>
~ s_logger : i Log
--
+ AuthenticationUtil()
+ afterPropertiesSet() : void
+ clearCurrentSecurityContext() : void
+ getAdminRoleName() : String
+ getAdminUserName() : String
+ getFullAuthentication() : Authentication
+ getFullyAuthenticatedUser() : String
+ getGuestRoleName() : String
+ getGuestUserName() : String
+ getRunAsAuthentication() : Authentication
+ getRunAsUser() : String
+ getSystemUserName() : String
+ getUserTenant(c String) : Pair
+ isMtEnabled() : boolean
+ isRunAsUserTheSystemUser() : boolean
+ logAuthenticatedUsers() : void
+ logNDC(c String) : void
+ popAuthentication() : void
+ pushAuthentication() : void
+ runAs( AuthenticationUtil$RunAsWork<R>, c String) : Object
+ runAsSystem( AuthenticationUtil$RunAsWork<R>) : Object
+ setAdminUserAsFullyAuthenticatedUser() : Authentication
+ setDefaultAdminUserName(c String) : void
+ setDefaultGuestUserName(c String) : void
+ setFullAuthentication(i Authentication) : Authentication
+ setFullyAuthenticatedUser(c String) : Authentication
+ setMtEnabled(boolean) : void
+ setRunAsUser(c String) : Authentication
+ setRunAsUserSystem() : Authentication
- getAuthenticationToken(c String, i UserDetails) : UsernamePasswordAuthenticationToken
- getDefaultUserDetails(c String) : UserDetails
- getUserName(i Authentication) : String
- setFullyAuthenticatedUser(c String, i UserDetails) : Authentication
~ setRunAsAuthentication(i Authentication) : Authentication
~ setRunAsUser(c String, i UserDetails) : Authentication
}
class org.alfresco.repo.security.authentication.AuthenticationException {
- serialVersionUID : long
~ diagnostic : c AuthenticationDiagnostic
--
+ AuthenticationException(c String)
+ AuthenticationException(c String, c AuthenticationDiagnostic)
+ AuthenticationException(c String, c AuthenticationDiagnostic, c Object;, c Throwable)
+ AuthenticationException(c String, c AuthenticationDiagnostic, c Throwable)
+ AuthenticationException(c String, c Object;)
+ AuthenticationException(c String, c Object;, c AuthenticationDiagnostic)
+ AuthenticationException(c String, c Object;, c Throwable)
+ AuthenticationException(c String, c Throwable)
+ getDiagnostic() : AuthenticationDiagnostic
}
class org.alfresco.error.AlfrescoRuntimeException {
- MESSAGE_DELIMITER : c String
- errorCounter : c AtomicInteger
- msgId : c String
- msgParams : c Object;
- serialVersionUID : long
--
+ AlfrescoRuntimeException(c String)
+ AlfrescoRuntimeException(c String, c Object;)
+ AlfrescoRuntimeException(c String, c Object;, c Throwable)
+ AlfrescoRuntimeException(c String, c Throwable)
+ create(c String, c Object;) : AlfrescoRuntimeException
+ create(c Throwable, c String, c Object;) : AlfrescoRuntimeException
+ getMsgId() : String
+ getMsgParams() : Object;
+ getNumericalId() : String
+ getRootCause() : Throwable
+ makeRuntimeException(c Throwable, c String, c Object;) : RuntimeException
- buildErrorLogNumber(c String) : String
- padInt(c StringBuilder, int, int) : void
- resolveMessage(c String, c Object;) : String
}
class org.alfresco.repo.security.authentication.AuthenticationDiagnostic {
+ STEP_KEY_LDAP_AUTHENTICATION : c String
+ STEP_KEY_LDAP_CONNECTED : c String
+ STEP_KEY_LDAP_CONNECTING : c String
+ STEP_KEY_LDAP_FORMAT_USER : c String
+ STEP_KEY_LDAP_LOOKEDUP_USER : c String
+ STEP_KEY_LDAP_LOOKUP_USER : c String
+ STEP_KEY_LDAP_SEARCH : c String
+ STEP_KEY_VALIDATION : c String
+ STEP_KEY_VALIDATION_AUTHENTICATOR_NOT_ACTIVE : c String
+ STEP_KEY_VALIDATION_AUTHENTICATOR_NOT_FOUND : c String
- serialVersionUID : long
- steps : List< AuthenticationStep>
--
+ AuthenticationDiagnostic()
+ addStep(c String, boolean) : void
+ addStep(c String, boolean, c Object;) : void
+ addStep(i AuthenticationStep) : void
+ getSteps() : List
}
class org.alfresco.repo.security.authentication.AuthenticationStepImpl {
- serialVersionUID : long
~ args : c Object;
~ key : c String
~ success : boolean
--
+ AuthenticationStepImpl(c String)
+ getArgs() : Object;
+ getKey() : String
+ getMessage() : String
+ isSuccess() : boolean
+ toString() : String
}
interface net.sf.acegisecurity.context.Context {
--
+ validate() : void
}
class org.alfresco.repo.security.authentication.AlfrescoSecureContextImpl {
- effectiveAuthentication : i Authentication
- realAuthentication : i Authentication
- serialVersionUID : long
--
+ AlfrescoSecureContextImpl()
+ equals(c Object) : boolean
+ getAuthentication() : Authentication
+ getEffectiveAuthentication() : Authentication
+ getRealAuthentication() : Authentication
+ hashCode() : int
+ setAuthentication(i Authentication) : void
+ setEffectiveAuthentication(i Authentication) : void
+ setRealAuthentication(i Authentication) : void
+ toString() : String
+ validate() : void
}
enum org.alfresco.repo.security.authentication.InMemoryTicketComponentImpl$ExpiryMode {
+ AFTER_FIXED_TIME : c InMemoryTicketComponentImpl$ExpiryMode
+ AFTER_INACTIVITY : c InMemoryTicketComponentImpl$ExpiryMode
+ DO_NOT_EXPIRE : c InMemoryTicketComponentImpl$ExpiryMode
--
- InMemoryTicketComponentImpl$ExpiryMode()
+ valueOf(c String) : InMemoryTicketComponentImpl$ExpiryMode
+ values() : InMemoryTicketComponentImpl$ExpiryMode;
}
interface org.alfresco.repo.security.authentication.TicketComponent {
--
+ clearCurrentTicket() : void
+ countTickets(boolean) : int
+ getAuthorityForTicket(c String) : String
+ getCurrentTicket(c String, boolean) : String
+ getNewTicket(c String) : String
+ getUseSingleTicketPerUser() : boolean
+ getUsersWithTickets(boolean) : Set
+ invalidateTicketById(c String) : void
+ invalidateTicketByUser(c String) : void
+ invalidateTickets(boolean) : int
+ validateTicket(c String) : String
}
' Relations
org.alfresco.repo.security.authentication.InMemoryTicketComponentImpl "1" o-left- "1" org.alfresco.repo.cache.SimpleCache : ticketsCache: SimpleCache< String, InMemoryTicketComponentImpl$Ticket>
org.alfresco.repo.security.authentication.AlfrescoSecureContextImpl "1" o-left- "1" net.sf.acegisecurity.Authentication : realAuthentication: i Authentication
org.alfresco.repo.security.authentication.InMemoryTicketComponentImpl$Ticket "1" o-left- "1" org.alfresco.repo.security.authentication.InMemoryTicketComponentImpl$ExpiryMode : expires: c InMemoryTicketComponentImpl$ExpiryMode
org.alfresco.error.AlfrescoRuntimeException "1" o-left- "1" java.util.concurrent.atomic.AtomicInteger : errorCounter: c AtomicInteger
org.alfresco.repo.security.authentication.InMemoryTicketComponentImpl$Ticket "1" o-left- "1" org.alfresco.service.cmr.repository.datatype.Duration : testDuration: c Duration
org.alfresco.repo.security.authentication.InMemoryTicketComponentImpl$Ticket "1" o-left- "1" org.alfresco.service.cmr.repository.datatype.Duration : validDuration: c Duration
org.alfresco.repo.security.authentication.AuthenticationUtil "1" o-left- "1" org.apache.commons.logging.Log : s_logger: i Log
org.alfresco.repo.security.authentication.InMemoryTicketComponentImpl "1" o-left- "1" org.alfresco.repo.security.authentication.InMemoryTicketComponentImpl$ExpiryMode : expiryMode: c InMemoryTicketComponentImpl$ExpiryMode
org.alfresco.repo.security.authentication.AuthenticationException "1" o-left- "1" org.alfresco.repo.security.authentication.AuthenticationDiagnostic : diagnostic: c AuthenticationDiagnostic
org.alfresco.repo.security.authentication.InMemoryTicketComponentImpl$Ticket "1" o-left- "1" java.util.Date : expiryDate: c Date
org.alfresco.repo.security.authentication.InMemoryTicketComponentImpl "1" o-left- "1" org.alfresco.service.cmr.repository.datatype.Duration : validDuration: c Duration
org.alfresco.repo.security.authentication.AlfrescoSecureContextImpl "1" o-left- "1" net.sf.acegisecurity.Authentication : effectiveAuthentication: i Authentication
org.alfresco.repo.security.authentication.AuthenticationException -up|> org.alfresco.error.AlfrescoRuntimeException
org.alfresco.repo.security.authentication.TicketExpiredException -up|> org.alfresco.repo.security.authentication.AuthenticationException
"net.sf.acegisecurity.context.Context" -() Serializable
"org.alfresco.repo.security.authentication.AuthenticationStepImpl" -() Serializable
org.alfresco.repo.security.authentication.AlfrescoSecureContext ..up|> net.sf.acegisecurity.context.security.SecureContext
org.alfresco.repo.security.authentication.AuthenticationStepImpl ..up|> org.alfresco.repo.security.authentication.AuthenticationStep
org.alfresco.repo.security.authentication.InMemoryTicketComponentImpl ..up|> org.alfresco.repo.security.authentication.TicketComponent
net.sf.acegisecurity.context.security.SecureContext ..up|> net.sf.acegisecurity.context.Context
"org.alfresco.repo.security.authentication.AuthenticationDiagnostic" -() Serializable
"org.alfresco.repo.security.authentication.InMemoryTicketComponentImpl$Ticket" -() Serializable
org.alfresco.repo.security.authentication.AlfrescoSecureContextImpl ..up|> org.alfresco.repo.security.authentication.AlfrescoSecureContext
org.alfresco.repo.security.authentication.AuthenticationUtil ..up|> org.springframework.beans.factory.InitializingBean
' Notes
@enduml

Binary file not shown.

After

Width:  |  Height:  |  Size: 369 KiB

View File

@@ -0,0 +1,344 @@
@startuml
left to right direction
' Generated using https://github.com/juanmf/Java2PlantUML
' Participants
class org.alfresco.error.AlfrescoRuntimeException {
- MESSAGE_DELIMITER : c String
- errorCounter : c AtomicInteger
- msgId : c String
- msgParams : c Object;
- serialVersionUID : long
--
+ AlfrescoRuntimeException(c String)
+ AlfrescoRuntimeException(c String, c Object;)
+ AlfrescoRuntimeException(c String, c Object;, c Throwable)
+ AlfrescoRuntimeException(c String, c Throwable)
+ create(c String, c Object;) : AlfrescoRuntimeException
+ create(c Throwable, c String, c Object;) : AlfrescoRuntimeException
+ getMsgId() : String
+ getMsgParams() : Object;
+ getNumericalId() : String
+ getRootCause() : Throwable
+ makeRuntimeException(c Throwable, c String, c Object;) : RuntimeException
- buildErrorLogNumber(c String) : String
- padInt(c StringBuilder, int, int) : void
- resolveMessage(c String, c Object;) : String
}
class org.alfresco.service.cmr.security.NoSuchPersonException {
- serialVersionUID : long
- userName : c String
--
+ NoSuchPersonException(c String)
+ getUserName() : String
}
interface org.alfresco.service.cmr.security.OwnableService {
+ NO_OWNER : c String
--
+ getOwner(c NodeRef) : String
+ hasOwner(c NodeRef) : boolean
+ setOwner(c NodeRef, c String) : void
+ takeOwnership(c NodeRef) : void
}
class org.alfresco.service.cmr.security.PersonService$PersonInfo {
- firstName : c String
- lastName : c String
- nodeRef : c NodeRef
- userName : c String
--
+ PersonService$PersonInfo(c NodeRef, c String, c String, c String)
+ getFirstName() : String
+ getLastName() : String
+ getNodeRef() : NodeRef
+ getUserName() : String
}
enum org.alfresco.service.cmr.security.AccessStatus {
+ ALLOWED : c AccessStatus
+ DENIED : c AccessStatus
+ UNDETERMINED : c AccessStatus
--
- AccessStatus()
+ valueOf(c String) : AccessStatus
+ values() : AccessStatus;
}
interface org.alfresco.service.cmr.security.AuthenticationService {
--
+ authenticate(c String, class [C) : void
+ authenticateAsGuest() : void
+ authenticationExists(c String) : boolean
+ clearCurrentSecurityContext() : void
+ getAuthenticationEnabled(c String) : boolean
+ getCurrentTicket() : String
+ getCurrentUserName() : String
+ getDefaultAdministratorUserNames() : Set
+ getDefaultGuestUserNames() : Set
+ getDomains() : Set
+ getDomainsThatAllowUserCreation() : Set
+ getDomainsThatAllowUserDeletion() : Set
+ getDomiansThatAllowUserPasswordChanges() : Set
+ getNewTicket() : String
+ guestUserAuthenticationAllowed() : boolean
+ invalidateTicket(c String) : void
+ invalidateUserSession(c String) : void
+ isCurrentUserTheSystemUser() : boolean
+ validate(c String) : void
}
interface org.alfresco.service.cmr.security.AuthorityService {
+ ZONE_APP_DEFAULT : c String
+ ZONE_APP_SHARE : c String
+ ZONE_AUTH_ALFRESCO : c String
+ ZONE_AUTH_EXT_PREFIX : c String
--
+ addAuthority( Collection< String>, c String) : void
+ addAuthority(c String, c String) : void
+ addAuthorityToZones(c String, Set< String>) : void
+ authorityExists(c String) : boolean
+ countGroups() : long
+ countUsers() : long
+ createAuthority(c AuthorityType, c String) : String
+ createAuthority(c AuthorityType, c String, c String, Set< String>) : String
+ deleteAuthority(c String) : void
+ deleteAuthority(c String, boolean) : void
+ findAuthorities(c AuthorityType, c String, boolean, c String, c String) : Set
+ getAllAuthorities(c AuthorityType) : Set
+ getAllAuthoritiesInZone(c String, c AuthorityType) : Set
+ getAllRootAuthorities(c AuthorityType) : Set
+ getAllRootAuthoritiesInZone(c String, c AuthorityType) : Set
+ getAuthorities() : Set
+ getAuthorities(c AuthorityType, c String, c String, boolean, boolean, c PagingRequest) : PagingResults
+ getAuthoritiesForUser(c String) : Set
+ getAuthoritiesInfo(c AuthorityType, c String, c String, c String, boolean, c PagingRequest) : PagingResults
+ getAuthorityDisplayName(c String) : String
+ getAuthorityNodeRef(c String) : NodeRef
+ getAuthorityZones(c String) : Set
+ getContainedAuthorities(c AuthorityType, c String, boolean) : Set
+ getContainingAuthorities(c AuthorityType, c String, boolean) : Set
+ getContainingAuthoritiesInZone(c AuthorityType, c String, c String, i AuthorityService$AuthorityFilter, int) : Set
+ getDefaultZones() : Set
+ getName(c AuthorityType, c String) : String
+ getOrCreateZone(c String) : NodeRef
+ getShortName(c String) : String
+ getZone(c String) : NodeRef
+ hasAdminAuthority() : boolean
+ hasGuestAuthority() : boolean
+ isAdminAuthority(c String) : boolean
+ isGuestAuthority(c String) : boolean
+ removeAuthority(c String, c String) : void
+ removeAuthorityFromZones(c String, Set< String>) : void
+ setAuthorityDisplayName(c String, c String) : void
}
enum org.alfresco.service.cmr.security.AuthorityType {
+ ADMIN : c AuthorityType
+ EVERYONE : c AuthorityType
+ GROUP : c AuthorityType
+ GUEST : c AuthorityType
+ OWNER : c AuthorityType
+ ROLE : c AuthorityType
+ USER : c AuthorityType
+ WILDCARD : c AuthorityType
--
- AuthorityType()
+ equals(c String) : boolean
+ getAuthorityType(c String) : AuthorityType
+ getFixedString() : String
+ getOrderPosition() : int
+ getPrefixString() : String
+ isFixedString() : boolean
+ isPrefixed() : boolean
+ valueOf(c String) : AuthorityType
+ values() : AuthorityType;
}
interface org.alfresco.service.cmr.security.PersonService {
--
+ countPeople() : int
+ createMissingPeople() : boolean
+ createPerson( Map< QName, Serializable>) : NodeRef
+ createPerson( Map< QName, Serializable>, Set< String>) : NodeRef
+ deletePerson(c NodeRef) : void
+ deletePerson(c NodeRef, boolean) : void
+ deletePerson(c String) : void
+ getAllPeople() : Set
+ getMutableProperties() : Set
+ getPeople( List< Pair< QName, String>>, boolean, List< Pair< QName, Boolean>>, c PagingRequest) : PagingResults
+ getPeople(c String, List< QName>, List< Pair< QName, Boolean>>, c PagingRequest) : PagingResults
+ getPeople(c String, List< QName>, Set< QName>, Set< QName>, boolean, List< Pair< QName, Boolean>>, c PagingRequest) : PagingResults
+ getPeopleContainer() : NodeRef
+ getPeopleFilteredByProperty(c QName, i Serializable, int) : Set
+ getPerson(c NodeRef) : PersonService$PersonInfo
+ getPerson(c String) : NodeRef
+ getPerson(c String, boolean) : NodeRef
+ getPersonOrNull(c String) : NodeRef
+ getUserIdentifier(c String) : String
+ getUserNamesAreCaseSensitive() : boolean
+ isEnabled(c String) : boolean
+ isMutable() : boolean
+ notifyPerson(c String, c String) : void
+ personExists(c String) : boolean
+ setCreateMissingPeople(boolean) : void
+ setPersonProperties(c String, Map< QName, Serializable>) : void
+ setPersonProperties(c String, Map< QName, Serializable>, boolean) : void
}
interface org.alfresco.service.cmr.security.PublicServiceAccessService {
--
+ hasAccess(c String, c String, c Object;) : AccessStatus
}
interface org.alfresco.service.cmr.security.MutableAuthenticationService {
--
+ createAuthentication(c String, class [C) : void
+ deleteAuthentication(c String) : void
+ isAuthenticationCreationAllowed() : boolean
+ isAuthenticationMutable(c String) : boolean
+ setAuthentication(c String, class [C) : void
+ setAuthenticationEnabled(c String, boolean) : void
+ updateAuthentication(c String, class [C, class [C) : void
}
class org.alfresco.service.cmr.security.PermissionContext {
- additionalContext : Map< String, Object>
- aspects : HashSet< QName>
- dynamicAuthorityAssignment : Map< String, Set< String>>
- properties : Map< QName, Serializable>
- storeAcl : c Long
- type : c QName
--
+ PermissionContext(c QName)
+ addDynamicAuthorityAssignment(c String, c String) : void
+ getAdditionalContext() : Map
+ getAspects() : HashSet
+ getDynamicAuthorityAssignment() : Map
+ getProperties() : Map
+ getStoreAcl() : Long
+ getType() : QName
+ setStoreAcl(c Long) : void
}
interface org.alfresco.repo.security.permissions.PermissionCheckValue {
--
+ getNodeRef() : NodeRef
}
interface org.alfresco.service.cmr.security.PermissionService {
+ ADD_CHILDREN : c String
+ ADMINISTRATOR_AUTHORITY : c String
+ ALL_AUTHORITIES : c String
+ ALL_PERMISSIONS : c String
+ ASPECTS : c String
+ CANCEL_CHECK_OUT : c String
+ CHANGE_PERMISSIONS : c String
+ CHECK_IN : c String
+ CHECK_OUT : c String
+ CONSUMER : c String
+ CONTRIBUTOR : c String
+ COORDINATOR : c String
+ CREATE_ASSOCIATIONS : c String
+ CREATE_CHILDREN : c String
+ DELETE : c String
+ DELETE_ASSOCIATIONS : c String
+ DELETE_CHILDREN : c String
+ DELETE_NODE : c String
+ EDITOR : c String
+ EXECUTE : c String
+ EXECUTE_CONTENT : c String
+ FULL_CONTROL : c String
+ GROUP_PREFIX : c String
+ GUEST_AUTHORITY : c String
+ LINK_CHILDREN : c String
+ LOCK : c String
+ LOCK_OWNER_AUTHORITY : c String
+ OWNER_AUTHORITY : c String
+ PROPERTIES : c String
+ READ : c String
+ READ_ASSOCIATIONS : c String
+ READ_CHILDREN : c String
+ READ_CONTENT : c String
+ READ_PERMISSIONS : c String
+ READ_PROPERTIES : c String
+ ROLE_PREFIX : c String
+ SET_OWNER : c String
+ TAKE_OWNERSHIP : c String
+ UNLOCK : c String
+ WRITE : c String
+ WRITE_CONTENT : c String
+ WRITE_PROPERTIES : c String
--
+ clearPermission(c NodeRef, c String) : void
+ clearPermission(c StoreRef, c String) : void
+ deletePermission(c NodeRef, c String, c String) : void
+ deletePermission(c StoreRef, c String, c String) : void
+ deletePermissions(c NodeRef) : void
+ deletePermissions(c StoreRef) : void
+ getAllAuthorities() : String
+ getAllPermission() : String
+ getAllSetPermissions(c NodeRef) : Set
+ getAllSetPermissions(c StoreRef) : Set
+ getAuthorisations() : Set
+ getInheritParentPermissions(c NodeRef) : boolean
+ getOwnerAuthority() : String
+ getPermissions(c NodeRef) : Set
+ getReaders(c Long) : Set
+ getReadersDenied(c Long) : Set
+ getSettablePermissions(c NodeRef) : Set
+ getSettablePermissions(c QName) : Set
+ hasPermission(c Long, c PermissionContext, c String) : AccessStatus
+ hasPermission(c NodeRef, c String) : AccessStatus
+ hasReadPermission(c NodeRef) : AccessStatus
+ setInheritParentPermissions(c NodeRef, boolean) : void
+ setInheritParentPermissions(c NodeRef, boolean, boolean) : void
+ setPermission(c NodeRef, c String, c String, boolean) : void
+ setPermission(c StoreRef, c String, c String, boolean) : void
}
interface org.alfresco.service.cmr.security.AuthorityService$AuthorityFilter {
--
+ includeAuthority(c String) : boolean
}
interface org.alfresco.service.cmr.security.AccessPermission {
--
+ getAccessStatus() : AccessStatus
+ getAuthority() : String
+ getAuthorityType() : AuthorityType
+ getPermission() : String
+ getPosition() : int
+ isInherited() : boolean
+ isSetDirectly() : boolean
}
class org.alfresco.repo.security.person.PersonException {
- serialVersionUID : long
--
+ PersonException(c String)
+ PersonException(c String, c Object;)
+ PersonException(c String, c Object;, c Throwable)
+ PersonException(c String, c Throwable)
}
' Relations
org.alfresco.service.cmr.security.PersonService$PersonInfo "1" o-left- "1" org.alfresco.service.cmr.repository.NodeRef : nodeRef: c NodeRef
org.alfresco.error.AlfrescoRuntimeException "1" o-left- "1" java.util.concurrent.atomic.AtomicInteger : errorCounter: c AtomicInteger
org.alfresco.service.cmr.security.PermissionContext "1" o-left- "*" org.alfresco.service.namespace.QName : aspects: HashSet< QName>
org.alfresco.service.cmr.security.PermissionContext "1" o-left- "1" org.alfresco.service.namespace.QName : type: c QName
org.alfresco.service.cmr.security.PermissionContext "1" o-left- "*" org.alfresco.service.namespace.QName : properties: Map< QName, Serializable>
org.alfresco.repo.security.person.PersonException -up|> org.alfresco.error.AlfrescoRuntimeException
org.alfresco.service.cmr.security.NoSuchPersonException -up|> org.alfresco.repo.security.person.PersonException
org.alfresco.service.cmr.security.MutableAuthenticationService ..up|> org.alfresco.service.cmr.security.AuthenticationService
org.alfresco.service.cmr.security.PersonService$PersonInfo ..up|> org.alfresco.repo.security.permissions.PermissionCheckValue
' Notes
@enduml

View File

@@ -0,0 +1,8 @@
# Repository -> Infrastructure
## Sub-components
* [ ] [Module Framework](./module-framework)
* [ ] [Cluster](./cluster)
* [ ] [Policies and Behaviours](./policies-and-behaviours)
* [ ] [Multi-tenancy](./multi-tenancy)

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -0,0 +1,30 @@
@startuml
title Tenant Service (aws)
interface TenantService
interface TenantServiceSPI
class Tenant
class TenantServiceDDBImpl
class TenantServiceImpl
class TenantServiceLambda
Tenant <.. TenantService : uses
Tenant <.. TenantServiceDDBImpl : uses
Tenant <.. TenantServiceImpl : uses
Tenant <.. TenantServiceSPI : uses
TenantServiceDDBImpl <.. TenantServiceLambda : uses
TenantServiceImpl <.. TenantServiceLambda : uses
TenantServiceSPI <.. TenantServiceImpl : uses
TenantServiceSPI <.. TenantServiceLambda : uses
TenantService <|.. TenantServiceImpl : implements
TenantServiceSPI <|.. TenantServiceDDBImpl : implements
center footer © 2016 Alfresco Software Inc. all rights reserved \n Generated from PlantUML
@enduml

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

View File

@@ -0,0 +1,89 @@
@startuml
left to right direction
interface com.alfresco.services.lambda.TenantServiceSPI {
--
+ createTenant(c String, c String) : Tenant
+ deleteTenant(c String) : boolean
+ getTenant(c String) : Tenant
+ getTenantSchema(c String) : String
+ updateTenant(c Tenant) : boolean
}
interface com.alfresco.services.lambda.TenantService {
--
+ createTenant(c String, c String) : Tenant
+ deleteTenant(c String) : boolean
+ getTenant(c String) : Tenant
+ getTenantSchema(c String) : String
+ updateTenant(c Tenant) : boolean
}
class com.alfresco.services.lambda.TenantServiceDDBImpl {
- regions : c Regions
- tableName : c String
--
+ TenantServiceDDBImpl()
+ createTenant(c String, c String) : Tenant
+ deleteTenant(c String) : boolean
+ getTenant(c String) : Tenant
+ getTenantSchema(c String) : String
+ updateTenant(c Tenant) : boolean
- log(c String) : void
}
class com.alfresco.services.lambda.TenantServiceLambda {
--
+ TenantServiceLambda()
+ handleRequest(c Tenant, i Context) : Tenant
}
class com.alfresco.services.lambda.TenantServiceImpl {
- tenantServiceSPI : i TenantServiceSPI
--
+ TenantServiceImpl(i TenantServiceSPI)
+ createTenant(c String, c String) : Tenant
+ deleteTenant(c String) : boolean
+ getTenant(c String) : Tenant
+ getTenantSchema(c String) : String
+ updateTenant(c Tenant) : boolean
}
class com.alfresco.services.lambda.Tenant {
- method : c String
- schema : c String
- spiProvider : c String
- tenantId : c String
- user : c String
--
+ Tenant()
+ getMethod() : String
+ getSchema() : String
+ getSpiProvider() : String
+ getTenantId() : String
+ getUser() : String
+ setMethod(c String) : void
+ setSchema(c String) : void
+ setSpiProvider(c String) : void
+ setTenantId(c String) : void
+ setUser(c String) : void
}
interface com.amazonaws.services.lambda.runtime.RequestHandler <I extends c Object, O extends c Object> {
--
+ handleRequest(I, i Context) : Object
}
' Relations
com.alfresco.services.lambda.TenantServiceImpl "1" o-left- "1" com.alfresco.services.lambda.TenantServiceSPI : tenantServiceSPI: i TenantServiceSPI
com.alfresco.services.lambda.TenantServiceDDBImpl "1" o-left- "1" com.amazonaws.regions.Regions : regions: c Regions
com.alfresco.services.lambda.TenantServiceImpl ..up|> com.alfresco.services.lambda.TenantService
com.alfresco.services.lambda.TenantServiceDDBImpl ..up|> com.alfresco.services.lambda.TenantServiceSPI
com.alfresco.services.lambda.TenantServiceLambda ..up|> com.amazonaws.services.lambda.runtime.RequestHandler
' Notes
@enduml

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

View File

@@ -0,0 +1,51 @@
@startuml
title Tenant Component (nosql)
skinparam linetype ortho
interface ApiConstants
interface TenantService
interface TenantServiceSPI
class AbstractRestResource {
#switchTenant()
}
class AbstractServiceClient
class An2ApiException
class CreateTenantPojo
class Cx1TenantService
class GetTenantPojo
class InitializingBean
class NoSuchTenantException
class TenantExistsException
class TenantService
class TenantServiceClient
class TenantServiceImpl
class TenantServiceIT
class TenantServiceRestV1
class VersionCheckException
AbstractRestResource <|-- TenantServiceRestV1 : extends
AbstractServiceClient <|-- TenantServiceClient : extends
An2ApiException <|-- NoSuchTenantException : extends
An2ApiException <|-- TenantExistsException : extends
An2ApiException <|-- VersionCheckException : extends
ApiConstants <|.. TenantServiceImpl : uses
CreateTenantPojo <.. TenantServiceClient : uses
CreateTenantPojo <.. TenantServiceRestV1 : uses
GetTenantPojo <.. TenantServiceClient : uses
GetTenantPojo <.. TenantServiceRestV1 : uses
InitializingBean <|.. TenantServiceImpl : implements
NoSuchTenantException <.. AbstractRestResource : uses
TenantService <.. AbstractRestResource : uses
TenantService <|.. TenantServiceClient : implements
TenantService <|.. TenantServiceImpl : implements
TenantService <|.. TenantServiceRestV1 : uses
TenantServiceClient <.. TenantServiceIT : uses
TenantServiceSPI <.. Cx1TenantService : implements
TenantServiceSPI <.. TenantServiceImpl : uses
@enduml

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

View File

@@ -0,0 +1,39 @@
@startuml
title Tenant Service (nosql)
skinparam linetype ortho
node "Service\nClient" as SC #E0F2F1
node "API Gateway" as APIG #EEEEEE {
node "/tenants Resource" as TR #EFEBE9 {
node "method POST" as POST #FBE9E7 {
node "POST \nbody mapping template" #DCEDC8
}
node "method PUT" as PUT #FBE9E7{
node "PUT \nbody mapping template" #DCEDC8
}
node "method GET" as GET #FBE9E7
node "method DELETE" As DELETE #FBE9E7
}
}
node "Tenant Service \nLambda Function" as L #FFD54F
database "DynamoDB Database\nalf-data-tenants" as DB #80D8FF
node "DynamoDB\nClient" As DDBC #B3E5FC
node "DynamoDB" As DDB #B2EBF2
SC->TR:https
POST->L:JSON
PUT->L:JSON
GET->L:URL {tenant-id}
DELETE->L:URL {tenant-id}
L->DDBC
DDBC->DDB:reads/writes
DDB->DB:reads/writes
@enduml

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

View File

@@ -0,0 +1,86 @@
@startuml
Title Tenant Deployment\nDesign Options
skinparam nodeBorderColor black
left to right direction
node "AWS" as 2G #EEEEEE {
node "Tenant\nService" as 2T #white
node "Alfresco\nControl Architecture" as 2CA #white
node "Customer A" as 2C1 #FFCDD2 {
node "Alfresco 5.1" as 2A1 #E6EE9C {
node "Tenant" as 2T1
}
}
node "Customer B" as 2C2 #FFCDD2{
node "Alfresco 5.1" as 2A2 #E6EE9C {
node "Tenant" as 2T2
}
}
node "Customer C" as 2C3 #FFCDD2{
node "Alfresco 5.1" as 2A3 #E6EE9C {
node "Tenant" as 2T3
}
}
node "Customers D and E" as CDE #FFCDD2{
node "Repository Next" as 2B #B3E5FC
node "Tenant\n(Customer D)" as 2CD
node "Tenant\n(Customer E)" as 2CE
}
}
2CA=>2C1
2CA=>2C2
2CA=>2C3
2CA=>CDE
2CA=>2T
2T=>2CD
2T=>2CE
2B=>2T
node "AWS" as G #EEEEEE {
node "Alfresco\nControl Architecture" as CA #white
node "Customer A" as C1 #FFCDD2 {
node "Alfresco 5.1" as A1 #E6EE9C {
node "Tenant" as T1
}
}
node "Customer B" as C2 #FFCDD2{
node "Alfresco 5.1" as A2 #E6EE9C {
node "Tenant" as T2
}
}
node "Customer C" as C3 #FFCDD2{
node "Alfresco 5.1" as A3 #E6EE9C {
node "Tenant" as T3
}
}
node "Customer D" as C4 #FFCDD2 {
node "Tenant\nService" as TC1 #white
node "Tenant" as T5
node "Repository Next" as B1 #B3E5FC
}
node "Customer E" as C5 #FFCDD2 {
node "Tenant\nService" as TC2 #white
node "Tenant" as T6
node "Tenant" as T7
node "Tenant" as T8
node "Repository Next" as B2 #B3E5FC
}
}
TC1=>T5
TC2=>T6
TC2=>T7
TC2=>T8
CA=>C1
CA=>C2
CA=>C3
CA=>C4
CA=>C5
B2=>TC2
B1=>TC1
@enduml

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

View File

@@ -0,0 +1,69 @@
@startuml
Title: Create a Tenant (NoSQL - AWS)
actor "User" as U
participant "Tenant Console" as TC
participant "API Gateway" as APIG
participant "TenantServiceLambda" as TSL
participant "TenantServiceImpl" as TSI
participant "DDbTenantService" as DDTS
participant "Dynamo DB Client" as DDBC
database "DynamoDB" as DDB
U->TC:create Tenant
note right
What the Tenant Console
is is TBD. It
may be part of the
AWS Control Architecture
or a Beowulf Admin
Console
end note
activate TC
TC->APIG: HTTP POST {<API Gateway host:port>/public/an2/v1/tenants\nTenant Object(JSON)
activate APIG
APIG->TSL: handler(serialized Tenant Object(JSON))
note right
The HTTP Verb and UserInfo is
passed to the Tenant Service
Lambda in the JSON object.
Tenant Service Lambda selects
the TenantServiceImpl method
to call based on the HTTP Verb
end note
activate TSL
TSL->TSI: createTenant()
note right
Unsolved Design Challenge #1:
How to supply a configuration
to allow alternative
implementations at
runtime. In current
form, the TenantServiceSPI
passed to TenantServiceImpl
is set in an API Gateway
template. That would allow
us to specify a different
provider of the SPI using,
for example, an HTTP parm
end note
activate TSI
TSI->DDTS
activate DDTS
DDTS->DDBC: table.putItem(new Item().withPrimaryKey("tenant-id")
activate DDBC
DDBC->DDB: store the tenant record
DDB->DDBC: success
deactivate DDB
DDBC->DDTS:success
deactivate DDBC
DDTS->TSI:success
deactivate DDTS
TSI->TSL: success
deactivate TSI
TSL->TC: HTTP 200 OK
deactivate TSL
TC->>U:success message
deactivate TC
@enduml

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

View File

@@ -0,0 +1,60 @@
@startuml
Title: Create a Tenant (NoSQL)
actor "User" as U
participant "Tenant Console" as TC
participant "TenantServiceClient" as TCL
participant "TenantServiceRestV1" as TRS
participant "TenantServiceImpl" as TSI
participant "Cx1TenantService" as CTS
participant "cassandra-driver-core-2.2.0-rc1" as DDC
database "Cassandra" as C
U->TC:create Tenant
note right
The Tenant Console
may be part of the
Admin Console
end note
activate TC
TC->TCL:createTenant()
activate TCL
TCL->TCL: find server from super\nAbstractServiceClient
TCL->TRS: HTTP POST {tenantCtx}/public/an2/v1/tenants\nCreateTenantPojo(JSON)
activate TRS
note right
This is wired
to the endpoint
using Jersey
end note
TRS->TSI: create(TenantServiceSPI=Cx1TenantService)
activate TSI
TSI->CTS: createTenant()
note right
The Cassandra
implementation can
be replaced by
other implementations
(e.g. Dynamo DB)
through Spring config
end note
activate CTS
CTS->DDC: insert into Table alf_data_tenants
activate DDC
DDC->C: store the tenant record
C->DDC:success
deactivate C
DDC->CTS: success
deactivate DDC
CTS->TSI:success
deactivate CTS
TSI->TRS: success
deactivate TSI
TRS->TCL: HTTP 200 OK
deactivate TRS
TCL->TC: success
deactivate TCL
TC->>U:success message
deactivate TC
@enduml

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

View File

@@ -0,0 +1,175 @@
@startuml
title Policies: Policy Component (V4.2 onwards)
' Split into 2 pages
page 2x1
interface PolicyEndpointService #DDDDDD
class PolicyEndpointServiceImpl #DDDDDD implements PolicyEndpointService{
- policyComponent : PolicyComponent
- policyEndpointRegistry : registry
+ registerPolicyEndpoint(QName policy, String endpoint) : void
+ registerPolicyEndpoint(QName policy, QName typeOrAspect, String endpoint) : void
+ unregisterPolicyEndpoint(QName policy, String endpoint) : void
+ unregisterPolicyEndpoint(QName policy, QName typeOrAspect, String endpoint) : void
}
PolicyEndpointServiceImpl -> PolicyComponent
class PolicyEndpointRegistry #DDDDDD {
- QueuedBehaviour dynamicBehaviour
- Set<PolicyEndPointPolicyEndpoint> registrations
+ {static} PolicyEndpointRegistry getInstance()
+ reload(long fromTime)
+ slip(String body, @Properties Map<String, Object> properties)
}
PolicyEndpointRegistry *- "1" QueuedBehaviour
PolicyEndpointServiceImpl -> PolicyEndpointRegistry
class PolicyEndpoint <<immutable>> #DDDDDD {
- QName policy
- QName typeOrAspect
- String endpoint
- long modified
- boolean active
+ getKey()
}
PolicyEndpointRegistry *- PolicyEndpoint
interface Policy
interface ClassPolicy extends Policy
interface OnCreateNodePolicy extends ClassPolicy {
+QNAME: http://www.alfresco.org:onCreateNode
+onCreateNode(ChildAssociationRef childAssocRef)
}
interface AssociationPolicy extends Policy
interface OnCreateChildAssociationPolicy extends AssociationPolicy {
+QNAME: http://www.alfresco.org:onCreateChildAssociation
+onCreateChildAssociation(ChildAssociationRef childAssocRef, boolean isNewNode)
}
enum NotificationFrequency {
EVERY_EVENT,
FIRST_EVENT,
TRANSACTION_COMMIT
}
interface Behaviour {
+ <T> getInterface(Class<T> policy) : T
+ disable()
+ enable()
+ isEnabled() : boolean
+ getNotificationFrequency() : NotificationFrequency
}
class BaseBehaviour implements Behaviour {
# proxies : Map<Class, Object>
# frequency : NotificationFrequency
- disabled : StackThreadLocal
+ disable()
+ enable()
+ isEnabled() : boolean
+ getNotificationFrequency() : NotificationFrequency
}
class JavaBehaviour extends BaseBehaviour {
+ <T> getInterface(Class<T> policy) : T
# <T> getInvocationHandler(Object instance, String method, Class<T> policyIF) : InvocationHandler
}
class QueuedBehaviour <<V5.2>> #DDDDDD extends BaseBehaviour {
- ProducerTemplate queueTemplate
+ <T> getInterface(Class<T> policy) : T
# <T> getInvocationHandler(Object instance, String method, Class<T> policyIF) : InvocationHandler
}
class ScriptBehaviour extends BaseBehaviour
BaseBehaviour o- NotificationFrequency
interface BehaviourBinding {
+ generaliseBinding() : BehaviourBinding
}
class ClassBehaviourBinding implements BehaviourBinding {
+ generaliseBinding() : BehaviourBinding
}
class ClassFeatureBehaviourBinding extends ClassBehaviourBinding {
}
class ServiceBehaviourBinding implements BehaviourBinding {
+ generaliseBinding() : BehaviourBinding
}
class ClassPolicyDelegate<P extends ClassPolicy> {
- dictionary : DictionaryService
- factory : CachedPolicyFactory<ClassFeatureBehaviourBinding, P>
# ClassPolicyDelegate(DictionaryService dictionary, Class<P> policyClass, BehaviourIndex<ClassBehaviourBinding> index, long tryLockTimeout)
}
class AssociationPolicyDelegate<P extends AssociationPolicy> {
- dictionary : DictionaryService
- factory : CachedPolicyFactory<ClassFeatureBehaviourBinding, P>
# AssociationPolicyDelegate(DictionaryService dictionary, Class<P> policyClass, BehaviourIndex<ClassFeatureBehaviourBinding> index, long tryLockTimeout)
}
class PropertyPolicyDelegate<P extends PropertyPolicy> {
- dictionary : DictionaryService
- factory : CachedPolicyFactory<ClassFeatureBehaviourBinding, P>
# PropertyPolicyDelegate(DictionaryService dictionary, Class<P> policyClass, BehaviourIndex<ClassFeatureBehaviourBinding> index, long tryLockTimeout)
}
class PolicyFactory<B extends BehaviourBinding, P extends Policy> {
- index : BehaviourIndex~<B>
- policyClass : Class~<P>
- transactionHandlerFactory : TransactionInvocationHandlerFactory
- tenantService : TenantService
--
PolicyFactory(Class<P> policyClass, BehaviourIndex~<B> index)
--
}
class CachedPolicyFactory<B extends BehaviourBinding, P extends Policy> extends PolicyFactory
abstract class AbstractNodeServiceImpl implements NodeService {
- policyComponent : PolicyComponent
# dictionaryService : DictionaryService
# transactionService : TransactionService
# tenantService : TenantService
- onCreateNodeDelegate : ClassPolicyDelegate<OnCreateNodePolicy>
}
interface PolicyComponent {
+ <P extends ClassPolicy> registerClassPolicy(Class<P> policy) : ClassPolicyDelegate<P>
+ <P extends PropertyPolicy> registerPropertyPolicy(Class<P> policy) : PropertyPolicyDelegate<P>
+ <P extends AssociationPolicy> registerAssociationPolicy(Class<P> policy) : AssociationPolicyDelegate<P>
+ bindClassBehaviour(QName policy, QName className, Behaviour behaviour) : BehaviourDefinition<ClassBehaviourBinding>
+ bindClassBehaviour(QName policy, Object service, Behaviour behaviour) : BehaviourDefinition<ServiceBehaviourBinding>
+ bindPropertyBehaviour(QName policy, QName className, QName propertyName, Behaviour behaviour) : BehaviourDefinition<ClassFeatureBehaviourBinding>
+ bindPropertyBehaviour(QName policy, QName className, Behaviour behaviour) : BehaviourDefinition<ClassFeatureBehaviourBinding>
+ bindPropertyBehaviour(QName policy, Object service, Behaviour behaviour) : BehaviourDefinition<ServiceBehaviourBinding>
+ bindAssociationBehaviour(QName policy, QName className, QName assocName, Behaviour behaviour) : BehaviourDefinition<ClassFeatureBehaviourBinding>
+ bindAssociationBehaviour(QName policy, QName className, Behaviour behaviour) : BehaviourDefinition<ClassFeatureBehaviourBinding>
+ bindAssociationBehaviour(QName policy, Object service, Behaviour behaviour) : BehaviourDefinition<ServiceBehaviourBinding>
+ removeClassDefinition(BehaviourDefinition<ClassBehaviourBinding> definition) : void
}
class PolicyComponentImpl implements PolicyComponent {
- Map<QName, ClassBehaviourIndex<ClassBehaviourBinding>> classBehaviours
- Map<QName, ClassBehaviourIndex<ClassFeatureBehaviourBinding>> propertyBehaviours
- Map<QName, ClassBehaviourIndex<ClassFeatureBehaviourBinding>> associationBehaviours
+ PolicyComponentImpl(DictionaryService dictionary)
+ ... (all)
}
interface BehaviourFilter {
+ disableBehaviour() : void
+ disableBehaviour(QName className) : void
+ disableBehaviour(QName className, boolean includeSubClasses) : void
+ disableBehaviour(NodeRef nodeRef, QName className) : void
+ disableBehaviour(NodeRef nodeRef) : void
+ enableBehaviour() : void
+ enableBehaviour(QName className) : void
+ enableBehaviour(NodeRef nodeRef, QName className) : void
+ enableBehaviour(NodeRef nodeRef) : void
}
class BehaviourFilterImpl implements BehaviourFilter {
+ ... (all)
}
AbstractNodeServiceImpl -> PolicyComponentImpl
PolicyComponentImpl -> BehaviourFilterImpl
PolicyComponentImpl -> "creates" ClassPolicyDelegate
PolicyComponentImpl -> "creates" AssocationPolicyDelegate
PolicyComponentImpl -> "creates" PropertyPolicyDelegate
ClassPolicyDelegate --> "uses" PolicyFactory
AssociationPolicyDelegate --> "uses" PolicyFactory
PropertyPolicyDelegate --> "uses" PolicyFactory
PolicyFactory --> "creates" BehaviourBinding
@enduml

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

View File

@@ -0,0 +1,56 @@
@startuml
title Policies: Queued Policies (V5.2 Proposal)
actor user
database ActiveMQ as AMQ
database DB {
folder alf_policies
}
component "Client Application" {
component [Client Code]
component [ClientQueueReceiver]
component [Camel(Client App)]
}
user <--> [Client Code]
ClientQueueReceiver <-- [Camel(Client App)]
ClientQueueReceiver -> [Client Code]
[Camel(Client App)] <- AMQ
component "Alfresco One Platform" {
component Quartz as Q
component [Camel] as Camel
component [Policy ReST API] as PR
component PolicyService as PS {
[PolicyCheckJob] as PCJ
[PolicyMap] as PM <<Map>>
component Routes as R {
component ToQueue
component FromQueue
}
}
component PolicyComponent as PC {
component QueuedBehaviour as QB
}
component [Alfresco Services] as Services
component PolicyDAO as PD
}
[Client Code] <-> PR
Q --> PCJ
PCJ -> PM
PR --> PS
Services -> PC
PCJ --> PD
PD -> DB
QB -> ToQueue
ToQueue -> Camel
FromQueue <- Camel
FromQueue --> Services
Camel <-> AMQ
@enduml

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

View File

@@ -0,0 +1,37 @@
@startuml
Title: Policies: Register Policy Endpoint (V5.2 Proposal)
skinparam componentStyle uml2
actor "Client App" as Client
participant "Policy Endpoint Rest API" as API
participant "PolicyEndpointService" as PS
participant "PolicyEndpointRegistry" as PER
participant "PolicyEndpointDAO" as DAO
database DB
participant "PolicyComponent" as PC
database AMQ
note over DAO,DB
UNIQUE : policy,typeOrAspect,endpoint
OTHER : active, modified
SORT : modified
end note
Client -> API
API -> PS : registerPolicyEndpoint(policy, typeOrAspect, endpoint)
PS -> DAO : createPolicyEndpoint(policy, typeOrAspect, endpoint)
DAO -> DB : INSERT
PS <-- DAO : success
PS -> PER : reload(fromTime)
PER -> DAO : getPolicyEndpoints(fromTime)
loop each policy endpoint registration change
PER -> PER
end
PS <-- PER : success
API <-- PS : success
Client <-- API : OK
@enduml

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

View File

@@ -0,0 +1,48 @@
@startuml
Title: Policies: Reload Policy Endpoints (V5.2 Proposal)
skinparam componentStyle uml2
participant "Quartz"
participant "PolicyCheckJob" as PCJ
participant "PolicyEndpointRegistry" as PER
participant "QueuedBehaviour" as QB
participant "PolicyEndpointDAO" as DAO
database DB
participant "PolicyComponent" as PC
database AMQ
== Static Route Initialization ==
activate PER
PER -> QB: new
activate PER
PER -> PER: from("direct:policy")
PER -> PER: dynamicRouter(method(PolicyEndpointRegistry.class, "slip"))
deactivate PER
deactivate PER
== Quartz ==
note over DAO,DB
UNIQUE : policy,typeOrAspect,endpoint
OTHER : active, modified
SORT : modified
end note
Quartz -> PCJ
PCJ -> PER : reload(fromTime)
PER -> DAO : getPolicyEndpoints(fromTime)
DAO -> DB: SELECT(fromTime)
DAO <-- DB
PER <-- DAO
loop each policy endpoint registration change
PER -> PER: updateRegistrations(PolicyEndpoint)
PER -> PC: bindClassBehaviour(policy,typeOrAspect, behaviour)
end
PCJ <-- PER : success
Quartz <-- PCJ : success
@enduml

View File

@@ -0,0 +1,11 @@
# Node Storage and Retrieval
## Properties
### Encrypted properties (```d:encrypted```)
Encrypted properties are stored as BLOBs in the database, but there is no additional handling for
them. In particular, the ```NodeService``` does not encrypt or decrypt them. It only guarantees
that properties of this type contain objects of type ```javax.crypto.SealedObject```. It is up to
the implementor of a custom extension to handle encryption.
The ACS provides the helper class ```MetadataEncryptor``` which provides key handling and a one-stop-shop
for encryption. But custom implementations do not need to use it.

View File

@@ -0,0 +1,118 @@
## Versions
![Completeness Badge](https://img.shields.io/badge/Document_Level-In_Progress-yellow.svg?style=flat-square)
### Purpose
***
### Overview
***
### Artifacts and Guidance
* Source Code Link:m https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/
* License: LGPL
* Issue Tracker Link: https://issues.alfresco.com/jira/secure/RapidBoard.jspa?projectKey=REPO&useStoredSettings=true&rapidView=379
* Documentation Link: http://docs.alfresco.com/5.1/concepts/versioning.html
* Contribution Model: Alfresco publishes the source code and will review proposed patch requests
***
### Prerequisite Knowledge
***
### Design
#### Component Model
#### Data Model
#### Data Dictionary
#### Flows
This is a series of flows illustrating when versions are created, based on changes to content and metadata.
##### No Autoversion on Property Updates
Suppose the defaults in the _cm:versionable_ aspect are set as follows:
```
version.store.enableAutoVersioning=true
version.store.enableAutoVersionOnUpdateProps=false
```
Note this is the default case when Alfresco is installed.
![Autoversion on Property Updates](./resource/sequence/noautoversionprops.png)
##### Autoversion on Property Updates
Suppose the defaults in the _cm:versionable_ aspect are set as follows:
```
version.store.enableAutoVersioning=true
version.store.enableAutoVersionOnUpdateProps=true
```
![Autoversion on Property Updates](./resource/sequence/autoversionprops.png)
#### Class Diagram
***
### APIs and Interfaces
***
### Configuration
#### What is Versioned
Whether an object is versionable at all is governed by the presence of the _cm:versionable_ aspect.
If the aspect is present, the object is versioned. Otherwise it is not versioned.
#### Autoversioning
Sometimes it is desirable to create a version automatically. Whether this happens is controlled by two variables in the _cm:versionable_ aspect:
* cm:autoVersion
* cm:autoVersionOnUpdateProps
When _cm:autoVersion_ is true, a new version is created when the _cm:content_ of a content node changes.
When _cm:autoVersionOnUpdateProps_ is true, a new version is created when any of the properties of a content node change.
The defaults for these properties are set in the contentModel.xml file in the usual way. But to simplify the admin experience, the values of these properties can also set using global properties:
* version.store.enableAutoVersioning
* version.store.enableAutoVersionOnUpdateProps
If the values are found in the properties file they have the effect of overriding what may have been set in the contenModel.xml file.
The effect of these properties can be overridden by Share using a set of two properties:
* autoVersion
* autoVersionProps
The values of these overrides are contained in the file _upload.post.config.xml_ such as in this example
```
<autoVersion>true</autoVersion>
<autoVersionProps>false</autoVersionProps>
```
***
### Performance Considerations
***
### Security Considerations
***
### Cloud Considerations
None
***
### Design Decisions
***

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

View File

@@ -0,0 +1,107 @@
@startuml
Title: Autoversion on Property Update - Versioning Flow
participant "CMIS Client" as C
participant "Repository" as R
participant "Version Service" as V
participant "workspace://SpacesStore" as SS
participant "workspace://version2Store" as VS
participant "File System" as FS
database "Database" as DB
C->R:HTTP POST
activate R
group "transaction"
R->V
activate V
V->SS: create node
activate SS
SS->DB: create node
activate DB
DB->SS: OK
deactivate DB
SS->V: OK
deactivate SS
note right of SS
workspace://SpacesStore/6060b6b6-2928-4092-ab66-659a7e68c0f6
cm:autoVersionOnUpdateProps=true
cm:name=foo.txt
cm:versionLabel=1.0
end note
V->VS: create version history node with one child node
activate VS
VS->DB: create nodes
activate DB
DB->VS: OK
deactivate DB
VS->V: OK
deactivate VS
note right of VS
One Version History Node with one child ...
workspace://version2Store/62de48fa-6adc-4228-8667-df62584f98de
cm:autoVersionOnUpdateProps=true
cm:name=foo.txt
cm:versionLabel=null
ver2:versionLabel=1.0
ver2:versionDescription=Initial Version
end note
end
V->R: OK
deactivate V
R->C: 200 OK
deactivate R
C->R:HTTP PUT (cm:name=bar.txt)
activate R
group "transaction"
R->V
V->SS: update node
activate V
activate SS
SS->DB: update node
activate DB
DB->SS: OK
deactivate DB
SS->V: OK
deactivate SS
note right of SS
workspace://SpacesStore/6060b6b6-2928-4092-ab66-659a7e68c0f6
cm:autoVersionOnUpdateProps=true
cm:name=bar.txt
cm:versionLabel=1.1
end note
V->VS: create a new child of the version history node
activate VS
VS->DB: create node
activate DB
DB->VS: OK
deactivate DB
VS->V: OK
deactivate VS
note right of VS
One Version History Node with two children...
workspace://version2Store/62de48fa-6adc-4228-8667-df62584f98de
cm:autoVersionOnUpdateProps=true
cm:name=foo.txt
cm:versionLabel=null
ver2:versionLabel=1.0
ver2:versionDescription=Initial Version
NEW CHILD:
workspace://version2Store/64d5fd85-40d3-4a44-b644-d871cb3a1030
cm:autoVersionOnUpdateProps=true
cm:name=bar.txt
cm:versionLabel=1.0
ver2:versionLabel=1.1
ver2:versionDescription=Update Name
end note
end
@enduml

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

View File

@@ -0,0 +1,144 @@
@startuml
Title: Autoversion on Content Update - Versioning Flow
participant "CMIS Client" as C
participant "Repository" as R
participant "Version Service" as V
participant "workspace://SpacesStore" as SS
participant "workspace://version2Store" as VS
participant "File System" as FS
database "Database" as DB
C->R:HTTP POST
activate R
group "transaction"
R->V
activate V
V->SS: create node
activate SS
SS->DB: create node
activate DB
DB->SS: OK
deactivate DB
SS->V: OK
deactivate SS
note right of SS
workspace://SpacesStore/e8cc2b68-7482-4304-a93e-02c758a80954
cm:autoVersionOnUpdateProps=false
cm:name=foo.txt
cm:versionLabel=1.0
end note
V->VS: create version history node with one child node
activate VS
VS->DB: create nodes
activate DB
DB->VS: OK
deactivate DB
VS->V: OK
deactivate VS
note right of VS
One Version History Node with one child ...
workspace://version2Store/ce68aba3-73f6-44f9-ad9b-a9e8d77212de
cm:autoVersionOnUpdateProps=false
cm:name=foo.txt
cm:versionLabel=null
ver2:versionLabel=1.0
ver2:versionDescription=Initial Version
end note
end
V->R: OK
deactivate V
R->C: 200 OK
deactivate R
C->R:HTTP PUT (cm:name=bar.txt)
activate R
group "transaction"
R->V
V->SS: update node
activate V
activate SS
SS->DB: update node
activate DB
DB->SS: OK
deactivate DB
SS->V: OK
deactivate SS
note right of SS
workspace://SpacesStore/e8cc2b68-7482-4304-a93e-02c758a80954
cm:autoVersionOnUpdateProps=false
cm:name=bar.txt
cm:versionLabel=1.0
end note
note right of VS
One Version History Node with one child...
workspace://version2Store/ce68aba3-73f6-44f9-ad9b-a9e8d77212de
cm:autoVersionOnUpdateProps=false
cm:name=foo.txt
cm:versionLabel=null
ver2:versionLabel=1.0
ver2:versionDescription=Initial Version
end note
end
C->R:HTTP PUT (new file content)
activate R
group "transaction"
R->V
activate V
V->SS: update node
activate SS
SS->FS: write file content
activate FS
FS->SS: OK
deactivate FS
SS->DB: update node to point to new file content
activate DB
DB->SS: OK
deactivate DB
SS->V: OK
deactivate SS
note right of SS
workspace://SpacesStore/e8cc2b68-7482-4304-a93e-02c758a80954
cm:autoVersionOnUpdateProps=false
cm:name=bar.txt
cm:versionLabel=1.0
end note
V->VS: create a new child of the version history node
activate VS
VS->DB: create node
activate DB
DB->VS: OK
deactivate DB
VS->V: OK
deactivate VS
note right of VS
One Version History Node with two children...
workspace://version2Store/ce68aba3-73f6-44f9-ad9b-a9e8d77212de
cm:autoVersionOnUpdateProps=false
cm:name=foo.txt
cm:versionLabel=null
ver2:versionLabel=1.0
ver2:versionDescription=Initial Version
NEW CHILD:
workspace://version2Store/9fb3fb08-7cfb-4c0a-ac72-233aaf60fa1e
cm:autoVersionOnUpdateProps=false
cm:name=bar.txt
cm:versionLabel=1.0
ver2:versionLabel=1.1
ver2:versionDescription=Update New File Content
end note
end
@enduml

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

View File

@@ -0,0 +1,37 @@
@startuml
title Actions: Asynchronous Actions Classes (V5.1 Current)
interface ActionService {
+ void executeAction(Action action, NodeRef actionedUponNodeRef, boolean checkConditions, boolean executeAsychronously)
}
class ActionServiceImpl implements ActionService {
- ActionTransactionListener transactionListener
- Map<String, AsynchronousActionExecutionQueue> asynchronousActionExecutionQueues
+ void registerAsynchronousActionExecutionQueue(String key, AsynchronousActionExecutionQueue asyncExecQueue)
+ void executeAction(...)
- void addPostTransactionPendingAction(action, actionedUponNodeRef, checkConditions, actionChain)
- List<PendingAction> getPostTransactionPendingActions()
+ postCommit()
- queueAction(PendingAction action)
+ etc(...)
}
interface AsynchronousActionExecutionQueue {
+ void executeAction(RuntimeActionService actionService, Action action, NodeRef actionedUponNodeRef, boolean checkConditions, Set<String> actionChain);
}
class AsynchronousActionExecutionQueueImpl implements AsynchronousActionExecutionQueue {
- ThreadPoolExecutor threadPoolExecutor
- TransactionService transactionService
- PolicyComponent policyComponent
- Map<String, AbstractAsynchronousActionFilter> actionFilters
- String id
+ void init()
+ void executeAction(...)
+ etc(...)
}
AsynchronousActionExecutionQueueImpl o- "1" ActionServiceImpl
AsynchronousActionExecutionQueueImpl "0..*" -o ActionServiceImpl
@enduml

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

View File

@@ -0,0 +1,61 @@
@startuml
title Actions: Queued Actions Classes (V5.2 Proposal)
interface ActionService {
+ void executeAction(Action action, NodeRef actionedUponNodeRef, boolean checkConditions, boolean executeAsychronously)
}
class ActionServiceImpl implements ActionService {
- ActionTransactionListener transactionListener
- Map<String, AsynchronousActionExecutionQueue> asynchronousActionExecutionQueues
+ void registerAsynchronousActionExecutionQueue(String key, AsynchronousActionExecutionQueue asyncExecQueue)
+ void executeAction(...)
- <b>void bindPendingActionToTransaction(action, actionedUponNodeRef, checkConditions, actionChain)</b>
- <s>void addPostTransactionPendingAction(action, actionedUponNodeRef, checkConditions, actionChain)</s>
- List<PendingAction> getPostTransactionPendingActions()
+ <b>beforeCommit()</b>
+ <s>postCommit()</s>
- queueAction(PendingAction action)
+ etc(...)
}
interface AsynchronousActionExecutionQueue {
+ void executeAction(RuntimeActionService actionService, Action action, NodeRef actionedUponNodeRef, boolean checkConditions, Set<String> actionChain);
}
class AsynchronousActionExecutionQueueImpl implements AsynchronousActionExecutionQueue {
- <b>ProducerTemplate producerTemplate</b>
- <s>ThreadPoolExecutor threadPoolExecutor</s>
- TransactionService transactionService
- PolicyComponent policyComponent
- Map<String, AbstractAsynchronousActionFilter> actionFilters
- String id
+ void init()
+ void executeAction(...)
+ etc(...)
}
package org::apache::camel {
class ProducerTemplate {
+ void sendBodyAndHeaders(String endpointUri, Object body, Map<String, Object> headers)
}
class RouteBuilder {
+ void configure()
+ etc(...)
}
class JacksonDataFormat
}
class QueuedActionProducer extends RouteBuilder {
+ void configure()
}
class QueuedActionReceiver extends RouteBuilder {
+ void configure()
}
AsynchronousActionExecutionQueueImpl o- "1" ActionServiceImpl
AsynchronousActionExecutionQueueImpl "0..*" -o ActionServiceImpl
AsynchronousActionExecutionQueueImpl o- "1" ProducerTemplate
QueuedActionProducer --> "uses" JacksonDataFormat
QueuedActionReceiver --> "uses" JacksonDataFormat
@enduml

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

View File

@@ -0,0 +1,63 @@
@startuml
class RuleType {
String name
String displayLabel
}
class Rule {
NodeRef nodeRef
String title
String description
boolean ruleDisabled
boolean executeAsynchronously
boolean isAppliedToChildren
}
class Action
class RuleTrigger
Rule --> "1..*" RuleType
Rule --> "1" Action
RuleType --> "*" RuleTrigger
note left of RuleType
There are three rule types defined by default:
- inbound
- outbound
- update
end note
together {
class BeforeDeleteChildAssociationRuleTrigger
class CreateNodeRuleTrigger
class OnCreateChildAssociationRuleTrigger
class OnMoveNodeRuleTrigger
class OnPropertyUpdateRuleTrigger
class RestoreNodeRuleTrigger
class SingleAssocRefPolicyRuleTrigger
class SingleNodeRefPolicyRuleTrigger
}
CreateNodeRuleTrigger -[hidden]--> BeforeDeleteChildAssociationRuleTrigger
OnMoveNodeRuleTrigger -[hidden]--> OnCreateChildAssociationRuleTrigger
OnPropertyUpdateRuleTrigger -[hidden]--> RestoreNodeRuleTrigger
SingleAssocRefPolicyRuleTrigger -[hidden]--> SingleNodeRefPolicyRuleTrigger
RuleTrigger <|-- BeforeDeleteChildAssociationRuleTrigger
RuleTrigger <|-- CreateNodeRuleTrigger
RuleTrigger <|-- OnCreateChildAssociationRuleTrigger
RuleTrigger <|-- OnMoveNodeRuleTrigger
RuleTrigger <|-- OnPropertyUpdateRuleTrigger
RuleTrigger <|-- RestoreNodeRuleTrigger
RuleTrigger <|-- SingleAssocRefPolicyRuleTrigger
RuleTrigger <|-- SingleNodeRefPolicyRuleTrigger
note right of Action
See separate diagram for info about Actions
end note
@enduml

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -0,0 +1,26 @@
@startuml
left to right direction
skinparam linetype ortho
component Client
component TransformationEngine {
component Service
component Router
component Configuration
}
component AdminConsole
component Log
component TransformationFarm {
component Worker1
component Worker2
component Worker3
}
Client->Service:calls
Service->Router:calls
Router->Configuration:reads
AdminConsole->Configuration:sets
Service->Log:writes
AdminConsole->Log:writes
Router->Worker1:calls
@enduml

View File

@@ -0,0 +1,7 @@
# Branch specific configuration file for localisation scripts
MESSAGE_SEARCH_PATH="src/main/resources/alfresco/messages/action-config*.properties src/main/resources/alfresco/messages/action-service*.properties src/main/resources/alfresco/messages/activiti-engine-messages*.properties src/main/resources/alfresco/messages/activities-service*.properties src/main/resources/alfresco/messages/activity-list*.properties src/main/resources/alfresco/messages/application-model*.properties src/main/resources/alfresco/messages/authentication*.properties src/main/resources/alfresco/messages/bootstrap-content-template-examples*.properties src/main/resources/alfresco/messages/bootstrap-example-javascripts*.properties src/main/resources/alfresco/messages/bootstrap-example-smartfoldertemplates*.properties src/main/resources/alfresco/messages/bootstrap-imapScripts*.properties src/main/resources/alfresco/messages/bootstrap-javascripts*.properties src/main/resources/alfresco/messages/bootstrap-messages*.properties src/main/resources/alfresco/messages/bootstrap-readme-template*.properties src/main/resources/alfresco/messages/bootstrap-spaces*.properties src/main/resources/alfresco/messages/bootstrap-templates*.properties src/main/resources/alfresco/messages/bootstrap-tutorial*.properties src/main/resources/alfresco/messages/bootstrap-webScripts*.properties src/main/resources/alfresco/messages/bootstrap-webScriptsExtensions*.properties src/main/resources/alfresco/messages/bpm-messages*.properties src/main/resources/alfresco/messages/categories*.properties src/main/resources/alfresco/messages/coci-service*.properties src/main/resources/alfresco/messages/content-filter-languages*.properties src/main/resources/alfresco/messages/content-model*.properties src/main/resources/alfresco/messages/copy-service*.properties src/main/resources/alfresco/messages/custommodel-service*.properties src/main/resources/alfresco/messages/discussion-messages*.properties src/main/resources/alfresco/messages/distributionpolicies-model*.properties src/main/resources/alfresco/messages/doclink-service*.properties src/main/resources/alfresco/messages/download-model*.properties src/main/resources/alfresco/messages/email-server-model*.properties src/main/resources/alfresco/messages/email-service*.properties src/main/resources/alfresco/messages/file-folder-service*.properties src/main/resources/alfresco/messages/form-service*.properties src/main/resources/alfresco/messages/forum-model*.properties src/main/resources/alfresco/messages/imap-service*.properties src/main/resources/alfresco/messages/initiate-inplace*.properties src/main/resources/alfresco/messages/invitation-service*.properties src/main/resources/alfresco/messages/lock-service*.properties src/main/resources/alfresco/messages/notification-service*.properties src/main/resources/alfresco/messages/period-provider*.properties src/main/resources/alfresco/messages/permissions-service*.properties src/main/resources/alfresco/messages/quickshare-service*.properties src/main/resources/alfresco/messages/rendition-config*.properties src/main/resources/alfresco/messages/replication*.properties src/main/resources/alfresco/messages/repoadmin-service*.properties src/main/resources/alfresco/messages/reset-password-messages*.properties src/main/resources/alfresco/messages/rule-config*.properties src/main/resources/alfresco/messages/site-model*.properties src/main/resources/alfresco/messages/site-service*.properties src/main/resources/alfresco/messages/slingshot*.properties src/main/resources/alfresco/messages/smartfolder-model*.properties src/main/resources/alfresco/messages/subscription-service*.properties src/main/resources/alfresco/messages/system-messages*.properties src/main/resources/alfresco/messages/system-model*.properties src/main/resources/alfresco/messages/template-service*.properties src/main/resources/alfresco/messages/templates-messages*.properties src/main/resources/alfresco/messages/transfer-model*.properties src/main/resources/alfresco/messages/transfer-service*.properties src/main/resources/alfresco/messages/ui-inplace*.properties src/main/resources/alfresco/messages/webdav-messages*.properties src/main/resources/alfresco/messages/workflow-package-messages*.properties src/main/resources/alfresco/workflow/invitation-moderated-workflow-messages*.properties src/main/resources/alfresco/workflow/invitation-nominated-workflow-messages*.properties src/main/resources/alfresco/workflow/workflow-messages*.properties"
EXCLUDED_FILES="src/main/resources/alfresco/messages/content-service.properties src/main/resources/alfresco/messages/module-messages.properties src/main/resources/alfresco/messages/patch-service.properties src/main/resources/alfresco/messages/repoadmin-interpreter-help.properties src/main/resources/alfresco/messages/schema-update.properties src/main/resources/alfresco/messages/tenant-interpreter-help.properties src/main/resources/alfresco/messages/version-service.properties src/main/resources/alfresco/messages/workflow-interpreter-help.properties src/main/resources/alfresco/alfresco-shared.properties src/main/resources/alfresco/caches.properties src/main/resources/alfresco/repository.properties src/main/resources/alfresco/client/config/repo-clients-apps.properties src/main/resources/alfresco/domain/cache-strategies.properties src/main/resources/alfresco/domain/hibernate-cfg.properties src/main/resources/alfresco/domain/quartz.properties src/main/resources/alfresco/domain/transaction.properties src/main/resources/alfresco/keystore/keystore-passwords.properties src/main/resources/alfresco/keystore/ssl-keystore-passwords.properties src/main/resources/alfresco/keystore/ssl-truststore-passwords.properties src/main/resources/alfresco/metadata/DWGMetadataExtracter.properties src/main/resources/alfresco/metadata/HtmlMetadataExtracter.properties src/main/resources/alfresco/metadata/MailMetadataExtracter.properties src/main/resources/alfresco/metadata/MP3MetadataExtracter.properties src/main/resources/alfresco/metadata/OfficeMetadataExtracter.properties src/main/resources/alfresco/metadata/PdfBoxMetadataExtracter.properties src/main/resources/alfresco/metadata/PoiMetadataExtracter.properties src/main/resources/alfresco/metadata/RFC822MetadataExtracter.properties src/main/resources/alfresco/metadata/TikaAudioMetadataExtracter.properties src/main/resources/alfresco/metadata/TikaAutoMetadataExtracter.properties src/main/resources/alfresco/metadata/TikaSpringConfiguredMetadataExtracter.properties src/main/resources/alfresco/subsystems/ActivitiesFeed/default/activities-jobs.properties src/main/resources/alfresco/subsystems/Authentication/alfrescoNtlm/alfresco-authentication.properties src/main/resources/alfresco/subsystems/Authentication/external/external-authentication.properties src/main/resources/alfresco/subsystems/Authentication/kerberos/kerberos-authentication.properties src/main/resources/alfresco/subsystems/Authentication/ldap/ldap-authentication.properties src/main/resources/alfresco/subsystems/Authentication/ldap-ad/ldap-ad-authentication.properties src/main/resources/alfresco/subsystems/email/InboundSMTP/inboundSMTP.properties src/main/resources/alfresco/subsystems/email/OutboundSMTP/outboundSMTP.properties src/main/resources/alfresco/subsystems/fileServers/default/file-servers.properties src/main/resources/alfresco/subsystems/imap/default/imap-server.properties src/main/resources/alfresco/subsystems/Replication/default/replication.properties src/main/resources/alfresco/subsystems/Search/noindex/common-search.properties src/main/resources/alfresco/subsystems/Search/noindex/noindex-search.properties src/main/resources/alfresco/subsystems/Search/solr/common-search.properties src/main/resources/alfresco/subsystems/Search/solr/solr-backup.properties src/main/resources/alfresco/subsystems/Search/solr/solr-search.properties src/main/resources/alfresco/subsystems/Search/solr/facet/solr-facets-config.properties src/main/resources/alfresco/subsystems/Search/solr4/common-search.properties src/main/resources/alfresco/subsystems/Search/solr4/solr-backup.properties src/main/resources/alfresco/subsystems/Search/solr4/solr-search.properties src/main/resources/alfresco/subsystems/Search/solr6/common-search.properties src/main/resources/alfresco/subsystems/Search/solr6/solr-backup.properties src/main/resources/alfresco/subsystems/Search/solr6/solr-search.properties src/main/resources/alfresco/subsystems/Subscriptions/default/subscription-service.properties src/main/resources/alfresco/subsystems/Synchronization/default/default-synchronization.properties src/main/resources/alfresco/subsystems/sysAdmin/default/sysadmin-parameter.properties src/main/resources/alfresco/subsystems/thirdparty/default/alfresco-pdf-renderer-transform.properties src/main/resources/alfresco/subsystems/thirdparty/default/imagemagick-transform.properties src/main/resources/alfresco/subsystems/Transformers/default/transformers.properties src/main/resources/org/alfresco/encryption/keystore-parameters.properties src/main/resources/org/alfresco/repo/i18n/testMessages.properties src/main/resources/org/alfresco/repo/module/tool/default-file-mapping.properties src/main/resources/alfresco/metadata/JodConverterMetadataExtracter.properties src/main/resources/alfresco/subsystems/OOoJodconverter/default/jodconverter.properties"

1606
repository/pom.xml Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,2 @@
Manifest-Version: 1.0
Main-Class: org.alfresco.repo.model.filefolder.loader.FileFolderRemoteLoader

View File

@@ -0,0 +1,2 @@
Manifest-Version: 1.0
Main-Class: org.alfresco.repo.module.tool.ModuleManagementTool

View File

@@ -0,0 +1,486 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package lib3party.org.apache.chemistry.opencmis.server.support.wrapper;
import java.math.BigInteger;
import java.util.List;
import org.apache.chemistry.opencmis.commons.data.Acl;
import org.apache.chemistry.opencmis.commons.data.AllowableActions;
import org.apache.chemistry.opencmis.commons.data.BulkUpdateObjectIdAndChangeToken;
import org.apache.chemistry.opencmis.commons.data.ContentStream;
import org.apache.chemistry.opencmis.commons.data.ExtensionsData;
import org.apache.chemistry.opencmis.commons.data.FailedToDeleteData;
import org.apache.chemistry.opencmis.commons.data.ObjectData;
import org.apache.chemistry.opencmis.commons.data.ObjectInFolderContainer;
import org.apache.chemistry.opencmis.commons.data.ObjectInFolderList;
import org.apache.chemistry.opencmis.commons.data.ObjectList;
import org.apache.chemistry.opencmis.commons.data.ObjectParentData;
import org.apache.chemistry.opencmis.commons.data.Properties;
import org.apache.chemistry.opencmis.commons.data.RenditionData;
import org.apache.chemistry.opencmis.commons.data.RepositoryInfo;
import org.apache.chemistry.opencmis.commons.definitions.TypeDefinition;
import org.apache.chemistry.opencmis.commons.definitions.TypeDefinitionContainer;
import org.apache.chemistry.opencmis.commons.definitions.TypeDefinitionList;
import org.apache.chemistry.opencmis.commons.enums.AclPropagation;
import org.apache.chemistry.opencmis.commons.enums.IncludeRelationships;
import org.apache.chemistry.opencmis.commons.enums.RelationshipDirection;
import org.apache.chemistry.opencmis.commons.enums.UnfileObject;
import org.apache.chemistry.opencmis.commons.enums.VersioningState;
import org.apache.chemistry.opencmis.commons.server.CallContext;
import org.apache.chemistry.opencmis.commons.server.CmisService;
import org.apache.chemistry.opencmis.commons.server.ObjectInfo;
import org.apache.chemistry.opencmis.commons.server.ProgressControlCmisService;
import org.apache.chemistry.opencmis.commons.spi.Holder;
/**
* An abstract CMIS service wrapper.
* <p>
* All service wrappers managed by {@link CmisServiceWrapperManager} must be
* derived from this class and must provide a constructor that takes a
* {@link CmisService} object as the sole parameter.
*/
public abstract class AbstractCmisServiceWrapper implements CallContextAwareCmisService, ProgressControlCmisService {
private CmisService service;
private CallContext context;
public AbstractCmisServiceWrapper(CmisService service) {
if (service == null) {
throw new IllegalArgumentException("Service must be set!");
}
this.service = service;
}
/**
* Initializes the wrapper with a set of parameters.
*
* @param params
* an array of parameter objects
*/
public void initialize(Object[] params) {
}
/**
* Returns the wrapped service or the next service wrapper.
*
* @return the wrapped service
*/
public CmisService getWrappedService() {
return service;
}
/**
* Sets the call context and propagates it down to the next service wrapper
* or service if it implements the {@link CallContextAwareCmisService}
* interface.
*/
@Override
public void setCallContext(CallContext callContext) {
this.context = callContext;
if (service instanceof CallContextAwareCmisService) {
((CallContextAwareCmisService) service).setCallContext(callContext);
}
}
/**
* Gets the current call context.
*/
@Override
public CallContext getCallContext() {
return context;
}
// --- processing ---
@Override
public ProgressControlCmisService.Progress beforeServiceCall() {
if (service instanceof ProgressControlCmisService) {
return ((ProgressControlCmisService) service).beforeServiceCall();
}
return ProgressControlCmisService.Progress.CONTINUE;
}
@Override
public ProgressControlCmisService.Progress afterServiceCall() {
if (service instanceof ProgressControlCmisService) {
return ((ProgressControlCmisService) service).afterServiceCall();
}
return ProgressControlCmisService.Progress.CONTINUE;
}
// --- service methods ---
@Override
public List<RepositoryInfo> getRepositoryInfos(ExtensionsData extension) {
return service.getRepositoryInfos(extension);
}
@Override
public RepositoryInfo getRepositoryInfo(String repositoryId, ExtensionsData extension) {
return service.getRepositoryInfo(repositoryId, extension);
}
@Override
public TypeDefinitionList getTypeChildren(String repositoryId, String typeId, Boolean includePropertyDefinitions,
BigInteger maxItems, BigInteger skipCount, ExtensionsData extension) {
return service
.getTypeChildren(repositoryId, typeId, includePropertyDefinitions, maxItems, skipCount, extension);
}
@Override
public List<TypeDefinitionContainer> getTypeDescendants(String repositoryId, String typeId, BigInteger depth,
Boolean includePropertyDefinitions, ExtensionsData extension) {
return service.getTypeDescendants(repositoryId, typeId, depth, includePropertyDefinitions, extension);
}
@Override
public TypeDefinition getTypeDefinition(String repositoryId, String typeId, ExtensionsData extension) {
return service.getTypeDefinition(repositoryId, typeId, extension);
}
@Override
public TypeDefinition createType(String repositoryId, TypeDefinition type, ExtensionsData extension) {
return service.createType(repositoryId, type, extension);
}
@Override
public TypeDefinition updateType(String repositoryId, TypeDefinition type, ExtensionsData extension) {
return service.updateType(repositoryId, type, extension);
}
@Override
public void deleteType(String repositoryId, String typeId, ExtensionsData extension) {
service.deleteType(repositoryId, typeId, extension);
}
@Override
public ObjectInFolderList getChildren(String repositoryId, String folderId, String filter, String orderBy,
Boolean includeAllowableActions, IncludeRelationships includeRelationships, String renditionFilter,
Boolean includePathSegment, BigInteger maxItems, BigInteger skipCount, ExtensionsData extension) {
return service.getChildren(repositoryId, folderId, filter, orderBy, includeAllowableActions,
includeRelationships, renditionFilter, includePathSegment, maxItems, skipCount, extension);
}
@Override
public List<ObjectInFolderContainer> getDescendants(String repositoryId, String folderId, BigInteger depth,
String filter, Boolean includeAllowableActions, IncludeRelationships includeRelationships,
String renditionFilter, Boolean includePathSegment, ExtensionsData extension) {
return service.getDescendants(repositoryId, folderId, depth, filter, includeAllowableActions,
includeRelationships, renditionFilter, includePathSegment, extension);
}
@Override
public List<ObjectInFolderContainer> getFolderTree(String repositoryId, String folderId, BigInteger depth,
String filter, Boolean includeAllowableActions, IncludeRelationships includeRelationships,
String renditionFilter, Boolean includePathSegment, ExtensionsData extension) {
return service.getFolderTree(repositoryId, folderId, depth, filter, includeAllowableActions,
includeRelationships, renditionFilter, includePathSegment, extension);
}
@Override
public List<ObjectParentData> getObjectParents(String repositoryId, String objectId, String filter,
Boolean includeAllowableActions, IncludeRelationships includeRelationships, String renditionFilter,
Boolean includeRelativePathSegment, ExtensionsData extension) {
return service.getObjectParents(repositoryId, objectId, filter, includeAllowableActions, includeRelationships,
renditionFilter, includeRelativePathSegment, extension);
}
@Override
public ObjectData getFolderParent(String repositoryId, String folderId, String filter, ExtensionsData extension) {
return service.getFolderParent(repositoryId, folderId, filter, extension);
}
@Override
public ObjectList getCheckedOutDocs(String repositoryId, String folderId, String filter, String orderBy,
Boolean includeAllowableActions, IncludeRelationships includeRelationships, String renditionFilter,
BigInteger maxItems, BigInteger skipCount, ExtensionsData extension) {
return service.getCheckedOutDocs(repositoryId, folderId, filter, orderBy, includeAllowableActions,
includeRelationships, renditionFilter, maxItems, skipCount, extension);
}
@Override
public String createDocument(String repositoryId, Properties properties, String folderId,
ContentStream contentStream, VersioningState versioningState, List<String> policies, Acl addAces,
Acl removeAces, ExtensionsData extension) {
return service.createDocument(repositoryId, properties, folderId, contentStream, versioningState, policies,
addAces, removeAces, extension);
}
@Override
public String createDocumentFromSource(String repositoryId, String sourceId, Properties properties,
String folderId, VersioningState versioningState, List<String> policies, Acl addAces, Acl removeAces,
ExtensionsData extension) {
return service.createDocumentFromSource(repositoryId, sourceId, properties, folderId, versioningState,
policies, addAces, removeAces, extension);
}
@Override
public String createFolder(String repositoryId, Properties properties, String folderId, List<String> policies,
Acl addAces, Acl removeAces, ExtensionsData extension) {
return service.createFolder(repositoryId, properties, folderId, policies, addAces, removeAces, extension);
}
@Override
public String createRelationship(String repositoryId, Properties properties, List<String> policies, Acl addAces,
Acl removeAces, ExtensionsData extension) {
return service.createRelationship(repositoryId, properties, policies, addAces, removeAces, extension);
}
@Override
public String createPolicy(String repositoryId, Properties properties, String folderId, List<String> policies,
Acl addAces, Acl removeAces, ExtensionsData extension) {
return service.createPolicy(repositoryId, properties, folderId, policies, addAces, removeAces, extension);
}
@Override
public String createItem(String repositoryId, Properties properties, String folderId, List<String> policies,
Acl addAces, Acl removeAces, ExtensionsData extension) {
return service.createItem(repositoryId, properties, folderId, policies, addAces, removeAces, extension);
}
@Override
public AllowableActions getAllowableActions(String repositoryId, String objectId, ExtensionsData extension) {
return service.getAllowableActions(repositoryId, objectId, extension);
}
@Override
public ObjectData getObject(String repositoryId, String objectId, String filter, Boolean includeAllowableActions,
IncludeRelationships includeRelationships, String renditionFilter, Boolean includePolicyIds,
Boolean includeAcl, ExtensionsData extension) {
return service.getObject(repositoryId, objectId, filter, includeAllowableActions, includeRelationships,
renditionFilter, includePolicyIds, includeAcl, extension);
}
@Override
public Properties getProperties(String repositoryId, String objectId, String filter, ExtensionsData extension) {
return service.getProperties(repositoryId, objectId, filter, extension);
}
@Override
public List<RenditionData> getRenditions(String repositoryId, String objectId, String renditionFilter,
BigInteger maxItems, BigInteger skipCount, ExtensionsData extension) {
return service.getRenditions(repositoryId, objectId, renditionFilter, maxItems, skipCount, extension);
}
@Override
public ObjectData getObjectByPath(String repositoryId, String path, String filter, Boolean includeAllowableActions,
IncludeRelationships includeRelationships, String renditionFilter, Boolean includePolicyIds,
Boolean includeAcl, ExtensionsData extension) {
return service.getObjectByPath(repositoryId, path, filter, includeAllowableActions, includeRelationships,
renditionFilter, includePolicyIds, includeAcl, extension);
}
@Override
public ContentStream getContentStream(String repositoryId, String objectId, String streamId, BigInteger offset,
BigInteger length, ExtensionsData extension) {
return service.getContentStream(repositoryId, objectId, streamId, offset, length, extension);
}
@Override
public void updateProperties(String repositoryId, Holder<String> objectId, Holder<String> changeToken,
Properties properties, ExtensionsData extension) {
service.updateProperties(repositoryId, objectId, changeToken, properties, extension);
}
@Override
public List<BulkUpdateObjectIdAndChangeToken> bulkUpdateProperties(String repositoryId,
List<BulkUpdateObjectIdAndChangeToken> objectIdsAndChangeTokens, Properties properties,
List<String> addSecondaryTypeIds, List<String> removeSecondaryTypeIds, ExtensionsData extension) {
return service.bulkUpdateProperties(repositoryId, objectIdsAndChangeTokens, properties, addSecondaryTypeIds,
removeSecondaryTypeIds, extension);
}
@Override
public void moveObject(String repositoryId, Holder<String> objectId, String targetFolderId, String sourceFolderId,
ExtensionsData extension) {
service.moveObject(repositoryId, objectId, targetFolderId, sourceFolderId, extension);
}
@Override
public void deleteObject(String repositoryId, String objectId, Boolean allVersions, ExtensionsData extension) {
service.deleteObject(repositoryId, objectId, allVersions, extension);
}
@Override
public FailedToDeleteData deleteTree(String repositoryId, String folderId, Boolean allVersions,
UnfileObject unfileObjects, Boolean continueOnFailure, ExtensionsData extension) {
return service.deleteTree(repositoryId, folderId, allVersions, unfileObjects, continueOnFailure, extension);
}
@Override
public void setContentStream(String repositoryId, Holder<String> objectId, Boolean overwriteFlag,
Holder<String> changeToken, ContentStream contentStream, ExtensionsData extension) {
service.setContentStream(repositoryId, objectId, overwriteFlag, changeToken, contentStream, extension);
}
@Override
public void deleteContentStream(String repositoryId, Holder<String> objectId, Holder<String> changeToken,
ExtensionsData extension) {
service.deleteContentStream(repositoryId, objectId, changeToken, extension);
}
@Override
public void appendContentStream(String repositoryId, Holder<String> objectId, Holder<String> changeToken,
ContentStream contentStream, boolean isLastChunk, ExtensionsData extension) {
service.appendContentStream(repositoryId, objectId, changeToken, contentStream, isLastChunk, extension);
}
@Override
public void checkOut(String repositoryId, Holder<String> objectId, ExtensionsData extension,
Holder<Boolean> contentCopied) {
service.checkOut(repositoryId, objectId, extension, contentCopied);
}
@Override
public void cancelCheckOut(String repositoryId, String objectId, ExtensionsData extension) {
service.cancelCheckOut(repositoryId, objectId, extension);
}
@Override
public void checkIn(String repositoryId, Holder<String> objectId, Boolean major, Properties properties,
ContentStream contentStream, String checkinComment, List<String> policies, Acl addAces, Acl removeAces,
ExtensionsData extension) {
service.checkIn(repositoryId, objectId, major, properties, contentStream, checkinComment, policies, addAces,
removeAces, extension);
}
@Override
public ObjectData getObjectOfLatestVersion(String repositoryId, String objectId, String versionSeriesId,
Boolean major, String filter, Boolean includeAllowableActions, IncludeRelationships includeRelationships,
String renditionFilter, Boolean includePolicyIds, Boolean includeAcl, ExtensionsData extension) {
return service
.getObjectOfLatestVersion(repositoryId, objectId, versionSeriesId, major, filter,
includeAllowableActions, includeRelationships, renditionFilter, includePolicyIds, includeAcl,
extension);
}
@Override
public Properties getPropertiesOfLatestVersion(String repositoryId, String objectId, String versionSeriesId,
Boolean major, String filter, ExtensionsData extension) {
return service.getPropertiesOfLatestVersion(repositoryId, objectId, versionSeriesId, major, filter, extension);
}
@Override
public List<ObjectData> getAllVersions(String repositoryId, String objectId, String versionSeriesId, String filter,
Boolean includeAllowableActions, ExtensionsData extension) {
return service.getAllVersions(repositoryId, objectId, versionSeriesId, filter, includeAllowableActions,
extension);
}
@Override
public ObjectList query(String repositoryId, String statement, Boolean searchAllVersions,
Boolean includeAllowableActions, IncludeRelationships includeRelationships, String renditionFilter,
BigInteger maxItems, BigInteger skipCount, ExtensionsData extension) {
return service.query(repositoryId, statement, searchAllVersions, includeAllowableActions, includeRelationships,
renditionFilter, maxItems, skipCount, extension);
}
@Override
public ObjectList getContentChanges(String repositoryId, Holder<String> changeLogToken, Boolean includeProperties,
String filter, Boolean includePolicyIds, Boolean includeAcl, BigInteger maxItems, ExtensionsData extension) {
return service.getContentChanges(repositoryId, changeLogToken, includeProperties, filter, includePolicyIds,
includeAcl, maxItems, extension);
}
@Override
public void addObjectToFolder(String repositoryId, String objectId, String folderId, Boolean allVersions,
ExtensionsData extension) {
service.addObjectToFolder(repositoryId, objectId, folderId, allVersions, extension);
}
@Override
public void removeObjectFromFolder(String repositoryId, String objectId, String folderId, ExtensionsData extension) {
service.removeObjectFromFolder(repositoryId, objectId, folderId, extension);
}
@Override
public ObjectList getObjectRelationships(String repositoryId, String objectId, Boolean includeSubRelationshipTypes,
RelationshipDirection relationshipDirection, String typeId, String filter, Boolean includeAllowableActions,
BigInteger maxItems, BigInteger skipCount, ExtensionsData extension) {
return service.getObjectRelationships(repositoryId, objectId, includeSubRelationshipTypes,
relationshipDirection, typeId, filter, includeAllowableActions, maxItems, skipCount, extension);
}
@Override
public Acl getAcl(String repositoryId, String objectId, Boolean onlyBasicPermissions, ExtensionsData extension) {
return service.getAcl(repositoryId, objectId, onlyBasicPermissions, extension);
}
@Override
public Acl applyAcl(String repositoryId, String objectId, Acl addAces, Acl removeAces,
AclPropagation aclPropagation, ExtensionsData extension) {
return service.applyAcl(repositoryId, objectId, addAces, removeAces, aclPropagation, extension);
}
@Override
public void applyPolicy(String repositoryId, String policyId, String objectId, ExtensionsData extension) {
service.applyPolicy(repositoryId, policyId, objectId, extension);
}
@Override
public void removePolicy(String repositoryId, String policyId, String objectId, ExtensionsData extension) {
service.removePolicy(repositoryId, policyId, objectId, extension);
}
@Override
public List<ObjectData> getAppliedPolicies(String repositoryId, String objectId, String filter,
ExtensionsData extension) {
return service.getAppliedPolicies(repositoryId, objectId, filter, extension);
}
@Override
public String create(String repositoryId, Properties properties, String folderId, ContentStream contentStream,
VersioningState versioningState, List<String> policies, ExtensionsData extension) {
return service.create(repositoryId, properties, folderId, contentStream, versioningState, policies, extension);
}
@Override
public void deleteObjectOrCancelCheckOut(String repositoryId, String objectId, Boolean allVersions,
ExtensionsData extension) {
service.deleteObjectOrCancelCheckOut(repositoryId, objectId, allVersions, extension);
}
@Override
public Acl applyAcl(String repositoryId, String objectId, Acl aces, AclPropagation aclPropagation) {
return service.applyAcl(repositoryId, objectId, aces, aclPropagation);
}
@Override
public ObjectInfo getObjectInfo(String repositoryId, String objectId) {
return service.getObjectInfo(repositoryId, objectId);
}
@Override
public void close() {
service.close();
context = null;
}
}

View File

@@ -0,0 +1,50 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package lib3party.org.apache.chemistry.opencmis.server.support.wrapper;
import org.apache.chemistry.opencmis.commons.server.CallContext;
import org.apache.chemistry.opencmis.commons.server.CmisService;
/**
* Identifies a CMIS service object that can handle call contexts.
*/
public interface CallContextAwareCmisService extends CmisService {
/**
* Returns the current call context.
*
* @return the call context
*/
CallContext getCallContext();
/**
* Sets a new call context.
*
* @param callContext
* the new call context
*/
void setCallContext(CallContext callContext);
}

View File

@@ -0,0 +1,280 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.email.server;
import java.io.Serializable;
import java.util.Map;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.repo.copy.CopyBehaviourCallback;
import org.alfresco.repo.copy.CopyDetails;
import org.alfresco.repo.copy.CopyServicePolicies;
import org.alfresco.repo.node.NodeServicePolicies;
import org.alfresco.repo.node.NodeServicePolicies.BeforeDeleteNodePolicy;
import org.alfresco.repo.node.NodeServicePolicies.OnAddAspectPolicy;
import org.alfresco.repo.node.NodeServicePolicies.BeforeRemoveAspectPolicy;
import org.alfresco.repo.node.NodeServicePolicies.OnUpdatePropertiesPolicy;
import org.alfresco.repo.policy.Behaviour.NotificationFrequency;
import org.alfresco.repo.policy.JavaBehaviour;
import org.alfresco.repo.policy.PolicyComponent;
import org.alfresco.service.cmr.attributes.AttributeService;
import org.alfresco.service.cmr.attributes.DuplicateAttributeException;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.PropertyCheck;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Class that supports functionality of email aliasable aspect.
*
* @author mrogers
* @since 2.2
*/
public class AliasableAspect implements NodeServicePolicies.OnAddAspectPolicy,
NodeServicePolicies.BeforeRemoveAspectPolicy,
NodeServicePolicies.OnUpdatePropertiesPolicy,
NodeServicePolicies.BeforeDeleteNodePolicy,
CopyServicePolicies.OnCopyNodePolicy
{
private PolicyComponent policyComponent;
private NodeService nodeService;
private AttributeService attributeService;
private static Log logger = LogFactory.getLog(AliasableAspect.class);
/**
* The first "key" into the attribute table - identifies that the attribute is for this class
*/
public final static String ALIASABLE_ATTRIBUTE_KEY_1 = "AliasableAspect";
/**
* The second "key" into the attribute table - identifies that the attribute is an alias
*/
public final static String ALIASABLE_ATTRIBUTE_KEY_2 = "Alias";
private final static String ERROR_MSG_DUPLICATE_ALIAS="email.server.err.duplicate_alias";
/**
* @param nodeService Alfresco Node Service
*/
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
/**
* @param policyComponent Alfresco Policy Component
*/
public void setPolicyComponent(PolicyComponent policyComponent)
{
this.policyComponent = policyComponent;
}
/**
* Spring initilaise method used to register the policy behaviours
*/
public void init()
{
PropertyCheck.mandatory(this, "policyComponent", policyComponent);
PropertyCheck.mandatory(this, "nodeService", nodeService);
PropertyCheck.mandatory(this, "attributeService", attributeService);
// Register the policy behaviours
policyComponent.bindClassBehaviour(OnAddAspectPolicy.QNAME,
EmailServerModel.ASPECT_ALIASABLE,
new JavaBehaviour(this, "onAddAspect", NotificationFrequency.FIRST_EVENT));
policyComponent.bindClassBehaviour(BeforeRemoveAspectPolicy.QNAME,
EmailServerModel.ASPECT_ALIASABLE,
new JavaBehaviour(this, "beforeRemoveAspect", NotificationFrequency.FIRST_EVENT));
policyComponent.bindClassBehaviour(OnUpdatePropertiesPolicy.QNAME,
EmailServerModel.ASPECT_ALIASABLE,
new JavaBehaviour(this, "onUpdateProperties"));
policyComponent.bindClassBehaviour(BeforeDeleteNodePolicy.QNAME,
EmailServerModel.ASPECT_ALIASABLE,
new JavaBehaviour(this, "beforeDeleteNode"));
policyComponent.bindClassBehaviour(CopyServicePolicies.OnCopyNodePolicy.QNAME,
EmailServerModel.ASPECT_ALIASABLE,
new JavaBehaviour(this, "getCopyCallback"));
}
/**
* method to normalise an email alias.
*
* Currently this involves trimmimg and lower casing, but it may change in future
*
* @param value
* @return the normalised value.
*/
public static String normaliseAlias(String value)
{
if(value != null)
{
return value.toLowerCase();
}
return value;
}
/**
* Set the email alias for the specified node.
*
* If the rule is broken, AlfrescoRuntimeException will be thrown.
*
* @param nodeRef Reference to target node
* @param alias Alias that we want to set to the target node
* @exception AlfrescoRuntimeException if the <b>alias</b> property is duplicated by another node.
*/
public void addAlias(NodeRef nodeRef, String alias)
{
if(logger.isDebugEnabled())
{
logger.debug("add email alias nodeRef:" + nodeRef + ", alias:" + alias);
}
// first try to see if the new alias is in use elsewhere?
try
{
attributeService.createAttribute(nodeRef, ALIASABLE_ATTRIBUTE_KEY_1, ALIASABLE_ATTRIBUTE_KEY_2, normaliseAlias(alias));
}
catch (DuplicateAttributeException de)
{
throw AlfrescoRuntimeException.create(ERROR_MSG_DUPLICATE_ALIAS, normaliseAlias(alias));
}
}
/**
* remove the specified alias
* @param alias to remove
*/
public void removeAlias(String alias)
{
if(logger.isDebugEnabled())
{
logger.debug("remove email alias alias:" + alias);
}
attributeService.removeAttribute(ALIASABLE_ATTRIBUTE_KEY_1, ALIASABLE_ATTRIBUTE_KEY_2, normaliseAlias(alias));
}
/**
* Get a node ref by its email alias
* @return the node ref, or null if there is no node for that alias
*/
public NodeRef getByAlias(String alias)
{
Serializable value = attributeService.getAttribute(ALIASABLE_ATTRIBUTE_KEY_1, ALIASABLE_ATTRIBUTE_KEY_2, normaliseAlias(alias));
if(value instanceof NodeRef)
{
return (NodeRef)value;
}
return null;
}
/**
* @see org.alfresco.repo.node.NodeServicePolicies.OnAddAspectPolicy#onAddAspect(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName)
* @exception AlfrescoRuntimeException Throws if the <b>alias</b> property is duplicated.
*/
public void onAddAspect(NodeRef nodeRef, QName aspectTypeQName)
{
Object alias = nodeService.getProperty(nodeRef, EmailServerModel.PROP_ALIAS);
if (alias != null)
{
addAlias(nodeRef, alias.toString());
}
}
/**
* @see org.alfresco.repo.node.NodeServicePolicies.OnUpdatePropertiesPolicy#onUpdateProperties(org.alfresco.service.cmr.repository.NodeRef, java.util.Map, java.util.Map)
* @exception AlfrescoRuntimeException Throws if the <b>alias</b> property is duplicated.
*/
public void onUpdateProperties(NodeRef nodeRef, Map<QName, Serializable> before, Map<QName, Serializable> after)
{
String oldAlias = (String)before.get(EmailServerModel.PROP_ALIAS);
String newAlias = (String)after.get(EmailServerModel.PROP_ALIAS);
if(oldAlias != null && newAlias != null && (normaliseAlias(oldAlias)).equals(normaliseAlias(newAlias)))
{
// alias has not changed
return;
}
if (newAlias != null)
{
addAlias(nodeRef, newAlias);
}
if(oldAlias != null)
{
removeAlias(oldAlias);
}
}
@Override
public void beforeRemoveAspect(NodeRef nodeRef, QName aspectTypeQName)
{
String alias = (String)nodeService.getProperty(nodeRef, EmailServerModel.PROP_ALIAS);
if(alias != null)
{
removeAlias(alias);
}
}
@Override
public void beforeDeleteNode(NodeRef nodeRef)
{
String alias = (String)nodeService.getProperty(nodeRef, EmailServerModel.PROP_ALIAS);
if(alias != null)
{
removeAlias(alias);
}
}
@Override
public CopyBehaviourCallback getCopyCallback(QName classRef,
CopyDetails copyDetails)
{
return AliasableAspectCopyBehaviourCallback.INSTANCE;
}
public void setAttributeService(AttributeService attributeService)
{
this.attributeService = attributeService;
}
public AttributeService getAttributeService()
{
return attributeService;
}
}

View File

@@ -0,0 +1,74 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.email.server;
import java.io.Serializable;
import java.util.Collections;
import java.util.Map;
import org.alfresco.repo.copy.CopyBehaviourCallback;
import org.alfresco.repo.copy.CopyDetails;
import org.alfresco.repo.copy.DefaultCopyBehaviourCallback;
import org.alfresco.service.namespace.QName;
public class AliasableAspectCopyBehaviourCallback extends DefaultCopyBehaviourCallback
{
static final CopyBehaviourCallback INSTANCE = new AliasableAspectCopyBehaviourCallback();
/**
* Disallows copying of the {@link EmailServerModel#ASPECT_ALIASABLE} aspect.
*/
@Override
public boolean getMustCopy(QName classQName, CopyDetails copyDetails)
{
if (classQName.equals(EmailServerModel.ASPECT_ALIASABLE))
{
return false;
}
else
{
return true;
}
}
/**
* Prevents copying off the {@link org.alfresco.model.ContentModel#PROP_NAME <b>cm:name</b>} property.
*/
@Override
public Map<QName, Serializable> getCopyProperties(
QName classQName,
CopyDetails copyDetails,
Map<QName, Serializable> properties)
{
if (classQName.equals(EmailServerModel.ASPECT_ALIASABLE))
{
return Collections.emptyMap();
}
return properties;
}
}

View File

@@ -0,0 +1,431 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.email.server;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.repo.security.authentication.AuthenticationComponent;
import org.alfresco.repo.security.authentication.AuthenticationException;
import org.alfresco.service.cmr.email.EmailMessageException;
import org.alfresco.service.cmr.email.EmailService;
import org.springframework.extensions.surf.util.AbstractLifecycleBean;
import org.alfresco.util.PropertyCheck;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Base implementation of an email server.
* @since 2.2
*/
public abstract class EmailServer extends AbstractLifecycleBean
{
private static final String ERR_SENDER_BLOCKED = "email.server.err.sender_blocked";
private static final String ERR_FROM_SYNTAX_INCORRECT = "email.server.err.from_syntax";
private boolean enabled;
private String domain;
private int port;
private int maxConnections;
private Set<String> blockedSenders;
private Set<String> allowedSenders;
private boolean hideTLS = false;
private boolean enableTLS = true;
private boolean requireTLS = false;
private boolean authenticate = false;
private EmailService emailService;
private AuthenticationComponent authenticationComponent;
private String unknownUser;
protected EmailServer()
{
this.enabled = false;
this.port = 25;
this.domain = null;
this.maxConnections = 3;
this.blockedSenders = new HashSet<String>(23);
this.allowedSenders = new HashSet<String>(23);
}
/**
* @param enabled Enable/disable server
*/
public void setEnabled(boolean enabled)
{
this.enabled = enabled;
}
protected String getDomain()
{
return domain;
}
public void setDomain(String domain)
{
this.domain = domain;
}
protected int getPort()
{
return port;
}
/**
* @param port SMTP port (25 is default)
*/
public void setPort(int port)
{
this.port = port;
}
/**
* Returns the maximum number of connection accepted by the server.
* @return the maximum number of connections
*/
protected int getMaxConnections()
{
return maxConnections;
}
/**
* Sets the maximum number of connection accepted by the server
* @param maxConnections
*/
public void setMaxConnections(int maxConnections)
{
this.maxConnections = maxConnections;
}
/**
* Set the blocked senders as a comma separated list. The entries will be trimmed of
* all whitespace.
*
* @param blockedSenders a comman separated list of blocked senders
*/
public void setBlockedSenders(String blockedSenders)
{
StringTokenizer tokenizer = new StringTokenizer(blockedSenders, ",", false);
while (tokenizer.hasMoreTokens())
{
String sender = tokenizer.nextToken().trim();
this.blockedSenders.add(sender);
}
}
/**
* @param blockedSenders a list of senders that are not allowed to email in
*/
public void setBlockedSendersList(List<String> blockedSenders)
{
this.blockedSenders.addAll(blockedSenders);
}
/**
* Set the allowed senders as a comma separated list. The entries will be trimmed of
* all whitespace.
*
* @param allowedSenders a comman separated list of blocked senders
*/
public void setAllowedSenders(String allowedSenders)
{
StringTokenizer tokenizer = new StringTokenizer(allowedSenders, ",", false);
while (tokenizer.hasMoreTokens())
{
String sender = tokenizer.nextToken().trim();
if (sender.length() == 0)
{
// Nothing
continue;
}
this.allowedSenders.add(sender);
}
}
/**
* @param allowedSenders a list of senders that are allowed to email in
*/
public void setAllowedSendersList(List<String> allowedSenders)
{
this.allowedSenders.addAll(allowedSenders);
}
/**
* @return the service interface to interact with
*/
protected EmailService getEmailService()
{
return emailService;
}
/**
* @param emailService the service interface to interact with
*/
public void setEmailService(EmailService emailService)
{
this.emailService = emailService;
}
/**
* Used only for check "isNullReversePatAllowed".
* @param unknownUser authority name
*/
public void setUnknownUser(String unknownUser)
{
this.unknownUser = unknownUser;
}
protected boolean isNullReversePatAllowed()
{
return isAuthenticate() || (unknownUser != null && !unknownUser.isEmpty());
}
/**
* Filter incoming message by its sender e-mail address.
*
* @param sender An e-mail address of sender
* @throws EmailMessageException if the e-mail is rejected accordingly with blocked and allowed lists
*/
protected void filterSender(String sender)
{
if (sender == null)
{
if (isNullReversePatAllowed())
{
// allow null reverse-path: e.g.: an undeliverable mail response
return;
}
else
{
throw new EmailMessageException(ERR_FROM_SYNTAX_INCORRECT);
}
}
// Check if the sender is in the blocked list
for (String blockedSender : blockedSenders)
{
if (sender.matches(blockedSender))
{
throw new EmailMessageException(ERR_SENDER_BLOCKED, sender);
}
}
// If there are any restrictions in the allowed list, then a positive match
// is absolutely required
if (!allowedSenders.isEmpty())
{
boolean matched = false;
for (String allowedSender : allowedSenders)
{
if (sender.matches(allowedSender))
{
matched = true;
break;
}
}
if (!matched)
{
throw new EmailMessageException(ERR_SENDER_BLOCKED, sender);
}
}
}
/**
* Method is called when server is starting up.
*/
public abstract void startup();
/**
* Method is called when server is shutting down.
*/
public abstract void shutdown();
/**
* {@inheritDoc}
*/
@Override
protected void onBootstrap(ApplicationEvent event)
{
if (!enabled)
{
return;
}
// Check properties
PropertyCheck.mandatory(this, "domain", domain);
if (port <= 0 || port > 65535)
{
throw new AlfrescoRuntimeException("Property 'port' is incorrect");
}
PropertyCheck.mandatory(this, "emailService", emailService);
// Startup
startup();
}
/**
* {@inheritDoc}
*/
@Override
protected void onShutdown(ApplicationEvent event)
{
if (enabled)
{
shutdown();
}
}
private static volatile Boolean stop = false;
public static void main(String[] args)
{
if (args.length == 0)
{
usage();
return;
}
try (AbstractApplicationContext context = new ClassPathXmlApplicationContext(args))
{
if (!context.containsBean("emailServer"))
{
usage();
return;
}
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run()
{
stop = true;
synchronized (stop)
{
stop.notifyAll();
}
}
});
System.out.println("Use Ctrl-C to shutdown EmailServer");
while (!stop)
{
synchronized (stop)
{
stop.wait();
}
}
}
catch (BeansException e)
{
System.err.println("Error creating context: " + e);
usage();
}
catch (InterruptedException e)
{
}
}
private static void usage()
{
System.err.println("Use: EmailServer configLocation1, configLocation2, ...");
System.err.println("\t configLocation - spring xml configs with EmailServer related beans (emailServer, emailServerConfiguration, emailService)");
}
/**
* authenticate with a user/password
* @param userName
* @param password
* @return true - authenticated
*/
protected boolean authenticateUserNamePassword(String userName, char[] password)
{
try
{
getAuthenticationComponent().authenticate(userName, password);
return true;
}
catch (AuthenticationException e)
{
return false;
}
}
/** Hide the TLS (Trusted Login Session) option
*
* @param hideTLS
*/
public void setHideTLS(boolean hideTLS)
{
this.hideTLS = hideTLS;
}
public boolean isHideTLS()
{
return hideTLS;
}
public void setEnableTLS(boolean enableTLS)
{
this.enableTLS = enableTLS;
}
public boolean isEnableTLS()
{
return enableTLS;
}
public void setRequireTLS(boolean requireTLS)
{
this.requireTLS = requireTLS;
}
public boolean isRequireTLS()
{
return requireTLS;
}
public void setAuthenticate(boolean enableAuthentication)
{
this.authenticate = enableAuthentication;
}
public boolean isAuthenticate()
{
return authenticate;
}
public void setAuthenticationComponent(AuthenticationComponent authenticationComponent)
{
this.authenticationComponent = authenticationComponent;
}
public AuthenticationComponent getAuthenticationComponent()
{
return authenticationComponent;
}
}

View File

@@ -0,0 +1,50 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.email.server;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
/**
* Class defines the costants for Email Server Model
*
* @see alfresco/model/emailServerModel.xml
* @author Yan O
* @since 2.2
*/
public interface EmailServerModel
{
// Attachable aspect
static final QName ASPECT_ATTACHED = QName.createQName(NamespaceService.EMAILSERVER_MODEL_URI, "attached");
static final QName ASSOC_ATTACHMENT = QName.createQName(NamespaceService.EMAILSERVER_MODEL_URI, "attachment");
// Aliasable aspect
static final QName ASPECT_ALIASABLE = QName.createQName(NamespaceService.EMAILSERVER_MODEL_URI, "aliasable");
static final QName PROP_ALIAS = QName.createQName(NamespaceService.EMAILSERVER_MODEL_URI, "alias");
}

View File

@@ -0,0 +1,575 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.email.server;
import java.util.Collection;
import java.util.Map;
import javax.mail.internet.InternetAddress;
import org.alfresco.email.server.handler.EmailMessageHandler;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.node.integrity.IntegrityException;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.security.permissions.AccessDeniedException;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.cmr.attributes.AttributeService;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.dictionary.TypeDefinition;
import org.alfresco.service.cmr.email.EmailDelivery;
import org.alfresco.service.cmr.email.EmailMessage;
import org.alfresco.service.cmr.email.EmailMessageException;
import org.alfresco.service.cmr.email.EmailService;
import org.alfresco.service.cmr.repository.DuplicateChildNodeNameException;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
import org.alfresco.service.cmr.search.ResultSet;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.cmr.security.AuthorityService;
import org.alfresco.service.cmr.security.AuthorityType;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.PropertyCheck;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.extensions.surf.util.ParameterCheck;
/**
* Concrete email service implementation. This is responsible for routing the
* emails into the server.
*
* @since 2.2
*/
public class EmailServiceImpl implements EmailService
{
private static final String ERR_INBOUND_EMAIL_DISABLED = "email.server.err.inbound_mail_disabled";
private static final String ERR_INVALID_SUBJECT = "email.server.err.invalid_subject";
private static final String ERR_ACCESS_DENIED = "email.server.err.access_denied";
private static final String ERR_UNKNOWN_SOURCE_ADDRESS = "email.server.err.unknown_source_address";
private static final String ERR_USER_NOT_EMAIL_CONTRIBUTOR = "email.server.err.user_not_email_contributor";
private static final String ERR_INVALID_NODE_ADDRESS = "email.server.err.invalid_node_address";
private static final String ERR_HANDLER_NOT_FOUND = "email.server.err.handler_not_found";
private NamespaceService namespaceService;
private NodeService nodeService;
private SearchService searchService;
private RetryingTransactionHelper retryingTransactionHelper;
private AuthorityService authorityService;
private DictionaryService dictionaryService;
private AttributeService attributeService;
/**
* The authority that needs to contain the users and groups
* who are allowed to contribute email.
*/
private String emailContributorsAuthority="EMAIL_CONTRIBUTORS";
private static Log logger = LogFactory.getLog(EmailServiceImpl.class);
private boolean emailInboundEnabled;
/** Login of user that is set as unknown. */
private String unknownUser;
/** List of message handlers */
private Map<String, EmailMessageHandler> emailMessageHandlerMap;
public void init()
{
PropertyCheck.mandatory(this, "namespaceService", namespaceService);
PropertyCheck.mandatory(this, "dictionaryService", getDictionaryService());
PropertyCheck.mandatory(this, "searchService", searchService);
PropertyCheck.mandatory(this, "authorityService", authorityService);
PropertyCheck.mandatory(this, "emailMessageHandlerMap", emailMessageHandlerMap);
PropertyCheck.mandatory(this, "attributeService", getAttributeService());
}
/**
*
* @param namespaceService the service to resolve namespace prefixes
*/
public void setNamespaceService(NamespaceService namespaceService)
{
this.namespaceService = namespaceService;
}
/**
* @param nodeService Alfresco Node Service
*/
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
/**
* @param searchService Alfresco Search Service
*/
public void setSearchService(SearchService searchService)
{
this.searchService = searchService;
}
/**
* @param retryingTransactionHelper Alfresco RetryingTransactionHelper
*/
public void setRetryingTransactionHelper(RetryingTransactionHelper retryingTransactionHelper)
{
this.retryingTransactionHelper = retryingTransactionHelper;
}
/**
* @param authorityService Alfresco authority service
*/
public void setAuthorityService(AuthorityService authorityService)
{
this.authorityService = authorityService;
}
/**
* @return Map of message handlers
*/
public Map<String, EmailMessageHandler> getEmailMessageHandlerMap()
{
return emailMessageHandlerMap;
}
/**
* @param emailMessageHandlerMap Map of message handlers
*/
public void setEmailMessageHandlerMap(Map<String, EmailMessageHandler> emailMessageHandlerMap)
{
this.emailMessageHandlerMap = emailMessageHandlerMap;
}
/**
* @param unknownUser Login of user that should be set as unknown.
*/
public void setUnknownUser(String unknownUser)
{
this.unknownUser = unknownUser;
}
public void setEmailInboundEnabled(boolean mailInboundEnabled)
{
this.emailInboundEnabled = mailInboundEnabled;
}
/**
* {@inheritDoc}
*/
public void importMessage(EmailDelivery delivery, EmailMessage message)
{
processMessage(delivery, null, message);
}
/**
* {@inheritDoc}
*/
public void importMessage(EmailDelivery delivery, NodeRef nodeRef, EmailMessage message)
{
processMessage(delivery, nodeRef, message);
}
/**
* Process the message. Method is called after filtering by sender's address.
* @param delivery - who gets the message and who is it from (may be different from the contents of the message)
* @param nodeRef Addressed node (target node).
* @param message Email message
* @throws EmailMessageException Any exception occured inside the method will be converted and thrown as <code>EmailMessageException</code>
*/
private void processMessage(final EmailDelivery delivery, final NodeRef nodeRef, final EmailMessage message)
{
if (!emailInboundEnabled)
{
throw new EmailMessageException(ERR_INBOUND_EMAIL_DISABLED);
}
try
{
// Get the username for the process using the system account
final RetryingTransactionCallback<String> getUsernameCallback = new RetryingTransactionCallback<String>()
{
public String execute() throws Throwable
{
String userName = null;
userName = getUsername(delivery.getFrom());
if(userName == null)
{
if(logger.isDebugEnabled())
{
logger.debug("unable to find user for from: " + delivery.getFrom() + ",trying message.from next");
}
userName = getUsername(message.getFrom());
}
if(logger.isDebugEnabled())
{
logger.debug("userName = : " + userName);
}
if (userName == null)
{
if(unknownUser.isEmpty())
{
if(logger.isDebugEnabled())
{
logger.debug("unable to find user for from: " + message.getFrom());
}
throw new EmailMessageException(ERR_UNKNOWN_SOURCE_ADDRESS, message.getFrom());
}
else
{
if(logger.isDebugEnabled())
{
logger.debug("unable to find user for from - return anonymous: ");
}
userName = unknownUser;
}
}
// Ensure that the user is part of the Email Contributors group
if (userName == null || !isEmailContributeUser(userName))
{
throw new EmailMessageException(ERR_USER_NOT_EMAIL_CONTRIBUTOR, userName);
}
return userName;
}
};
RunAsWork<String> getUsernameRunAsWork = new RunAsWork<String>()
{
public String doWork() throws Exception
{
return retryingTransactionHelper.doInTransaction(getUsernameCallback, false);
}
};
String username;
if(delivery.getAuth() != null)
{
// The user has authenticated.
username = delivery.getAuth();
logger.debug("user has already authenticated as:" + username);
}
else
{
// Need to faff with old message stuff.
username = AuthenticationUtil.runAs(getUsernameRunAsWork, AuthenticationUtil.SYSTEM_USER_NAME);
}
// Process the message using the username's account
final RetryingTransactionCallback<Object> processMessageCallback = new RetryingTransactionCallback<Object>()
{
public Object execute() throws Throwable
{
//String recipient = message.getTo();
String recipient = delivery.getRecipient();
NodeRef targetNodeRef = null;
if (nodeRef == null)
{
targetNodeRef = getTargetNode(recipient);
}
else
{
targetNodeRef = nodeRef;
}
EmailMessageHandler messageHandler = getMessageHandler(targetNodeRef);
try
{
messageHandler.processMessage(targetNodeRef, message);
}
catch (DuplicateChildNodeNameException e)
{
throw new ConcurrencyFailureException(e.getMessage());
}
return null;
}
};
RunAsWork<Object> processMessageRunAsWork = new RunAsWork<Object>()
{
public Object doWork() throws Exception
{
return retryingTransactionHelper.doInTransaction(processMessageCallback, false);
}
};
AuthenticationUtil.runAs(processMessageRunAsWork, username);
}
catch (EmailMessageException e)
{
// These are email-specific errors
throw e;
}
catch (AccessDeniedException e)
{
throw new EmailMessageException(ERR_ACCESS_DENIED, delivery.getFrom(), delivery.getRecipient());
}
catch (IntegrityException e)
{
throw new EmailMessageException(ERR_INVALID_SUBJECT);
}
catch (Throwable e)
{
throw new AlfrescoRuntimeException("Email message processing failed", e);
}
}
/**
* @param nodeRef Target node
* @return Handler that can process message addressed to specific node (target node).
* @throws EmailMessageException is thrown if a suitable message handler isn't found.
*/
private EmailMessageHandler getMessageHandler(NodeRef nodeRef)
{
ParameterCheck.mandatory("nodeRef", nodeRef);
QName nodeTypeQName = nodeService.getType(nodeRef);
String prefixedNodeTypeStr = nodeTypeQName.toPrefixString(namespaceService);
EmailMessageHandler handler = emailMessageHandlerMap.get(prefixedNodeTypeStr);
if( handler == null)
{
if(logger.isDebugEnabled())
{
logger.debug("did not find a handler for type:" + prefixedNodeTypeStr);
}
// not a direct match on type
// need to check the super-types (if any) of the target node
TypeDefinition typeDef = dictionaryService.getType(nodeTypeQName);
while(typeDef != null)
{
QName parentName = typeDef.getParentName();
if(parentName != null)
{
String prefixedSubTypeStr = parentName.toPrefixString(namespaceService);
handler = emailMessageHandlerMap.get(prefixedSubTypeStr);
if(handler != null)
{
if(logger.isDebugEnabled())
{
logger.debug("found a handler for a subtype:" + prefixedSubTypeStr);
}
return handler;
}
}
typeDef = dictionaryService.getType(parentName);
}
}
if (handler == null)
{
throw new EmailMessageException(ERR_HANDLER_NOT_FOUND, prefixedNodeTypeStr);
}
return handler;
}
/**
* Method determines target node by recipient e-mail address.
*
* @param recipient An e-mail address of a recipient
* @return Reference to the target node
* @throws EmailMessageException is thrown if the target node couldn't be determined by some reasons.
*/
private NodeRef getTargetNode(String recipient)
{
if (logger.isDebugEnabled())
{
logger.debug("getTarget node for" + recipient);
}
if (recipient == null || recipient.length() == 0)
{
throw new EmailMessageException(ERR_INVALID_NODE_ADDRESS, recipient);
}
String[] parts = recipient.split("@");
if (parts.length != 2)
{
throw new EmailMessageException(ERR_INVALID_NODE_ADDRESS, recipient);
}
String alias = parts[0];
/*
* First lookup via the attributes service
*
* Then lookup by search service - may be old data prior to attributes service
*
* Then see if we can find a node by dbid
*/
// Lookup via the attributes service
NodeRef ref = (NodeRef)getAttributeService().getAttribute(AliasableAspect.ALIASABLE_ATTRIBUTE_KEY_1, AliasableAspect.ALIASABLE_ATTRIBUTE_KEY_2, AliasableAspect.normaliseAlias(alias));
if(ref != null)
{
if(logger.isDebugEnabled())
{
logger.debug("found email alias via attribute service alias =" + alias);
}
return ref;
}
// Ok, alias wasn't found, let's try to interpret recipient address as 'node-bdid' value
try
{
Long nodeId = Long.parseLong(parts[0]);
// Get recipient by system account
NodeRef byNodeId = AuthenticationUtil.runAsSystem(() -> nodeService.getNodeRef(nodeId));
if(byNodeId != null)
{
if(logger.isDebugEnabled())
{
logger.debug("found email alias via node service =" + alias);
}
return byNodeId;
}
}
catch (NumberFormatException ne)
{
}
throw new EmailMessageException(ERR_INVALID_NODE_ADDRESS, recipient);
}
/**
* Authenticate in Alfresco repository by sender's e-mail address.
*
* @param from Sender's email address
* @return User name or null if the user does not exist.
* @throws EmailMessageException Exception will be thrown if authentication is failed.
*/
private String getUsername(String from)
{
String userName = null;
if(from == null || from.length()==0)
{
return null;
}
if(logger.isDebugEnabled())
{
logger.debug("getUsername from: " + from);
}
StoreRef storeRef = new StoreRef(StoreRef.PROTOCOL_WORKSPACE, "SpacesStore");
String query = "TYPE:cm\\:person AND =@cm\\:email:\"" + from + "\"";
ResultSet resultSet = searchService.query(storeRef, SearchService.LANGUAGE_FTS_ALFRESCO, query);
try
{
if (resultSet == null || resultSet.length() == 0)
{
return null;
}
if (resultSet.length() > 1)
{
if(logger.isWarnEnabled())
{
logger.warn("found more as one result for email '" + from + "'. The first will be used");
}
}
NodeRef userNode = resultSet.getNodeRef(0);
if (nodeService.exists(userNode))
{
userName = DefaultTypeConverter.INSTANCE.convert(
String.class,
nodeService.getProperty(userNode, ContentModel.PROP_USERNAME));
if(logger.isDebugEnabled())
{
logger.debug("found username: " + userName);
}
}
else
{
// The Lucene index returned a dead result
throw new EmailMessageException(ERR_UNKNOWN_SOURCE_ADDRESS, from);
}
}
finally
{
if(resultSet != null)
{
resultSet.close();
}
}
return userName;
}
/**
* Check that the user is the member in <b>EMAIL_CONTRIBUTORS</b> group
*
* @param userName Alfresco user name
* @return True if the user is member of the group
* @exception EmailMessageException Exception will be thrown if the <b>EMAIL_CONTRIBUTORS</b> group isn't found
*/
private boolean isEmailContributeUser(String userName)
{
return this.authorityService.getAuthoritiesForUser(userName).contains(
authorityService.getName(AuthorityType.GROUP, getEmailContributorsAuthority()));
}
public void setEmailContributorsAuthority(
String emailContributorsAuthority)
{
this.emailContributorsAuthority = emailContributorsAuthority;
}
public String getEmailContributorsAuthority()
{
return emailContributorsAuthority;
}
public void setDictionaryService(DictionaryService dictionaryService)
{
this.dictionaryService = dictionaryService;
}
public DictionaryService getDictionaryService()
{
return dictionaryService;
}
public void setAttributeService(AttributeService attributeService)
{
this.attributeService = attributeService;
}
public AttributeService getAttributeService()
{
return attributeService;
}
}

View File

@@ -0,0 +1,101 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.email.server;
import org.alfresco.email.server.impl.subetha.SubethaEmailMessage;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.service.cmr.email.EmailDelivery;
import org.alfresco.service.cmr.email.EmailMessage;
import org.alfresco.service.cmr.email.EmailService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.springframework.extensions.surf.util.AbstractLifecycleBean;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.context.ApplicationEvent;
import org.springframework.remoting.rmi.RmiClientInterceptor;
/**
* @author Michael Shavnev
* @since 2.2
*/
public class EmailServiceRemotable extends AbstractLifecycleBean implements EmailService
{
private String rmiRegistryHost;
private int rmiRegistryPort;
private EmailService emailServiceProxy;
public void setRmiRegistryHost(String rmiRegistryHost)
{
this.rmiRegistryHost = rmiRegistryHost;
}
public void setRmiRegistryPort(int rmiRegistryPort)
{
this.rmiRegistryPort = rmiRegistryPort;
}
public void importMessage(EmailDelivery delivery, EmailMessage message)
{
if (message instanceof SubethaEmailMessage)
{
((SubethaEmailMessage) message).setRmiRegistry(rmiRegistryHost, rmiRegistryPort);
}
emailServiceProxy.importMessage(delivery, message);
}
public void importMessage(EmailDelivery delivery, NodeRef nodeRef, EmailMessage message)
{
if (message instanceof SubethaEmailMessage)
{
((SubethaEmailMessage) message).setRmiRegistry(rmiRegistryHost, rmiRegistryPort);
}
emailServiceProxy.importMessage(delivery, nodeRef, message);
}
@Override
protected void onBootstrap(ApplicationEvent event)
{
if (rmiRegistryHost == null)
{
throw new AlfrescoRuntimeException("Property 'rmiRegistryHost' not set");
}
if (rmiRegistryPort == 0)
{
throw new AlfrescoRuntimeException("Property 'rmiRegistryPort' not set");
}
RmiClientInterceptor rmiClientInterceptor = new RmiClientInterceptor();
rmiClientInterceptor.setRefreshStubOnConnectFailure(true);
rmiClientInterceptor.setServiceUrl("rmi://" + rmiRegistryHost + ":" + rmiRegistryPort + "/emailService");
emailServiceProxy = (EmailService) ProxyFactory.getProxy(EmailService.class, rmiClientInterceptor);
}
@Override
protected void onShutdown(ApplicationEvent event)
{
}
}

View File

@@ -0,0 +1,512 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.email.server.handler;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.content.encoding.ContentCharsetFinder;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.email.EmailMessage;
import org.alfresco.service.cmr.email.EmailMessagePart;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.MimetypeService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Abstract class implements common logic for processing email messages.
*
* @author maxim
* @since 2.2
*/
public abstract class AbstractEmailMessageHandler implements EmailMessageHandler
{
private static final Log log = LogFactory.getLog(EmailMessageHandler.class);
private DictionaryService dictionaryService;
private NodeService nodeService;
private ContentService contentService;
private MimetypeService mimetypeService;
private static Log logger = LogFactory.getLog(AbstractEmailMessageHandler.class);
/**
* @return Alfresco Content Service.
*/
protected ContentService getContentService()
{
return contentService;
}
/**
* @param contentService Alfresco Content Service.
*/
public void setContentService(ContentService contentService)
{
this.contentService = contentService;
}
/**
* @return the Alfresco dictionary service
*/
protected DictionaryService getDictionaryService()
{
return dictionaryService;
}
/**
* @param dictionaryService Alfresco dictionary service
*/
public void setDictionaryService(DictionaryService dictionaryService)
{
this.dictionaryService = dictionaryService;
}
/**
* @return Alfresco Node Service.
*/
protected NodeService getNodeService()
{
return nodeService;
}
/**
* @param nodeService Alfresco Node Service.
*/
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
/**
* @return the service used to determine mimeypte and encoding
*/
protected MimetypeService getMimetypeService()
{
return mimetypeService;
}
/**
* @param mimetypeService the the service to determine mimetype and encoding
*/
public void setMimetypeService(MimetypeService mimetypeService)
{
this.mimetypeService = mimetypeService;
}
// /**
// * @param to Email address which user part specifies node-dbid
// * @return Referance to requested node.
// * @throws InvalidArgumentException The exception is thrown if input string has incorrect format or empty.
// */
// protected NodeRef getTargetNode(String to) throws InvalidArgumentException
// {
// if (to == null || to.length() == 0)
// {
// throw new InvalidArgumentException("Input string has to contain email address.");
// }
// String[] parts = to.split("@");
// if (parts.length != 2)
// {
// throw new InvalidArgumentException("Incorrect email address format.");
// }
// try
// {
// Long dbId = Long.parseLong(parts[0]);
// return nodeService.getNodeRef(dbId);
// }
// catch (NumberFormatException e)
// {
// return null;
// }
// }
// /**
// * Write the content to the node as MIMETYPE TEXT PLAIN.
// *
// * @param nodeRef Target node
// * @param content Content
// */
// protected void writeContent(NodeRef nodeRef, String content)
// {
// writeContent(nodeRef, content, MimetypeMap.MIMETYPE_TEXT_PLAIN);
// }
/**
* Write the string as content to the node.
*
* @param nodeRef Target node.
* @param content Text for writting.
* @param mimetype MIME content type. For exaple you can set this parameter to "text/html" or "text/xml", etc.
*/
protected void writeContent(NodeRef nodeRef, String content, String mimetype)
{
try
{
InputStream inputStream = new ByteArrayInputStream(content.getBytes("UTF-8"));
writeContent(nodeRef, inputStream, mimetype, "UTF-8");
}
catch (UnsupportedEncodingException e)
{
throw new AlfrescoRuntimeException("Failed to write content", e);
}
}
/**
* Write content to the node from InputStream.
*
* @param nodeRef Target node.
* @param content Content stream.
* @param mimetype MIME content type.
* @param encoding Encoding. Can be null for text based content, n which case the best guess.
*/
protected void writeContent(NodeRef nodeRef, InputStream content, String mimetype, String encoding)
{
InputStream bis = new BufferedInputStream(content, 4092);
// Only guess the encoding if it has not been supplied
if (encoding == null)
{
if (mimetypeService.isText(mimetype))
{
ContentCharsetFinder charsetFinder = mimetypeService.getContentCharsetFinder();
encoding = charsetFinder.getCharset(bis, mimetype).name();
}
else
{
encoding = "UTF-8";
}
}
if (log.isDebugEnabled())
{
log.debug("Write content (MimeType=\"" + mimetype + "\", Encoding=\"" + encoding + "\"");
}
ContentService contentService = getContentService();
ContentWriter writer = contentService.getWriter(nodeRef, ContentModel.PROP_CONTENT, true);
writer.setMimetype(mimetype);
writer.setEncoding(encoding);
writer.putContent(bis);
}
/**
* Add emailed aspect to the specified node.
*
* @param nodeRef Target node.
* @param message Mail message that will be used for extracting necessary information
*/
protected void addEmailedAspect(NodeRef nodeRef, EmailMessage message)
{
/*
* TODO - get rid of this and use the RFC822 metadata extractor instead.
*/
Map<QName, Serializable> emailProps = new HashMap<QName, Serializable>();
emailProps.put(ContentModel.PROP_SENTDATE, message.getSentDate());
emailProps.put(ContentModel.PROP_ORIGINATOR, message.getFrom());
emailProps.put(ContentModel.PROP_ADDRESSEE, message.getTo());
emailProps.put(ContentModel.PROP_ADDRESSEES, (Serializable)message.getCC());
emailProps.put(ContentModel.PROP_SUBJECT, message.getSubject());
nodeService.addAspect(nodeRef, ContentModel.ASPECT_EMAILED, emailProps);
/*
* MER
* Can't add IMAP_CONTENT here since that means the body of the message is a mime message.
*/
//Map<QName, Serializable> imapProps = new HashMap<QName, Serializable>();
//emailProps.put(ImapModel.PROP_MESSAGE_FROM, message.getFrom());
//emailProps.put(ImapModel.PROP_MESSAGE_TO, message.getTo());
//emailProps.put(ImapModel.PROP_MESSAGE_CC, (Serializable)message.getCC());
//emailProps.put(ImapModel.PROP_MESSAGE_SUBJECT, message.getSubject());
//nodeService.addAspect(nodeRef, ImapModel.ASPECT_IMAP_CONTENT, imapProps);
if (log.isDebugEnabled())
{
log.debug("Emailed aspect has been added.");
}
}
/**
* Add new node into Alfresco repository with specified parameters. Node content isn't added.
*
* @param nodeService Alfresco Node Service
* @param parent Parent node
* @param name Name of the new node
* @param overwrite if true then overwrite an existing node with the same name. if false the name is changed to make it unique.
* @param assocType Association type that should be set between parent node and the new one.
* @return Reference to created node
*/
protected NodeRef addContentNode(NodeService nodeService, NodeRef parent, String name, QName assocType, boolean overwrite)
{
String workingName = encodeSubject(name);
// Need to work out a new safe name.
String baseName = FilenameUtils.getBaseName(workingName);
String extension = FilenameUtils.getExtension(workingName);
if(logger.isDebugEnabled())
{
logger.debug("addContentNode name:" + workingName);
}
for(int counter = 1; counter < 10000; counter++)
{
QName safeQName = QName.createQNameWithValidLocalName(NamespaceService.CONTENT_MODEL_1_0_URI, workingName);
NodeRef childNodeRef = nodeService.getChildByName(parent, ContentModel.ASSOC_CONTAINS, workingName);
if (childNodeRef != null)
{
if(overwrite)
{
if(logger.isDebugEnabled())
{
logger.debug("overwriting existing node :" + workingName);
}
// Node already exists
// The node is present already. Make sure the name case is correct
nodeService.setProperty(childNodeRef, ContentModel.PROP_NAME, baseName);
return childNodeRef;
}
// Node already exists and not overwrite
String postFix = "(" + counter + ")";
if(baseName.length() + extension.length() + postFix.length() > QName.MAX_LENGTH )
{
// Need to truncate base name
workingName = baseName.substring(0, QName.MAX_LENGTH - postFix.length() - extension.length() -1) + postFix;
}
else
{
workingName = baseName + postFix ;
}
if(extension.length() > 0)
{
workingName = workingName + "." + extension;
}
}
else
{
// Here if child node ref does not already exist
if(logger.isDebugEnabled())
{
logger.debug("child node ref does not already exist :" + workingName);
}
Map<QName, Serializable> contentProps = new HashMap<QName, Serializable>();
contentProps.put(ContentModel.PROP_NAME, workingName);
ChildAssociationRef associationRef = nodeService.createNode(
parent,
assocType,
safeQName,
ContentModel.TYPE_CONTENT,
contentProps);
childNodeRef = associationRef.getChildRef();
return childNodeRef;
}
}
throw new AlfrescoRuntimeException("Unable to add new file");
}
/**
* Add new node into Alfresco repository with specified parameters.
* Node content isn't added.
*
* New node will be created with ContentModel.ASSOC_CONTAINS association with parent.
*
* @param nodeService Alfresco Node Service
* @param parent Parent node
* @param name Name of the new node
* @param overwrite whether a new node should overwrite an existing node with the same name or have its name
* mangled to be alongside the existing node.
* @return Reference to created node
*
*/
protected NodeRef addContentNode(NodeService nodeService, NodeRef parent, String name, boolean overwrite)
{
return addContentNode(nodeService, parent, name, ContentModel.ASSOC_CONTAINS, overwrite);
}
/**
* Adds new node into Alfresco repository and mark its as an attachment.
*
* @param nodeService Alfresco Node Service.
* @param folder Space/Folder to add.
* @param mainContentNode Main content node. Any mail is added into Alfresco as one main content node and several its attachments. Each attachment related with its main node.
* @param fileName File name for the attachment.
* @return Reference to created node.
*/
protected NodeRef addAttachment(NodeService nodeService, NodeRef folder, NodeRef mainContentNode, String fileName)
{
if (log.isDebugEnabled())
{
log.debug("Adding attachment node (name=" + fileName + ").");
}
NodeRef attachmentNode = addContentNode(nodeService, folder, fileName, false);
// Add attached aspect
nodeService.addAspect(mainContentNode, ContentModel.ASPECT_ATTACHABLE, null);
// Add the association
nodeService.createAssociation(mainContentNode, attachmentNode, ContentModel.ASSOC_ATTACHMENTS);
if (log.isDebugEnabled())
{
log.debug("Attachment has been added.");
}
return attachmentNode;
}
/**
* Return unique content name in passed folder based on provided name
*
* @param parent parent folder
* @param name name of node
* @param assocType assocType between parent and child
* @return Original name or name in format {name}({number})
*/
private String getAppropriateNodeName(NodeRef parent, String name, QName assocType)
{
if (nodeService.getChildByName(parent, assocType, name) != null)
{
name = name + "(1)";
while (nodeService.getChildByName(parent, assocType, name) != null)
{
int index = name.lastIndexOf("(");
if ((index > 0) && name.charAt(name.length() - 1) == ')')
{
String posibleNumber = name.substring(index + 1, name.length() - 1);
long num = Long.parseLong(posibleNumber) + 1;
name = name.substring(0, index) + "(" + num + ")";
}
}
}
return name;
}
/**
* Extracts the attachments from the given message and adds them to the space. All attachments
* are linked back to the original node that they are attached to.
*
* @param spaceNodeRef the space to add the documents into
* @param nodeRef the node to which the documents will be attached
* @param message the email message
*/
protected void addAttachments(NodeRef spaceNodeRef, NodeRef nodeRef, EmailMessage message)
{
// Add attachments
EmailMessagePart[] attachments = message.getAttachments();
for (EmailMessagePart attachment : attachments)
{
String fileName = attachment.getFileName();
InputStream contentIs = attachment.getContent();
MimetypeService mimetypeService = getMimetypeService();
String mimetype = mimetypeService.guessMimetype(fileName);
String encoding = attachment.getEncoding();
NodeRef attachmentNode = addAttachment(getNodeService(), spaceNodeRef, nodeRef, fileName);
writeContent(attachmentNode, contentIs, mimetype, encoding);
}
}
// Lookup Table for dubious characters.
final static String[][] dubiousChars = new String[][] { { "\\", "%5c" },
{ "/", "%2f" },
{ "*", "%2a" },
{ "|", "%7c" },
{ ":", "%3a" },
{ "\"", "%22" },
{ "<", "%3c" },
{ ">", "%3e" },
{ "?", "%3f" }};
/**
*
* Subject field is used as name of the content, so we need to replace characters that are forbidden in file names.
*
* Trims whitespace
*
* Replaces characters \/*|:"<>? with their hex values.
*
* @param subject the string of the email subject
*
** @return filename
*/
public static String encodeSubject(String subject)
{
// MER Removed . * , { ".", "%2e" }
// Filename regex from model is (.*[\"\*\\\>\<\?\/\:\|]+.*)|(.*[\.]?.*[\.]+$)|(.*[ ]+$)
//Strip whitespace
String result = subject.trim();
// replace dubious chars
for (int i = 0; i < dubiousChars.length; i++)
{
result = result.replace(dubiousChars[i][0], dubiousChars[i][1]);
}
// Replace trailing "." with %2e
if(result.endsWith("."))
{
result = result.substring(0, result.length() -1) + "%2e";
}
return result;
}
}

View File

@@ -0,0 +1,160 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.email.server.handler;
import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.alfresco.model.ApplicationModel;
import org.alfresco.model.ContentModel;
import org.alfresco.model.ForumModel;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.service.cmr.email.EmailMessage;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.PropertyMap;
/**
* Abstact class implements common logic for forum processing email mesages.
*
* @author maxim
* @since 2.2
*/
public abstract class AbstractForumEmailMessageHandler extends AbstractEmailMessageHandler
{
/**
* Posts content
*
* @param nodeRef Reference to node
* @param message Mail parser
* @return Returns the new post node
*/
protected NodeRef addPostNode(NodeRef nodeRef, EmailMessage message)
{
NodeService nodeService = getNodeService();
Date now = new Date();
String nodeName = "posted-" + new SimpleDateFormat("dd-MM-yyyy-hh-mm-ss").format(now) + ".html";
PropertyMap properties = new PropertyMap(3);
properties.put(ContentModel.PROP_NAME, nodeName);
NodeRef postNodeRef = nodeService.getChildByName(nodeRef, ContentModel.ASSOC_CONTAINS, nodeName);
if (postNodeRef == null)
{
ChildAssociationRef childAssoc = nodeService.createNode(
nodeRef,
ContentModel.ASSOC_CONTAINS,
QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, nodeName),
ForumModel.TYPE_POST,
properties);
postNodeRef = childAssoc.getChildRef();
}
// Add necessary aspects
properties.clear();
properties.put(ContentModel.PROP_TITLE, nodeName);
nodeService.addAspect(postNodeRef, ContentModel.ASPECT_TITLED, properties);
properties.clear();
properties.put(ApplicationModel.PROP_EDITINLINE, true);
nodeService.addAspect(postNodeRef, ApplicationModel.ASPECT_INLINEEDITABLE, properties);
// Write content
if (message.getBody() != null)
{
writeContent(
postNodeRef,
message.getBody().getContent(),
message.getBody().getContentType(),
message.getBody().getEncoding());
}
else
{
writeContent(postNodeRef, "<The message was empty>", MimetypeMap.MIMETYPE_TEXT_PLAIN);
}
addEmailedAspect(postNodeRef, message);
// Done
return postNodeRef;
}
/**
* Finds first child with specified name
*
* @param nodeRef Parent node for the search
* @param name String for search
* @return Reference to found node or null if node isn't found
*/
protected NodeRef getTopicNode(NodeRef nodeRef, String name)
{
String workingName = encodeSubject(name);
NodeRef ret = getNodeService().getChildByName(nodeRef, ContentModel.ASSOC_CONTAINS, workingName);
return ret;
}
/**
* Adds topic node into Alfresco repository
*
* @param parentNode Parent node
* @param name Topic name
* @return Reference to created node
*/
protected NodeRef addTopicNode(NodeRef parentNode, String name)
{
String workingName = encodeSubject(name);
NodeService nodeService = getNodeService();
Map<QName, Serializable> properties = new HashMap<QName, Serializable>(1);
properties.put(ContentModel.PROP_NAME, workingName);
NodeRef topicNode = nodeService.getChildByName(parentNode, ContentModel.ASSOC_CONTAINS, workingName);
if (topicNode == null)
{
ChildAssociationRef association = nodeService.createNode(
parentNode,
ContentModel.ASSOC_CONTAINS,
QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, workingName),
ForumModel.TYPE_TOPIC,
properties);
topicNode = association.getChildRef();
}
// Add necessary aspects
properties.clear();
properties.put(ApplicationModel.PROP_ICON, "topic");
getNodeService().addAspect(topicNode, ApplicationModel.ASPECT_UIFACETS, properties);
return topicNode;
}
}

View File

@@ -0,0 +1,172 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.email.server.handler;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.alfresco.email.server.EmailServiceImpl;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ApplicationModel;
import org.alfresco.model.ContentModel;
import org.alfresco.model.ForumModel;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.email.EmailMessage;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.namespace.QName;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Handler implementation address to document node.
*
* @author maxim
* @since 2.2
*/
public class DocumentEmailMessageHandler extends AbstractForumEmailMessageHandler
{
private static Log logger = LogFactory.getLog(DocumentEmailMessageHandler.class);
private static final String forumNodeName = "EmailForum";
public void processMessage(NodeRef contentNodeRef, EmailMessage message)
{
String messageSubject = message.getSubject();
if (messageSubject != null && messageSubject.length() > 0)
{
messageSubject = message.getSubject();
}
else
{
messageSubject = "EMPTY_SUBJECT_" + System.currentTimeMillis();
}
if(logger.isDebugEnabled())
{
logger.debug("process message:" + messageSubject);
}
QName nodeTypeQName = getNodeService().getType(contentNodeRef);
DictionaryService dictionaryService = getDictionaryService();
if (dictionaryService.isSubClass(nodeTypeQName, ContentModel.TYPE_CONTENT))
{
// Find where the content resides
NodeRef spaceNodeRef = getNodeService().getPrimaryParent(contentNodeRef).getParentRef();
NodeRef forumNode = getForumNode(contentNodeRef);
if (forumNode == null)
{
logger.debug("adding new forum node");
forumNode = addForumNode(contentNodeRef);
}
// Try to find existed node
NodeRef topicNodeRef = getTopicNode(forumNode, messageSubject);
if (topicNodeRef == null)
{
logger.debug("adding new topic node");
topicNodeRef = addTopicNode(forumNode, messageSubject);
}
// Create the post
logger.debug("add a post to the topic");
NodeRef postNodeRef = addPostNode(topicNodeRef, message);
// Add attachments
addAttachments(spaceNodeRef, postNodeRef, message);
}
else
{
throw new AlfrescoRuntimeException("\n" +
"Message handler " + this.getClass().getName() + " cannot handle type " + nodeTypeQName + ".\n" +
"Check the message handler mappings.");
}
}
/**
* Adds forum node
*
* @param nodeRef Paren node
* @return Reference to created node
*/
private NodeRef addForumNode(NodeRef nodeRef)
{
NodeService nodeService = getNodeService();
// //Add discussable aspect to content node
// if (!nodeService.hasAspect(nodeRef, ForumModel.ASPECT_DISCUSSABLE))
// {
// nodeService.addAspect(nodeRef, ForumModel.ASPECT_DISCUSSABLE, null);
// }
//Create forum node and associate it with content node
Map<QName, Serializable> properties = new HashMap<QName, Serializable>(1);
properties.put(ContentModel.PROP_NAME, forumNodeName);
ChildAssociationRef childAssoc = nodeService.createNode(nodeRef, ForumModel.ASSOC_DISCUSSION, ForumModel.ASSOC_DISCUSSION, ForumModel.TYPE_FORUM, properties);
NodeRef forumNode = childAssoc.getChildRef();
//Add necessary aspects to forum node
properties.clear();
properties.put(ApplicationModel.PROP_ICON, "forum");
nodeService.addAspect(forumNode, ApplicationModel.ASPECT_UIFACETS, properties);
return forumNode;
}
/**
* Finds the first forum node
*
* @param nodeRef Parent node
* @return Found node or null
*/
private NodeRef getForumNode(NodeRef nodeRef)
{
if (getNodeService().hasAspect(nodeRef, ForumModel.ASPECT_DISCUSSABLE))
{
List<ChildAssociationRef> assocRefList = getNodeService().getChildAssocs(nodeRef);
Iterator<ChildAssociationRef> assocRefIter = assocRefList.iterator();
while (assocRefIter.hasNext())
{
ChildAssociationRef assocRef = assocRefIter.next();
QName nodeTypeName = getNodeService().getType(assocRef.getChildRef());
if (nodeTypeName.equals(ForumModel.TYPE_FORUM))
return assocRef.getChildRef();
}
}
return null;
}
}

View File

@@ -0,0 +1,51 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.email.server.handler;
import org.alfresco.service.cmr.email.EmailMessage;
import org.alfresco.service.cmr.email.EmailMessageException;
import org.alfresco.service.cmr.repository.DuplicateChildNodeNameException;
import org.alfresco.service.cmr.repository.NodeRef;
/**
* Interface for email handler for processing email message.
*
* @author maxim
* @since 2.2
*/
public interface EmailMessageHandler
{
/**
* Method invokes for processing email message.
*
* @param nodeRef Target node
* @param message Email message
* @exception EmailMessageException Exception is thrown if processing was failed
* @exception DuplicateChildNodeNameException Exception is thrown if node name is duplicate.
*/
void processMessage(NodeRef nodeRef, EmailMessage message);
}

View File

@@ -0,0 +1,217 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.email.server.handler;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import javax.mail.MessagingException;
import org.alfresco.error.AlfrescoRuntimeException;
import org.springframework.extensions.surf.util.I18NUtil;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.service.cmr.email.EmailMessage;
import org.alfresco.service.cmr.email.EmailMessageException;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.MimetypeService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.namespace.QName;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Handler implementation address to folder node.
*
* @author Yan O
* @since 2.2
*/
public class FolderEmailMessageHandler extends AbstractEmailMessageHandler
{
private static final String MSG_RECEIVED_BY_SMTP = "email.server.msg.received_by_smtp";
private static final String MSG_DEFAULT_SUBJECT = "email.server.msg.default_subject";
private static final String ERR_MAIL_READ_ERROR = "email.server.err.mail_read_error";
private static final Log log = LogFactory.getLog(FolderEmailMessageHandler.class);
/**
* overwriteDuplicates.
* if true then overwrite an existing node with the same name. if false the name is changed to make it unique.
*/
private boolean overwriteDuplicates = false;
/**
* {@inheritDoc}
*/
public void processMessage(NodeRef nodeRef, EmailMessage message)
{
if (log.isDebugEnabled())
{
log.debug("Message is processing by FolderMailMessageHandler");
}
try
{
// Check type of the node. It must be a FOLDER
QName nodeTypeQName = getNodeService().getType(nodeRef);
if (getDictionaryService().isSubClass(nodeTypeQName, ContentModel.TYPE_FOLDER))
{
// Add the content into the system
addAlfrescoContent(nodeRef, message);
}
else
{
throw new AlfrescoRuntimeException("\n" +
"Message handler " + this.getClass().getName() + " cannot handle type " + nodeTypeQName + ".\n" +
"Check the message handler mappings.");
}
}
catch (IOException ex)
{
throw new EmailMessageException(ERR_MAIL_READ_ERROR, ex.getMessage());
}
}
/**
* Add content to Alfresco repository
*
* @param spaceNodeRef Addressed node
* @param message Mail message
* @throws IOException Exception can be thrown while saving a content into Alfresco repository.
*/
public void addAlfrescoContent(NodeRef spaceNodeRef, EmailMessage message) throws IOException
{
// Set default values for email fields
String messageSubject = message.getSubject();
if (messageSubject == null || messageSubject.length() == 0)
{
Date now = new Date();
messageSubject = I18NUtil.getMessage(MSG_DEFAULT_SUBJECT, new SimpleDateFormat("dd-MM-yyyy-hh-mm-ss").format(now));
}
String messageFrom = message.getFrom();
if(messageFrom == null)
{
messageFrom = "";
}
// Create main content node
NodeRef contentNodeRef;
contentNodeRef = addContentNode(getNodeService(), spaceNodeRef, messageSubject, overwriteDuplicates);
// Add titled aspect
addTitledAspect(contentNodeRef, messageSubject, messageFrom);
// Add emailed aspect
addEmailedAspect(contentNodeRef, message);
// Write the message content
if (message.getBody() != null)
{
if (message.getBody().getSize() == -1)
{
// If message body is empty we write space as a content
// to make possible rule processing
// (Rules don't work on empty documents)
writeSpace(contentNodeRef);
}
else
{
InputStream contentIs = message.getBody().getContent();
// The message body is plain text, unless an extension has been provided
MimetypeService mimetypeService = getMimetypeService();
String mimetype = mimetypeService.guessMimetype(messageSubject);
if (mimetype.equals(MimetypeMap.MIMETYPE_BINARY))
{
mimetype = MimetypeMap.MIMETYPE_TEXT_PLAIN;
}
// Use the default encoding. It will get overridden if the body is text.
String encoding = message.getBody().getEncoding();
writeContent(contentNodeRef, contentIs, mimetype, encoding);
}
}
// Add attachments
addAttachments(spaceNodeRef, contentNodeRef, message);
}
/**
* This method writes space as a content. We need this space because rules doesn't proceed documents with empty content. We need rule processing for command email messages with
* empty body.
*
* @param nodeRef Reference to the parent node
*/
private void writeSpace(NodeRef nodeRef)
{
if (log.isDebugEnabled())
{
log.debug("Write space string");
}
ContentService contentService = getContentService();
ContentWriter writer = contentService.getWriter(nodeRef, ContentModel.PROP_CONTENT, true);
writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN);
writer.setEncoding("UTF-8");
writer.putContent(" ");
}
/**
* Adds titled aspect to the specified node.
*
* @param nodeRef Target node.
* @param title Title
*/
private void addTitledAspect(NodeRef nodeRef, String title, String from)
{
Map<QName, Serializable> titledProps = new HashMap<QName, Serializable>();
titledProps.put(ContentModel.PROP_TITLE, title);
titledProps.put(ContentModel.PROP_DESCRIPTION, I18NUtil.getMessage(MSG_RECEIVED_BY_SMTP, from));
getNodeService().addAspect(nodeRef, ContentModel.ASPECT_TITLED, titledProps);
if (log.isDebugEnabled())
{
log.debug("Titled aspect has been added.");
}
}
/**
* Set the behaviour to be done on detecting a new message with the same subject.
* @param overwriteDuplicates boolean
*/
public void setOverwriteDuplicates(boolean overwriteDuplicates)
{
this.overwriteDuplicates = overwriteDuplicates;
}
public boolean isOverwriteDuplicates()
{
return overwriteDuplicates;
}
}

View File

@@ -0,0 +1,77 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.email.server.handler;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ForumModel;
import org.alfresco.service.cmr.email.EmailMessage;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.namespace.QName;
/**
* Handler implementation address to forum node.
*
* @author maxim
* @since 2.2
*/
public class ForumEmailMessageHandler extends AbstractForumEmailMessageHandler
{
/**
* {@inheritDoc}
*/
public void processMessage(NodeRef nodeRef, EmailMessage message)
{
String messageSubject;
if (message.getSubject() != null)
{
messageSubject = message.getSubject();
}
else
{
messageSubject = "EMPTY_SUBJECT_" + System.currentTimeMillis();
}
QName nodeTypeQName = getNodeService().getType(nodeRef);
if (getDictionaryService().isSubClass(nodeTypeQName, ForumModel.TYPE_FORUM))
{
NodeRef topicNode = getTopicNode(nodeRef, messageSubject);
if (topicNode == null)
{
topicNode = addTopicNode(nodeRef, messageSubject);
}
addPostNode(topicNode, message);
}
else
{
throw new AlfrescoRuntimeException("\n" +
"Message handler " + this.getClass().getName() + " cannot handle type " + nodeTypeQName + ".\n" +
"Check the message handler mappings.");
}
}
}

View File

@@ -0,0 +1,70 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.email.server.handler;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ForumModel;
import org.alfresco.service.cmr.email.EmailMessage;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.namespace.QName;
/**
* Handler implementation address to topic node.
*
* @author maxim
* @since 2.2
*/
public class TopicEmailMessageHandler extends AbstractForumEmailMessageHandler
{
/**
* {@inheritDoc}
*/
public void processMessage(NodeRef nodeRef, EmailMessage message)
{
QName nodeTypeQName = getNodeService().getType(nodeRef);
NodeRef topicNode = null;
if (getDictionaryService().isSubClass(nodeTypeQName, ForumModel.TYPE_TOPIC))
{
topicNode = nodeRef;
}
else if (getDictionaryService().isSubClass(nodeTypeQName, ForumModel.TYPE_POST))
{
topicNode = getNodeService().getPrimaryParent(nodeRef).getParentRef();
if (topicNode == null)
{
throw new AlfrescoRuntimeException("A POST node has no primary parent: " + nodeRef);
}
}
else
{
throw new AlfrescoRuntimeException("\n" +
"Message handler " + this.getClass().getName() + " cannot handle type " + nodeTypeQName + ".\n" +
"Check the message handler mappings.");
}
addPostNode(topicNode, message);
}
}

View File

@@ -0,0 +1,113 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.email.server.impl;
import java.util.Date;
import java.util.List;
import org.alfresco.service.cmr.email.EmailMessage;
import org.alfresco.service.cmr.email.EmailMessagePart;
/**
* Implementation EmailMessage interface.
*
* @deprecated - class not used.
* @author maxim
* @since 2.2
*/
public class EmailMessageImpl implements EmailMessage
{
private static final long serialVersionUID = 8215537693963343756L;
private String to;
private String from;
private String subject;
private Date sentDate;
private EmailMessagePart body;
public EmailMessageImpl(String to, String from, String subject, String body)
{
if (to == null)
{
throw new IllegalArgumentException("To cannot be null");
}
this.to = to;
if (from == null)
{
throw new IllegalArgumentException("From cannot be null");
}
this.from = from;
if (subject == null)
{
throw new IllegalArgumentException("Subject cannot be null");
}
this.subject = subject;
if (body == null)
{
throw new IllegalArgumentException("Body cannot be null");
}
this.body = new EmailMessagePartImpl("Content.txt", body.getBytes());
this.sentDate = new Date();
}
public String getTo()
{
return to;
}
public String getFrom()
{
return from;
}
public String getSubject()
{
return subject;
}
public List<String> getCC()
{
return null;
}
public Date getSentDate()
{
return sentDate;
}
public EmailMessagePart getBody()
{
return body;
}
public EmailMessagePart[] getAttachments()
{
return new EmailMessagePart[0];
}
}

View File

@@ -0,0 +1,102 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.email.server.impl;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import org.alfresco.service.cmr.email.EmailMessagePart;
/**
* Implementation EmailMessagePart interface.
*
* @deprecated class not used.
* @author maxim
* @since 2.2
*/
public class EmailMessagePartImpl implements EmailMessagePart
{
private static final long serialVersionUID = 779186820993301580L;
private byte[] content;
private String encoding;
private String fileName;
public EmailMessagePartImpl(String fileName, byte[] content)
{
this(fileName, null, content);
}
public EmailMessagePartImpl(String fileName, String encoding, byte[] content)
{
if (fileName == null)
{
throw new IllegalArgumentException("FileName cannot be null");
}
this.fileName = fileName;
if (content == null)
{
throw new IllegalArgumentException("Content cannot be null");
}
this.content = content;
if (encoding == null)
{
this.encoding = "utf8";
}
else
{
this.encoding = encoding;
}
}
public InputStream getContent()
{
return new ByteArrayInputStream(content);
}
public String getContentType()
{
return "text/plain";
}
public String getEncoding()
{
return encoding;
}
public String getFileName()
{
return fileName;
}
public int getSize()
{
return content.length;
}
}

View File

@@ -0,0 +1,473 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.email.server.impl.subetha;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import javax.mail.Address;
import javax.mail.BodyPart;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Part;
import javax.mail.Session;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.internet.MimeUtility;
import javax.mail.internet.MimeMessage.RecipientType;
import org.alfresco.service.cmr.email.EmailMessage;
import org.alfresco.service.cmr.email.EmailMessageException;
import org.alfresco.service.cmr.email.EmailMessagePart;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/*
* TODO There's a lot of metadata extraction going on in this class that
* is duplicated by RFC822MetadataExtracter
*/
/**
* Concrete representation of an email message as implemented for the SubEtha mail server.
*
* @since 2.2
*/
public class SubethaEmailMessage implements EmailMessage
{
private static final String ERR_FAILED_TO_CREATE_MIME_MESSAGE = "email.server.err.failed_to_create_mime_message";
private static final String ERR_EXTRACTING_FROM_ADDRESS = "email.server.err.extracting_from_address";
private static final String ERR_NO_FROM_ADDRESS = "email.server.err.no_from_address";
private static final String ERR_EXTRACTING_TO_ADDRESS = "email.server.err.extracting_to_address";
private static final String ERR_NO_TO_ADDRESS = "email.server.err.no_to_address";
private static final String ERR_EXTRACTING_SUBJECT = "email.server.err.extracting_subject";
private static final String ERR_EXTRACTING_SENT_DATE = "email.server.err.extracting_sent_date";
private static final String ERR_PARSE_MESSAGE = "email.server.err.parse_message";
private static final long serialVersionUID = -3735187524926395261L;
private static final Log log = LogFactory.getLog(SubethaEmailMessage.class);
private static final String MIME_PLAIN_TEXT = "text/plain";
private static final String MIME_HTML_TEXT = "text/html";
private static final String MIME_XML_TEXT = "text/xml";
private static final String MIME_APPLICATION = "application/*";
private static final String MIME_IMAGE = "image/*";
private static final String MIME_MULTIPART = "multipart/*";
private static final String MIME_RFC822 = "message/rfc822";
private static final String FILENAME_ATTACHMENT_PREFIX = "Attachment";
private String from;
private String to;
private String subject;
private List<String> cc;
private Date sentDate;
private EmailMessagePart body;
private EmailMessagePart[] attachments;
transient private int bodyNumber = 0;
transient private int attachmentNumber = 0;
transient private List<EmailMessagePart> attachmentList = new LinkedList<EmailMessagePart>();
protected SubethaEmailMessage()
{
super();
}
public SubethaEmailMessage(MimeMessage mimeMessage)
{
processMimeMessage(mimeMessage);
}
public SubethaEmailMessage(InputStream dataInputStream)
{
MimeMessage mimeMessage = null;
try
{
mimeMessage = new MimeMessage(Session.getDefaultInstance(System.getProperties()), dataInputStream);
}
catch (MessagingException e)
{
throw new EmailMessageException(ERR_FAILED_TO_CREATE_MIME_MESSAGE, e.getMessage());
}
processMimeMessage(mimeMessage);
}
private void processMimeMessage(MimeMessage mimeMessage)
{
if (from == null)
{
Address[] addresses = null;
try
{
addresses = mimeMessage.getFrom();
}
catch (MessagingException e)
{
throw new EmailMessageException(ERR_EXTRACTING_FROM_ADDRESS, e.getMessage());
}
if (addresses == null || addresses.length == 0)
{
//throw new EmailMessageException(ERR_NO_FROM_ADDRESS);
}
else
{
if(addresses[0] instanceof InternetAddress)
{
from = ((InternetAddress)addresses[0]).getAddress();
}
else
{
from = addresses[0].toString();
}
}
}
if (to == null)
{
Address[] addresses = null;
try
{
addresses = mimeMessage.getAllRecipients();
}
catch (MessagingException e)
{
throw new EmailMessageException(ERR_EXTRACTING_TO_ADDRESS, e.getMessage());
}
if (addresses == null || addresses.length == 0)
{
//throw new EmailMessageException(ERR_NO_TO_ADDRESS);
}
else
{
if(addresses[0] instanceof InternetAddress)
{
to = ((InternetAddress)addresses[0]).getAddress();
}
else
{
to = addresses[0].toString();
}
}
}
if (cc == null)
{
try
{
ArrayList<String> list = new ArrayList<String>();
Address[] cca = mimeMessage.getRecipients(RecipientType.CC);
if(cca != null)
{
for(Address a : cca)
{
list.add(a.toString());
}
}
cc = list;
}
catch (MessagingException e)
{
// Do nothing - this is not a show-stopper.
cc = null;
}
}
try
{
subject = mimeMessage.getSubject();
//subject = encodeSubject(mimeMessage.getSubject());
}
catch (MessagingException e)
{
throw new EmailMessageException(ERR_EXTRACTING_SUBJECT, e.getMessage());
}
//if (subject == null)
//{
// subject = ""; // Just anti-null stub :)
//}
try
{
sentDate = mimeMessage.getSentDate();
}
catch (MessagingException e)
{
throw new EmailMessageException(ERR_EXTRACTING_SENT_DATE, e.getMessage());
}
if (sentDate == null)
{
sentDate = new Date(); // Just anti-null stub :)
}
parseMessagePart(mimeMessage);
attachments = new EmailMessagePart[attachmentList.size()];
attachmentList.toArray(attachments);
attachmentList = null;
}
private void parseMessagePart(Part messagePart)
{
try
{
if (messagePart.isMimeType(MIME_PLAIN_TEXT) || messagePart.isMimeType(MIME_HTML_TEXT))
{
if (log.isDebugEnabled())
{
log.debug("Text or HTML part was found. ContentType: " + messagePart.getContentType());
}
addBody(messagePart);
}
else if (messagePart.isMimeType(MIME_XML_TEXT))
{
if (log.isDebugEnabled())
{
log.debug("XML part was found.");
}
addAttachment(messagePart);
}
else if (messagePart.isMimeType(MIME_APPLICATION))
{
if (log.isDebugEnabled())
{
log.debug("Application part was found.");
}
addAttachment(messagePart);
}
else if (messagePart.isMimeType(MIME_IMAGE))
{
if (log.isDebugEnabled())
{
log.debug("Image part was found.");
}
addAttachment(messagePart);
}
else if (messagePart.isMimeType(MIME_MULTIPART))
{
// if multipart, this method will be called recursively
// for each of its parts
Multipart mp = (Multipart) messagePart.getContent();
int count = mp.getCount();
if (log.isDebugEnabled())
{
log.debug("MULTIPART with " + count + " part(s) found. Processin each part...");
}
for (int i = 0; i < count; i++)
{
BodyPart bp = mp.getBodyPart(i);
if (bp.getContent() instanceof MimeMultipart)
{
// It's multipart. Recurse.
parseMessagePart(bp);
}
else
{
// It's the body
addBody(bp);
}
}
if (log.isDebugEnabled())
{
log.debug("MULTIPART processed.");
}
}
else if (messagePart.isMimeType(MIME_RFC822))
{
// if rfc822, call this method with its content as the part
if (log.isDebugEnabled())
{
log.debug("MIME_RFC822 part found. Processing inside part...");
}
parseMessagePart((Part) messagePart.getContent());
if (log.isDebugEnabled())
{
log.debug("MIME_RFC822 processed.");
}
}
else
{
// if all else fails, put this in the attachments map.
// Actually we don't know what it is.
if (log.isDebugEnabled())
{
log.debug("Unrecognized part was found. Put it into attachments.");
}
addAttachment(messagePart);
}
}
catch (IOException e)
{
throw new EmailMessageException(ERR_PARSE_MESSAGE, e.getMessage());
}
catch (MessagingException e)
{
throw new EmailMessageException(ERR_PARSE_MESSAGE, e.getMessage());
}
}
private void addBody(Part messagePart) throws MessagingException
{
if (body != null)
{
attachmentList.add(new SubethaEmailMessagePart(messagePart, getPartFileName(getSubject() + " (part " + ++bodyNumber + ")", messagePart)));
if (log.isInfoEnabled())
{
log.info(String.format("Attachment \"%s\" has been added.", attachmentList.get(attachmentList.size() - 1).getFileName()));
}
}
else
{
body = new SubethaEmailMessagePart(messagePart, getPartFileName(getSubject(), messagePart));
if (log.isDebugEnabled())
{
log.debug("Body has been added.");
}
}
}
/**
* Method adds a message part to the attachments list
*
* @param messagePart A part of message
* @throws EmailMessageException
* @throws MessagingException
*/
private void addAttachment(Part messagePart) throws MessagingException
{
String fileName = getPartFileName(FILENAME_ATTACHMENT_PREFIX + attachmentNumber, messagePart);
attachmentList.add(new SubethaEmailMessagePart(messagePart, fileName));
if (log.isDebugEnabled())
{
log.debug("Attachment added: " + fileName);
}
}
/**
* Method extracts file name from a message part for saving its as aa attachment. If the file name can't be extracted, it will be generated based on defaultPrefix parameter.
*
* @param defaultPrefix This prefix fill be used for generating file name.
* @param messagePart A part of message
* @return File name.
* @throws MessagingException
*/
private String getPartFileName(String defaultPrefix, Part messagePart) throws MessagingException
{
String fileName = messagePart.getFileName();
if (fileName != null)
{
try
{
fileName = MimeUtility.decodeText(fileName);
}
catch (UnsupportedEncodingException ex)
{
// Nothing to do :)
}
}
else
{
fileName = defaultPrefix;
if (messagePart.isMimeType(MIME_PLAIN_TEXT))
fileName += ".txt";
else if (messagePart.isMimeType(MIME_HTML_TEXT))
fileName += ".html";
else if (messagePart.isMimeType(MIME_XML_TEXT))
fileName += ".xml";
else if (messagePart.isMimeType(MIME_IMAGE))
fileName += ".gif";
}
return fileName;
}
public void setRmiRegistry(String rmiRegistryHost, int rmiRegistryPort)
{
if (body instanceof SubethaEmailMessagePart)
{
((SubethaEmailMessagePart) body).setRmiRegistry(rmiRegistryHost, rmiRegistryPort);
}
for (EmailMessagePart attachment : attachments)
{
if (attachment instanceof SubethaEmailMessagePart)
{
((SubethaEmailMessagePart) attachment).setRmiRegistry(rmiRegistryHost, rmiRegistryPort);
}
}
}
public List<String> getCC()
{
return cc;
}
public String getFrom()
{
return from;
}
public String getTo()
{
return to;
}
public Date getSentDate()
{
return sentDate;
}
public String getSubject()
{
return subject;
}
public EmailMessagePart getBody()
{
return body;
}
public EmailMessagePart[] getAttachments()
{
return attachments;
}
}

View File

@@ -0,0 +1,164 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.email.server.impl.subetha;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.nio.charset.Charset;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.mail.MessagingException;
import javax.mail.Part;
import org.alfresco.service.cmr.email.EmailMessageException;
import org.alfresco.service.cmr.email.EmailMessagePart;
import org.springframework.extensions.surf.util.ParameterCheck;
import org.alfresco.util.remote.RemotableInputStream;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* @since 2.2
*/
public class SubethaEmailMessagePart implements EmailMessagePart
{
private static final String ERR_UNSUPPORTED_ENCODING = "email.server.err.usupported_encoding";
private static final String ERR_FAILED_TO_READ_CONTENT_STREAM = "email.server.err.failed_to_read_content_stream";
private static final String ERR_INCORRECT_MESSAGE_PART = "email.server.err.incorrect_message_part";
private static final long serialVersionUID = -8530238872199733096L;
static final Log log = LogFactory.getLog(SubethaEmailMessagePart.class);
private static final Pattern encodingExtractor = Pattern.compile("charset\\s*=[\\s\"]*([^\";\\s]*)");
private String encoding;
private String fileName;
private int fileSize = -1;
private String contentType;
private InputStream contentInputStream;
private String rmiRegistryHost;
private int rmiRegistryPort;
protected SubethaEmailMessagePart()
{
super();
}
/**
* Object can be built on existing message part only.
*
* @param messagePart Message part.
*/
public SubethaEmailMessagePart(Part messagePart)
{
ParameterCheck.mandatory("messagePart", messagePart);
try
{
fileSize = messagePart.getSize();
fileName = messagePart.getFileName();
contentType = messagePart.getContentType();
Matcher matcher = encodingExtractor.matcher(contentType);
if (matcher.find())
{
encoding = matcher.group(1);
if (!Charset.isSupported(encoding))
{
throw new EmailMessageException(ERR_UNSUPPORTED_ENCODING, encoding);
}
}
try
{
contentInputStream = messagePart.getInputStream();
}
catch (Exception ex)
{
throw new EmailMessageException(ERR_FAILED_TO_READ_CONTENT_STREAM, ex.getMessage());
}
}
catch (MessagingException e)
{
throw new EmailMessageException(ERR_INCORRECT_MESSAGE_PART, e.getMessage());
}
}
public SubethaEmailMessagePart(Part messagePart, String fileName)
{
this(messagePart);
this.fileName = fileName;
}
public InputStream getContent()
{
return contentInputStream;
}
public String getContentType()
{
return contentType;
}
public String getEncoding()
{
return encoding;
}
public String getFileName()
{
return fileName;
}
public int getSize()
{
return fileSize;
}
public void setRmiRegistry(String rmiRegistryHost, int rmiRegistryPort)
{
this.rmiRegistryHost = rmiRegistryHost;
this.rmiRegistryPort = rmiRegistryPort;
}
private void writeObject(ObjectOutputStream out) throws IOException
{
contentInputStream = new RemotableInputStream(rmiRegistryHost, rmiRegistryPort, contentInputStream);
out.defaultWriteObject();
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException
{
in.defaultReadObject();
}
}

View File

@@ -0,0 +1,255 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.email.server.impl.subetha;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import org.alfresco.email.server.EmailServer;
import org.alfresco.service.cmr.email.EmailDelivery;
import org.alfresco.service.cmr.email.EmailMessage;
import org.alfresco.service.cmr.email.EmailMessageException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.subethamail.smtp.AuthenticationHandler;
import org.subethamail.smtp.AuthenticationHandlerFactory;
import org.subethamail.smtp.MessageContext;
import org.subethamail.smtp.MessageHandler;
import org.subethamail.smtp.MessageHandlerFactory;
import org.subethamail.smtp.RejectException;
import org.subethamail.smtp.TooMuchDataException;
import org.subethamail.smtp.auth.EasyAuthenticationHandlerFactory;
import org.subethamail.smtp.auth.LoginFailedException;
import org.subethamail.smtp.auth.UsernamePasswordValidator;
import org.subethamail.smtp.io.DeferredFileOutputStream;
import org.subethamail.smtp.server.SMTPServer;
/**
* @since 2.2
*/
public class SubethaEmailServer extends EmailServer
{
private final static Log logger = LogFactory.getLog(SubethaEmailServer.class);
private SMTPServer serverImpl;
protected SubethaEmailServer()
{
super();
}
@Override
public void startup()
{
serverImpl = new SMTPServer(new HandlerFactory());
// MER - May need to override SMTPServer.createSSLSocket to specify non default keystore.
serverImpl.setPort(getPort());
serverImpl.setHostName(getDomain());
serverImpl.setMaxConnections(getMaxConnections());
serverImpl.setHideTLS(isHideTLS());
serverImpl.setEnableTLS(isEnableTLS());
serverImpl.setRequireTLS(isRequireTLS());
if(isAuthenticate())
{
AuthenticationHandlerFactory authenticationHandler = new EasyAuthenticationHandlerFactory(new AlfrescoLoginUsernamePasswordValidator());
serverImpl.setAuthenticationHandlerFactory(authenticationHandler);
}
serverImpl.start();
log.info("Inbound SMTP Email Server has started successfully, on hostName:" + getDomain() + "port:" + getPort());
}
@Override
public void shutdown()
{
serverImpl.stop();
log.info("Inbound SMTP Email Server has stopped successfully");
}
class AlfrescoLoginUsernamePasswordValidator implements UsernamePasswordValidator
{
@Override
public void login(String username, String password)
throws LoginFailedException
{
if(!authenticateUserNamePassword(username, password.toCharArray()))
{
throw new LoginFailedException("unable to log on");
}
if(logger.isDebugEnabled())
{
logger.debug("User authenticated successfully" + username);
}
// here if authentication successful.
}
}
class HandlerFactory implements MessageHandlerFactory
{
public MessageHandler create(MessageContext messageContext)
{
return new Handler(messageContext);
}
};
class Handler implements MessageHandler
{
/**
* 7 megs by default. The server will buffer incoming messages to disk when they hit this limit in the DATA received.
*/
private int DEFAULT_DATA_DEFERRED_SIZE = 1024 * 1024 * 7;
private List<String> EMPTY_LIST = new LinkedList<String>();
private MessageContext messageContext;
private String from;
List<EmailDelivery> deliveries = new ArrayList<EmailDelivery>();
public Handler(MessageContext messageContext)
{
this.messageContext = messageContext;
}
public MessageContext getMessageContext()
{
return messageContext;
}
public void from(String fromString) throws RejectException
{
try
{
InternetAddress a = new InternetAddress(fromString);
from = a.getAddress();
}
catch (AddressException e)
{
}
try
{
if(logger.isDebugEnabled())
{
logger.debug("check whether user is allowed to send email from" + from);
}
filterSender(from);
}
catch (EmailMessageException e)
{
throw new RejectException(554, e.getMessage());
}
}
public void recipient(String recipient) throws RejectException
{
AuthenticationHandler auth = messageContext.getAuthenticationHandler();
deliveries.add(new EmailDelivery(recipient, from, auth != null ? (String)auth.getIdentity(): null));
}
public void data(InputStream data) throws TooMuchDataException, IOException, RejectException
{
if (deliveries.size() == 1)
{
EmailDelivery delivery = deliveries.get(0);
processDelivery(delivery, data);
}
else if (deliveries.size() > 1)
{
DeferredFileOutputStream dfos = null;
try
{
dfos = new DeferredFileOutputStream(DEFAULT_DATA_DEFERRED_SIZE);
byte[] bytes = new byte[1024 * 8];
int bytesRead;
while ((bytesRead = data.read(bytes)) != -1)
{
dfos.write(bytes, 0, bytesRead);
}
for (EmailDelivery delivery : deliveries)
{
processDelivery(delivery, dfos.getInputStream());
}
}
finally
{
try
{
dfos.close();
}
catch (Exception e)
{
}
}
}
}
private void processDelivery(EmailDelivery delivery, InputStream data) throws RejectException
{
EmailMessage emailMessage;
try
{
emailMessage = new SubethaEmailMessage(data);
getEmailService().importMessage(delivery, emailMessage);
}
catch (EmailMessageException e)
{
if (log.isDebugEnabled())
{
log.debug("about to raise EmailMessageException", e);
}
throw new RejectException(554, e.getMessage());
}
catch (Throwable e)
{
log.error(e.getMessage(), e);
throw new RejectException(554, "An internal error prevented mail delivery.");
}
}
@Override
public void done()
{
deliveries.clear();
}
};
}

View File

@@ -0,0 +1,89 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.encryption;
import org.alfresco.error.AlfrescoRuntimeException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationEvent;
import org.springframework.extensions.surf.util.AbstractLifecycleBean;
/**
*
* @since 4.0
*
*/
public class BootstrapReEncryptor extends AbstractLifecycleBean
{
private static Log logger = LogFactory.getLog(BootstrapReEncryptor.class);
private boolean enabled;
private ReEncryptor reEncryptor;
public void setEnabled(boolean enabled)
{
this.enabled = enabled;
}
public void setReEncryptor(ReEncryptor reEncryptor)
{
this.reEncryptor = reEncryptor;
}
public int reEncrypt()
{
try
{
return reEncryptor.bootstrapReEncrypt();
}
catch(MissingKeyException e)
{
throw new AlfrescoRuntimeException("Bootstrap re-encryption failed", e);
}
}
@Override
protected void onBootstrap(ApplicationEvent event)
{
if(enabled)
{
if(logger.isDebugEnabled())
{
logger.debug("Re-encrypting encryptable properties...");
}
int propertiesReEncrypted = reEncrypt();
if(logger.isDebugEnabled())
{
logger.debug("...done, re-encrypted " + propertiesReEncrypted + " properties.");
}
}
}
@Override
protected void onShutdown(ApplicationEvent event)
{
}
}

View File

@@ -0,0 +1,91 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.encryption;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.transaction.TransactionService;
import org.springframework.context.ApplicationEvent;
import org.springframework.extensions.surf.util.AbstractLifecycleBean;
/**
* The EncryptionChecker checks the state of the repository's encryption system.
* In particular it checks:
* <ul>
* <li> that the keystore exists and, if not, creates one.
* <li> that the encryption keys have not been changed. If so, the bootstrap will be halted.
* </ul>
*
* @since 4.0
*
*/
public class EncryptionChecker extends AbstractLifecycleBean
{
private TransactionService transactionService;
private KeyStoreChecker keyStoreChecker;
public void setKeyStoreChecker(KeyStoreChecker keyStoreChecker)
{
this.keyStoreChecker = keyStoreChecker;
}
public void setTransactionService(TransactionService transactionService)
{
this.transactionService = transactionService;
}
@Override
protected void onBootstrap(ApplicationEvent event)
{
RetryingTransactionHelper txnHelper = transactionService.getRetryingTransactionHelper();
txnHelper.setForceWritable(true); // Force write in case server is read-only
txnHelper.doInTransaction(new RetryingTransactionCallback<Void>()
{
public Void execute() throws Throwable
{
try
{
keyStoreChecker.validateKeyStores();
}
catch(Throwable e)
{
// Just throw as a runtime exception
throw new AlfrescoRuntimeException("Keystores are invalid", e);
}
return null;
}
});
}
@Override
protected void onShutdown(ApplicationEvent event)
{
}
}

View File

@@ -0,0 +1,243 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.encryption;
import java.io.Serializable;
import java.security.InvalidKeyException;
import java.security.Key;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.cmr.attributes.AttributeService;
import org.alfresco.service.cmr.attributes.AttributeService.AttributeQueryCallback;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.EqualsHelper;
import org.alfresco.util.GUID;
import org.alfresco.util.Pair;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Registered Encryption Keys are stored in the AttributeService directly under a top level key defined by
* TOP_LEVEL_KEY (which means that all key aliases must be unique across however many keystores are being used).
*
* @since 4.0
*
*/
// TODO caching? This will probably not be used extensively.
// TODO instead of persisting the Pair when registering a key, create two attributes per key (one for the
// guid and one for the encrypted value of the guid). This means a custom class does not need to be bound to
// the attribute service.
public class EncryptionKeysRegistryImpl implements EncryptionKeysRegistry
{
public static String TOP_LEVEL_KEY = "keyCheck";
private static final Log logger = LogFactory.getLog(EncryptionKeysRegistryImpl.class);
private TransactionService transactionService;
private AttributeService attributeService;
private String cipherAlgorithm;
private String cipherProvider;
public void setAttributeService(AttributeService attributeService)
{
this.attributeService = attributeService;
}
public void setCipherAlgorithm(String cipherAlgorithm)
{
this.cipherAlgorithm = cipherAlgorithm;
}
public void setCipherProvider(String cipherProvider)
{
this.cipherProvider = cipherProvider;
}
public void setTransactionService(TransactionService transactionService)
{
this.transactionService = transactionService;
}
protected Encryptor getEncryptor(final KeyMap keys)
{
DefaultEncryptor encryptor = new DefaultEncryptor();
encryptor.setCipherAlgorithm(cipherAlgorithm);
encryptor.setCipherProvider(cipherProvider);
encryptor.setKeyProvider(new KeyProvider()
{
@Override
public Key getKey(String keyAlias)
{
return keys.getCachedKey(keyAlias).getKey();
}
});
return encryptor;
}
public void init()
{
}
public void registerKey(String keyAlias, Key key)
{
if(isKeyRegistered(keyAlias))
{
throw new IllegalArgumentException("Key " + keyAlias + " is already registered");
}
// register the key by creating an attribute that stores a guid and its encrypted value
String guid = GUID.generate();
KeyMap keys = new KeyMap();
keys.setKey(keyAlias, key);
Encryptor encryptor = getEncryptor(keys);
Serializable encrypted = encryptor.sealObject(keyAlias, null, guid);
Pair<String, Serializable> keyCheck = new Pair<String, Serializable>(guid, encrypted);
RetryingTransactionHelper retryingTransactionHelper = transactionService.getRetryingTransactionHelper();
final RetryingTransactionCallback<Void> createAttributeCallback = new RetryingTransactionCallback<Void>()
{
public Void execute() throws Throwable
{
attributeService.createAttribute(keyCheck, TOP_LEVEL_KEY, keyAlias);
return null;
}
};
retryingTransactionHelper.doInTransaction(createAttributeCallback, false);
logger.info("Registered key " + keyAlias);
}
public void unregisterKey(String keyAlias)
{
attributeService.removeAttribute(TOP_LEVEL_KEY, keyAlias);
}
public boolean isKeyRegistered(String keyAlias)
{
try
{
return (attributeService.getAttribute(TOP_LEVEL_KEY, keyAlias) != null);
}
catch(Throwable e)
{
// there is an issue getting the attribute. Remove it.
attributeService.removeAttribute(TOP_LEVEL_KEY, keyAlias);
return (attributeService.getAttribute(TOP_LEVEL_KEY, keyAlias) != null);
}
}
public List<String> getRegisteredKeys(final Set<String> keyStoreKeys)
{
final List<String> registeredKeys = new ArrayList<String>();
attributeService.getAttributes(new AttributeQueryCallback()
{
public boolean handleAttribute(Long id, Serializable value,
Serializable[] keys)
{
// Add as a registered key if the keystore contains the key
String keyAlias = (String)keys[1];
if(keyStoreKeys.contains(keyAlias))
{
registeredKeys.add(keyAlias);
}
return true;
}
},
TOP_LEVEL_KEY);
return registeredKeys;
}
@SuppressWarnings("unchecked")
public KEY_STATUS checkKey(String keyAlias, Key key)
{
Pair<String, Serializable> keyCheck = null;
if(attributeService.exists(TOP_LEVEL_KEY, keyAlias))
{
try
{
// check that the key has not changed by decrypting the encrypted guid attribute
// comparing against the guid
try
{
keyCheck = (Pair<String, Serializable>)attributeService.getAttribute(TOP_LEVEL_KEY, keyAlias);
}
catch(Throwable e)
{
// there is an issue getting the attribute. Remove it.
attributeService.removeAttribute(TOP_LEVEL_KEY, keyAlias);
return KEY_STATUS.MISSING;
}
if(keyCheck == null)
{
return KEY_STATUS.MISSING;
}
KeyMap keys = new KeyMap();
keys.setKey(keyAlias, key);
Encryptor encryptor = getEncryptor(keys);
Serializable storedGUID = encryptor.unsealObject(keyAlias, keyCheck.getSecond());
return EqualsHelper.nullSafeEquals(storedGUID, keyCheck.getFirst()) ? KEY_STATUS.OK : KEY_STATUS.CHANGED;
}
catch(InvalidKeyException e)
{
// key exception indicates that the key has changed - it can't decrypt the
// previously-encrypted data
return KEY_STATUS.CHANGED;
}
}
else
{
return KEY_STATUS.MISSING;
}
}
// note that this removes _all_ keys in the keystore. Use with care.
public void removeRegisteredKeys(final Set<String> keys)
{
RetryingTransactionHelper retryingTransactionHelper = transactionService.getRetryingTransactionHelper();
final RetryingTransactionCallback<Void> removeKeysCallback = new RetryingTransactionCallback<Void>()
{
public Void execute() throws Throwable
{
for(String keyAlias : keys)
{
attributeService.removeAttribute(TOP_LEVEL_KEY, keyAlias);
}
return null;
}
};
retryingTransactionHelper.doInTransaction(removeKeysCallback, false);
}
}

View File

@@ -0,0 +1,55 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.encryption;
/**
* Checks the repository key stores.
*
* @since 4.0
*
*/
public class KeyStoreChecker
{
private AlfrescoKeyStore mainKeyStore;
public KeyStoreChecker()
{
}
public void setMainKeyStore(AlfrescoKeyStore mainKeyStore)
{
this.mainKeyStore = mainKeyStore;
}
public void validateKeyStores() throws InvalidKeystoreException, MissingKeyException
{
mainKeyStore.validateKeys();
if(!mainKeyStore.exists())
{
mainKeyStore.create();
}
}
}

View File

@@ -0,0 +1,360 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.encryption;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import javax.crypto.SealedObject;
import org.alfresco.repo.batch.BatchProcessWorkProvider;
import org.alfresco.repo.batch.BatchProcessor;
import org.alfresco.repo.dictionary.DictionaryDAO;
import org.alfresco.repo.domain.node.NodeDAO;
import org.alfresco.repo.domain.node.NodePropertyEntity;
import org.alfresco.repo.domain.node.NodePropertyKey;
import org.alfresco.repo.domain.node.NodePropertyValue;
import org.alfresco.repo.domain.qname.QNameDAO;
import org.alfresco.repo.lock.JobLockService;
import org.alfresco.repo.lock.LockAcquisitionException;
import org.alfresco.repo.node.encryption.MetadataEncryptor;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.transaction.TransactionService;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
/**
* Re-encrypts encryptable repository properties using a new set of encryption keys.
* Decrypts the repository properties using the default encryptor, falling back to
* a backup decryptor (using the old encryption keys) if necessary, and then re-encrypts
* the properties.
*
* Can run in one of two ways:
*
* <ul>
* <li> during bootstrap.
* <li> by using JMX (available only to Enterprise). In this case, the system can stay running while the re-encryption takes place.
* </ul>
*
* @since 4.0
*/
public class ReEncryptor implements ApplicationContextAware
{
private static Log logger = LogFactory.getLog(ReEncryptor.class);
private NodeDAO nodeDAO;
private DictionaryDAO dictionaryDAO;
private QNameDAO qnameDAO;
private MetadataEncryptor metadataEncryptor;
private ApplicationContext applicationContext;
private TransactionService transactionService;
private RetryingTransactionHelper transactionHelper;
private int numThreads;
private int chunkSize;
private boolean splitTxns = true;
private static final QName LOCK = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "OrphanReaper");
private JobLockService jobLockService;
/**
* Set the transaction provider so that each execution can be performed within a transaction
*/
public void setTransactionService(TransactionService transactionService)
{
this.transactionService = transactionService;
this.transactionHelper = transactionService.getRetryingTransactionHelper();
}
public void setMetadataEncryptor(MetadataEncryptor metadataEncryptor)
{
this.metadataEncryptor = metadataEncryptor;
}
public MetadataEncryptor getMetadataEncryptor()
{
return metadataEncryptor;
}
public void setJobLockService(JobLockService jobLockService)
{
this.jobLockService = jobLockService;
}
public void setNumThreads(int numThreads)
{
this.numThreads = numThreads;
}
public void setChunkSize(int chunkSize)
{
this.chunkSize = chunkSize;
}
public void setSplitTxns(boolean splitTxns)
{
this.splitTxns = splitTxns;
}
public void setNodeDAO(NodeDAO nodeDAO)
{
this.nodeDAO = nodeDAO;
}
public void setDictionaryDAO(DictionaryDAO dictionaryDAO)
{
this.dictionaryDAO = dictionaryDAO;
}
public void setQnameDAO(QNameDAO qnameDAO)
{
this.qnameDAO = qnameDAO;
}
/**
* Attempts to get the lock. If the lock couldn't be taken, then <tt>null</tt> is returned.
*
* @return Returns the lock token or <tt>null</tt>
*/
private String getLock(long time)
{
try
{
return jobLockService.getLock(LOCK, time);
}
catch (LockAcquisitionException e)
{
return null;
}
}
/**
* Attempts to get the lock. If it fails, the current transaction is marked for rollback.
*/
private void refreshLock(String lockToken, long time)
{
if (lockToken == null)
{
throw new IllegalArgumentException("Must provide existing lockToken");
}
jobLockService.refreshLock(lockToken, LOCK, time);
}
protected void reEncryptProperties(final List<NodePropertyEntity> properties, final String lockToken)
{
final Iterator<NodePropertyEntity> it = properties.iterator();
// TODO use BatchProcessWorkerAdaptor?
BatchProcessor.BatchProcessWorker<NodePropertyEntity> worker = new BatchProcessor.BatchProcessWorker<NodePropertyEntity>()
{
public String getIdentifier(NodePropertyEntity entity)
{
return String.valueOf(entity.getNodeId());
}
public void beforeProcess() throws Throwable
{
refreshLock(lockToken, chunkSize * 100L);
}
public void afterProcess() throws Throwable
{
}
public void process(final NodePropertyEntity entity) throws Throwable
{
NodePropertyValue nodePropValue = entity.getValue();
// TODO check that we have the correct type i.e. can be cast to Serializable
Serializable value = nodePropValue.getSerializableValue();
if(value instanceof SealedObject)
{
SealedObject sealed = (SealedObject)value;
NodePropertyKey propertyKey = entity.getKey();
QName propertyQName = qnameDAO.getQName(propertyKey.getQnameId()).getSecond();
// decrypt...
Serializable decrypted = metadataEncryptor.decrypt(propertyQName, sealed);
// ...and then re-encrypt. The new key will be used.
Serializable resealed = metadataEncryptor.encrypt(propertyQName, decrypted);
// TODO update resealed using batch update?
// does the node DAO do batch updating?
nodeDAO.setNodeProperties(entity.getNodeId(), Collections.singletonMap(propertyQName, resealed));
}
else
{
NodePropertyKey nodeKey = entity.getKey();
QName propertyQName = qnameDAO.getQName(nodeKey.getQnameId()).getSecond();
logger.warn("Encountered an encrypted property that is not a SealedObject, for node id " +
entity.getNodeId() + ", property " + propertyQName);
}
}
};
BatchProcessWorkProvider<NodePropertyEntity> provider = new BatchProcessWorkProvider<NodePropertyEntity>()
{
@Override
public int getTotalEstimatedWorkSize()
{
return properties.size();
}
@Override
public Collection<NodePropertyEntity> getNextWork()
{
List<NodePropertyEntity> sublist = new ArrayList<NodePropertyEntity>(chunkSize);
synchronized(it)
{
int count = 0;
while(it.hasNext() && count < chunkSize)
{
sublist.add(it.next());
count++;
}
}
return sublist;
}
};
new BatchProcessor<NodePropertyEntity>(
"Reencryptor",
transactionHelper,
provider,
numThreads, chunkSize,
applicationContext,
logger, 100).process(worker, splitTxns);
}
/**
* Re-encrypt using the configured backup keystore to decrypt and the main keystore to encrypt
*/
public int bootstrapReEncrypt() throws MissingKeyException
{
if(!metadataEncryptor.backupKeyAvailable(KeyProvider.ALIAS_METADATA))
{
throw new MissingKeyException("Backup key store is either not present or does not contain a metadata encryption key");
}
return reEncrypt();
}
/**
* Re-encrypt by decrypting using the configured keystore and encrypting using a keystore configured using the provided new key store parameters.
* Called from e.g. JMX.
*
* Assumes that the main key store has been already been reloaded.
*
* Note: it is the responsibility of the end user to ensure that the underlying keystores have been set up appropriately
* i.e. the old key store is backed up to the location defined by the property '${dir.keystore}/backup-keystore' and the new
* key store replaces it. This can be done while the repository is running.
*/
public int reEncrypt() throws MissingKeyException
{
if(!metadataEncryptor.keyAvailable(KeyProvider.ALIAS_METADATA))
{
throw new MissingKeyException("Main key store is either not present or does not contain a metadata encryption key");
}
if(!metadataEncryptor.backupKeyAvailable(KeyProvider.ALIAS_METADATA))
{
throw new MissingKeyException("Backup key store is either not present or does not contain a metadata encryption key");
}
int numProps = reEncryptImpl();
return numProps;
}
protected int reEncryptImpl()
{
// Take out a re-encryptor lock
RetryingTransactionCallback<String> txnWork = new RetryingTransactionCallback<String>()
{
public String execute() throws Exception
{
String lockToken = getLock(20000L);
return lockToken;
}
};
String lockToken = transactionService.getRetryingTransactionHelper().doInTransaction(txnWork, false, true);
if(lockToken == null)
{
logger.warn("Can't get lock. Assume multiple re-encryptors ...");
return 0;
}
// get encrypted properties
Collection<PropertyDefinition> propertyDefs = dictionaryDAO.getPropertiesOfDataType(DataTypeDefinition.ENCRYPTED);
Set<QName> qnames = new HashSet<QName>();
for(PropertyDefinition propDef : propertyDefs)
{
qnames.add(propDef.getName());
}
// TODO use callback mechanism, or select based on set of nodes?
List<NodePropertyEntity> properties = nodeDAO.selectNodePropertiesByTypes(qnames);
if(logger.isDebugEnabled())
{
logger.debug("Found " + properties.size() + " properties to re-encrypt...");
}
// reencrypt these properties TODO don't call if num props == 0
reEncryptProperties(properties, lockToken);
if(logger.isDebugEnabled())
{
logger.debug("...done re-encrypting.");
}
return properties.size();
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
{
this.applicationContext = applicationContext;
}
}

View File

@@ -0,0 +1,860 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.filesys;
import java.io.IOException;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.EnumSet;
import java.util.Enumeration;
import java.util.Locale;
import java.util.StringTokenizer;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.filesys.alfresco.AlfrescoClientInfoFactory;
import org.alfresco.filesys.alfresco.ExtendedDiskInterface;
import org.alfresco.jlan.debug.DebugConfigSection;
import org.alfresco.jlan.ftp.FTPConfigSection;
import org.alfresco.jlan.netbios.NetBIOSName;
import org.alfresco.jlan.netbios.NetBIOSNameList;
import org.alfresco.jlan.netbios.NetBIOSSession;
import org.alfresco.jlan.netbios.win32.Win32NetBIOS;
import org.alfresco.jlan.oncrpc.nfs.NFSConfigSection;
import org.alfresco.jlan.server.auth.ClientInfo;
import org.alfresco.jlan.server.config.GlobalConfigSection;
import org.alfresco.jlan.server.config.InvalidConfigurationException;
import org.alfresco.jlan.server.config.ServerConfiguration;
import org.alfresco.jlan.util.IPAddress;
import org.alfresco.jlan.util.Platform;
import org.alfresco.repo.security.authentication.AuthenticationComponent;
import org.alfresco.repo.tenant.TenantService;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.cmr.security.AuthenticationService;
import org.alfresco.service.cmr.security.AuthorityService;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.transaction.TransactionService;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.extensions.config.element.GenericConfigElement;
/**
* Alfresco File Server Configuration Bean Class
*
* @author gkspencer
*/
public abstract class AbstractServerConfigurationBean extends ServerConfiguration implements
ExtendedServerConfigurationAccessor, ApplicationListener, ApplicationContextAware
{
// Debug logging
protected static final Log logger = LogFactory.getLog("org.alfresco.fileserver");
// IP address representing null
public static final String BIND_TO_IGNORE = "0.0.0.0";
// FTP server debug type strings
protected static final String m_ftpDebugStr[] = { "STATE", "RXDATA", "TXDATA", "DUMPDATA", "SEARCH", "INFO", "FILE", "FILEIO", "ERROR", "PKTTYPE",
"TIMING", "DATAPORT", "DIRECTORY", "SSL" };
// Default FTP server port
protected static final int DefaultFTPServerPort = 21;
// Default FTP server session timeout
protected static final int DefaultFTPSrvSessionTimeout = 5000;
// Default FTP anonymous account name
protected static final String DefaultFTPAnonymousAccount = "anonymous";
// NFS server debug type strings
protected static final String m_nfsDebugStr[] = { "RXDATA", "TXDATA", "DUMPDATA", "SEARCH", "INFO", "FILE",
"FILEIO", "ERROR", "TIMING", "DIRECTORY", "SESSION" };
// Default thread pool size
protected static final int DefaultThreadPoolInit = 25;
protected static final int DefaultThreadPoolMax = 50;
// Default memory pool settings
protected static final int[] DefaultMemoryPoolBufSizes = { 256, 4096, 16384, 66000 };
protected static final int[] DefaultMemoryPoolInitAlloc = { 20, 20, 5, 5 };
protected static final int[] DefaultMemoryPoolMaxAlloc = { 100, 50, 50, 50 };
// Memory pool allocation limits
protected static final int MemoryPoolMinimumAllocation = 5;
protected static final int MemoryPoolMaximumAllocation = 500;
// Maximum session timeout
public static final int MaxSessionTimeout = 60 * 60; // 1 hour
// Disk interface to use for shared filesystems
private ExtendedDiskInterface m_repoDiskInterface;
// Runtime platform type
private Platform.Type m_platform = Platform.Type.Unchecked;
// flag to indicate successful initialization
private boolean m_initialised;
// Main authentication service, public API
private AuthenticationService m_authenticationService;
// Authentication component, for internal functions
protected AuthenticationComponent m_authenticationComponent;
// Various services
private NodeService m_nodeService;
private PersonService m_personService;
private TransactionService m_transactionService;
protected TenantService m_tenantService;
private SearchService m_searchService;
private NamespaceService m_namespaceService;
private AuthorityService m_authorityService;
// Local server name and domain/workgroup name
private String m_localName;
private String m_localNameFull;
private String m_localDomain;
// Disable use of native code on Windows, do not use any JNI calls
protected boolean m_disableNativeCode = false;
/**
* Default constructor
*/
public AbstractServerConfigurationBean()
{
super ( "");
}
/**
* Class constructor
*
* @param srvName String
*/
public AbstractServerConfigurationBean( String srvName)
{
super( srvName);
}
/**
* Set the authentication service
*
* @param authenticationService AuthenticationService
*/
public void setAuthenticationService(AuthenticationService authenticationService)
{
m_authenticationService = authenticationService;
}
/**
* Set the filesystem driver for the node service based filesystem
*
* @param diskInterface DiskInterface
*/
public void setDiskInterface(ExtendedDiskInterface diskInterface)
{
m_repoDiskInterface = diskInterface;
}
/**
* Set the authentication component
*
* @param component AuthenticationComponent
*/
public void setAuthenticationComponent(AuthenticationComponent component)
{
m_authenticationComponent = component;
}
/**
* Set the node service
*
* @param service NodeService
*/
public void setNodeService(NodeService service)
{
m_nodeService = service;
}
/**
* Set the person service
*
* @param service PersonService
*/
public void setPersonService(PersonService service)
{
m_personService = service;
}
/**
* Set the transaction service
*
* @param service TransactionService
*/
public void setTransactionService(TransactionService service)
{
m_transactionService = service;
}
/**
* Set the tenant service
*
* @param tenantService TenantService
*/
public void setTenantService(TenantService tenantService)
{
m_tenantService = tenantService;
}
/**
* Set the search service
*
* @param searchService SearchService
*/
public void setSearchService(SearchService searchService)
{
m_searchService = searchService;
}
/**
* Set the namespace service
*
* @param namespaceService NamespaceService
*/
public void setNamespaceService(NamespaceService namespaceService)
{
m_namespaceService = namespaceService;
}
/**
* Set the authority service
*
* @param authService AuthorityService
*/
public void setAuthorityService(AuthorityService authService)
{
m_authorityService = authService;
}
/**
* Check if the configuration has been initialized
*
* @return Returns true if the configuration was fully initialised
*/
public boolean isInitialised()
{
return m_initialised;
}
/**
* Check if the FTP server is enabled
*
* @return boolean
*/
public final boolean isFTPServerEnabled()
{
return hasConfigSection( FTPConfigSection.SectionName);
}
/**
* Check if the NFS server is enabled
*
* @return boolean
*/
public final boolean isNFSServerEnabled()
{
return hasConfigSection( NFSConfigSection.SectionName);
}
/**
* Return the repository disk interface to be used to create shares
*
* @return DiskInterface
*/
public final ExtendedDiskInterface getRepoDiskInterface()
{
return m_repoDiskInterface;
}
/**
* Initialize the configuration using the configuration service
*/
public void init()
{
// Check that all required properties have been set
if (m_authenticationComponent == null)
{
throw new AlfrescoRuntimeException("Property 'authenticationComponent' not set");
}
else if (m_authenticationService == null)
{
throw new AlfrescoRuntimeException("Property 'authenticationService' not set");
}
else if (m_nodeService == null)
{
throw new AlfrescoRuntimeException("Property 'nodeService' not set");
}
else if (m_personService == null)
{
throw new AlfrescoRuntimeException("Property 'personService' not set");
}
else if (m_transactionService == null)
{
throw new AlfrescoRuntimeException("Property 'transactionService' not set");
}
else if (m_repoDiskInterface == null)
{
throw new AlfrescoRuntimeException("Property 'diskInterface' not set");
}
else if (m_authorityService == null)
{
throw new AlfrescoRuntimeException("Property 'authorityService' not set");
}
// Set the platform type
determinePlatformType();
// Create the debug output configuration using a logger for all file server debug output
DebugConfigSection debugConfig = new DebugConfigSection( this);
try
{
debugConfig.setDebug("org.alfresco.filesys.debug.FileServerDebugInterface", new GenericConfigElement( "params"));
}
catch ( InvalidConfigurationException ex)
{
}
// Create the global configuration and Alfresco configuration sections
new GlobalConfigSection( this);
new AlfrescoConfigSection( this);
// Install the Alfresco client information factory
ClientInfo.setFactory( new AlfrescoClientInfoFactory());
// We need to check for a WINS server configuration in the CIFS server config section to initialize
// the NetBIOS name lookups to use WINS rather broadcast lookups, which may be used to get the local
// domain
try {
// Get the CIFS server config section and extract the WINS server config, if available
processWINSServerConfig();
}
catch (Exception ex) {
// Configuration error
logger.error("File server configuration error (WINS), " + ex.getMessage(), ex);
}
// Initialize the filesystems
try
{
// Process the core server configuration
processCoreServerConfig();
// Process the security configuration
processSecurityConfig();
// Process the filesystems configuration
processFilesystemsConfig();
}
catch (Exception ex)
{
// Configuration error
throw new AlfrescoRuntimeException("File server configuration error, " + ex.getMessage(), ex);
}
// Initialize the FTP server
try
{
// Process the FTP server configuration
processFTPServerConfig();
// Log the successful startup
logger.info("FTP server " + (isFTPServerEnabled() ? "" : "NOT ") + "started");
}
catch (Exception ex)
{
// Configuration error
logger.error("FTP server configuration error, " + ex.getMessage(), ex);
}
}
protected abstract void processCoreServerConfig() throws InvalidConfigurationException;
protected abstract void processSecurityConfig();
protected abstract void processFilesystemsConfig();
protected abstract void processFTPServerConfig();
protected void processWINSServerConfig() {}
/**
* Close the configuration bean
*/
public final void closeConfiguration()
{
super.closeConfiguration();
}
/**
* Determine the platform type
*/
private final void determinePlatformType()
{
if ( m_platform == Platform.Type.Unchecked)
m_platform = Platform.isPlatformType();
}
/**
* Parse the platforms attribute returning the set of platform ids
*
* @param platformStr String
*/
protected final EnumSet<Platform.Type> parsePlatformString(String platformStr)
{
// Split the platform string and build up a set of platform types
EnumSet<Platform.Type> platformTypes = EnumSet.noneOf(Platform.Type.class);
if (platformStr == null || platformStr.length() == 0)
return platformTypes;
StringTokenizer token = new StringTokenizer(platformStr.toUpperCase(Locale.ENGLISH), ",");
String typ = null;
try
{
while (token.hasMoreTokens())
{
// Get the current platform type string and validate
typ = token.nextToken().trim();
Platform.Type platform = Platform.Type.valueOf(typ);
if (platform != Platform.Type.Unknown)
platformTypes.add(platform);
else
throw new AlfrescoRuntimeException("Invalid platform type, " + typ);
}
}
catch (IllegalArgumentException ex)
{
throw new AlfrescoRuntimeException("Invalid platform type, " + typ);
}
// Return the platform types
return platformTypes;
}
/**
* Get the local server name and optionally trim the domain name
*
* @param trimDomain boolean
* @return String
*/
public final String getLocalServerName(boolean trimDomain)
{
// Use cached untrimmed version if necessary
if (!trimDomain)
{
return getLocalServerName();
}
// Check if the name has already been set
if (m_localName != null)
return m_localName;
// Find the local server name
String srvName = getLocalServerName();
// Strip the domain name
if (trimDomain && srvName != null)
{
int pos = srvName.indexOf(".");
if (pos != -1)
srvName = srvName.substring(0, pos);
}
// Save the local server name
m_localName = srvName;
// Return the local server name
return srvName;
}
/**
* Get the local server name (untrimmed)
*
* @return String
*/
private String getLocalServerName()
{
// Check if the name has already been set
if (m_localNameFull != null)
return m_localNameFull;
// Find the local server name
String srvName = null;
if (getPlatformType() == Platform.Type.WINDOWS && !isNativeCodeDisabled())
{
// Get the local name via JNI
srvName = Win32NetBIOS.GetLocalNetBIOSName();
}
else
{
// Get the DNS name of the local system
try
{
srvName = InetAddress.getLocalHost().getHostName();
}
catch (UnknownHostException ex)
{
}
}
// Save the local server name
m_localNameFull = srvName;
// Return the local server name
return srvName;
}
/**
* Get the local domain/workgroup name
*
* @return String
*/
public final String getLocalDomainName()
{
// Check if the local domain has been set
if (m_localDomain != null)
return m_localDomain;
// Find the local domain name
String domainName = null;
if (getPlatformType() == Platform.Type.WINDOWS && !isNativeCodeDisabled())
{
// Get the local domain/workgroup name via JNI
domainName = Win32NetBIOS.GetLocalDomainName();
// Debug
if (logger.isDebugEnabled())
logger.debug("Local domain name is " + domainName + " (via JNI)");
}
else
{
NetBIOSName nbName = null;
try
{
// Try and find the browse master on the local network
nbName = NetBIOSSession.FindName(NetBIOSName.BrowseMasterName, NetBIOSName.BrowseMasterGroup, 5000);
// Log the browse master details
if (logger.isDebugEnabled())
logger.debug("Found browse master at " + nbName.getIPAddressString(0));
// Get the NetBIOS name list from the browse master
NetBIOSNameList nbNameList = NetBIOSSession.FindNamesForAddress(nbName.getIPAddressString(0));
if (nbNameList != null)
{
nbName = nbNameList.findName(NetBIOSName.MasterBrowser, false);
// Set the domain/workgroup name
if (nbName != null)
domainName = nbName.getName();
}
}
catch (IOException ex)
{
}
}
// Save the local domain name
m_localDomain = domainName;
// Return the local domain/workgroup name
return domainName;
}
/**
* Parse an adapter name string and return the matching address
*
* @param adapter String
* @return InetAddress
* @exception InvalidConfigurationException
*/
protected final InetAddress parseAdapterName(String adapter)
throws InvalidConfigurationException {
NetworkInterface ni = null;
try {
ni = NetworkInterface.getByName( adapter);
}
catch (SocketException ex) {
throw new InvalidConfigurationException( "Invalid adapter name, " + adapter);
}
if ( ni == null)
throw new InvalidConfigurationException( "Invalid network adapter name, " + adapter);
// Get the IP address for the adapter
InetAddress adapAddr = null;
Enumeration<InetAddress> addrEnum = ni.getInetAddresses();
while ( addrEnum.hasMoreElements() && adapAddr == null) {
// Get the current address
InetAddress addr = addrEnum.nextElement();
if ( IPAddress.isNumericAddress( addr.getHostAddress()))
adapAddr = addr;
}
// Check if we found the IP address to bind to
if ( adapAddr == null)
throw new InvalidConfigurationException( "Adapter " + adapter + " does not have a valid IP address");
// Return the adapter address
return adapAddr;
}
private ApplicationContext applicationContext = null;
/* (non-Javadoc)
* @see org.springframework.context.ApplicationListener#onApplicationEvent(org.springframework.context.ApplicationEvent)
*/
public void onApplicationEvent(ApplicationEvent event)
{
if (event instanceof ContextRefreshedEvent)
{
ContextRefreshedEvent refreshEvent = (ContextRefreshedEvent)event;
ApplicationContext refreshContext = refreshEvent.getApplicationContext();
if (refreshContext != null && refreshContext.equals(applicationContext))
{
// Initialize the bean
init();
}
}
}
/* (non-Javadoc)
* @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
*/
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException
{
this.applicationContext = applicationContext;
}
/**
* Return the authentication service
*
* @return AuthenticationService
*/
protected final AuthenticationService getAuthenticationService()
{
return m_authenticationService;
}
/**
* Return the authentication component
*
* @return AuthenticationComponent
*/
protected final AuthenticationComponent getAuthenticationComponent()
{
return m_authenticationComponent;
}
/**
* Return the node service
*
* @return NodeService
*/
protected final NodeService getNodeService()
{
return m_nodeService;
}
/**
* Return the person service
*
* @return PersonService
*/
protected final PersonService getPersonService()
{
return m_personService;
}
/**
* Return the transaction service
*
* @return TransactionService
*/
protected final TransactionService getTransactionService()
{
return m_transactionService;
}
/**
* Return the tenant service
*
* @return TenantService
*/
protected final TenantService getTenantService()
{
return m_tenantService;
}
/**
* Return the search service
*
* @return SearchService
*/
protected final SearchService getSearchService()
{
return m_searchService;
}
/**
* Return the namespace service
*
* @return NamespaceService
*/
protected final NamespaceService getNamespaceService()
{
return m_namespaceService;
}
/**
* Check if native code calls are disabled
*
* @return boolean
*/
public final boolean isNativeCodeDisabled()
{
return m_disableNativeCode;
}
/**
* Return the named bean
*
* @param beanName String
* @return Object
*/
public final Object getBean( String beanName)
{
return applicationContext.getBean( beanName);
}
/**
* Return the applicatin context
*
* @return ApplicationContext
*/
public final ApplicationContext getApplicationsContext()
{
return applicationContext;
}
/**
* Return the authority service
*
* @return AuthorityService
*/
public final AuthorityService getAuthorityService()
{
return m_authorityService;
}
}

View File

@@ -0,0 +1,196 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.filesys;
import org.alfresco.jlan.server.config.ConfigSection;
import org.alfresco.jlan.server.filesys.DiskInterface;
import org.alfresco.repo.security.authentication.AuthenticationComponent;
import org.alfresco.repo.tenant.TenantService;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.cmr.security.AuthenticationService;
import org.alfresco.service.cmr.security.AuthorityService;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.transaction.TransactionService;
/**
* Alfresco Configuration Section Class
*
* @author gkspencer
*/
public class AlfrescoConfigSection extends ConfigSection {
// Alfresco configuration section name
public static final String SectionName = "Alfresco";
// Disk interface to use for shared filesystems
private DiskInterface m_repoDiskInterface;
// Main authentication service, public API
private AuthenticationService m_authenticationService;
// Authentication component, for internal functions
private AuthenticationComponent m_authenticationComponent;
// Various services
private NodeService m_nodeService;
private PersonService m_personService;
private TransactionService m_transactionService;
private TenantService m_tenantService;
private SearchService m_searchService;
private NamespaceService m_namespaceService;
private AuthorityService m_authorityService;
/**
* Class constructor
*
* @param config ServerConfigurationBean
*/
public AlfrescoConfigSection(AbstractServerConfigurationBean config) {
super( SectionName, config);
// Copy values from the server configuration bean
m_repoDiskInterface = config.getRepoDiskInterface();
m_authenticationService = config.getAuthenticationService();
m_authenticationComponent = config.getAuthenticationComponent();
m_nodeService = config.getNodeService();
m_personService = config.getPersonService();
m_transactionService = config.getTransactionService();
m_tenantService = config.getTenantService();
m_searchService = config.getSearchService();
m_namespaceService = config.getNamespaceService();
m_authorityService = config.getAuthorityService();
}
/**
* Return the repository disk interface to be used to create shares
*
* @return DiskInterface
*/
public final DiskInterface getRepoDiskInterface()
{
return m_repoDiskInterface;
}
/**
* Return the authentication service
*
* @return AuthenticationService
*/
public final AuthenticationService getAuthenticationService()
{
return m_authenticationService;
}
/**
* Return the authentication component
*
* @return AuthenticationComponent
*/
public final AuthenticationComponent getAuthenticationComponent()
{
return m_authenticationComponent;
}
/**
* Return the node service
*
* @return NodeService
*/
public final NodeService getNodeService()
{
return m_nodeService;
}
/**
* Return the person service
*
* @return PersonService
*/
public final PersonService getPersonService()
{
return m_personService;
}
/**
* Return the transaction service
*
* @return TransactionService
*/
public final TransactionService getTransactionService()
{
return m_transactionService;
}
/**
* Return the tenant service
*
* @return TenantService
*/
public final TenantService getTenantService()
{
return m_tenantService;
}
/**
* Return the search service
*
* @return SearchService
*/
public final SearchService getSearchService()
{
return m_searchService;
}
/**
* Return the namespace service
*
* @return NamespaceService
*/
public final NamespaceService getNamespaceService()
{
return m_namespaceService;
}
/**
* Return the authority service
*
* @return AuthorityService
*/
public final AuthorityService getAuthorityService()
{
return m_authorityService;
}
}

View File

@@ -0,0 +1,53 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.filesys;
import org.alfresco.jlan.server.config.ServerConfigurationAccessor;
/**
* An interface exposing some extended capabilities of the AbstractServerConfigurationBean.
*
* @author dward
*/
public interface ExtendedServerConfigurationAccessor extends ServerConfigurationAccessor
{
/**
* Get the local server name and optionally trim the domain name
*
* @param trimDomain
* boolean
* @return String
*/
public String getLocalServerName(boolean trimDomain);
/**
* Get the local domain/workgroup name
*
* @return String
*/
public String getLocalDomainName();
}

View File

@@ -0,0 +1,274 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.filesys;
import java.io.IOException;
import java.io.PrintStream;
import java.net.SocketException;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.jlan.ftp.FTPConfigSection;
import org.alfresco.jlan.ftp.FTPServer;
import org.alfresco.jlan.server.NetworkServer;
import org.alfresco.jlan.server.config.ServerConfiguration;
import org.springframework.extensions.surf.util.AbstractLifecycleBean;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* FTP Server Class
*
* <p>Create and start the server components required to run the FTP server.
*
* @author GKSpencer
*/
public class FTPServerBean extends AbstractLifecycleBean
{
private static final Log logger = LogFactory.getLog("org.alfresco.ftp.server");
// Server configuration and sections
private ServerConfiguration filesysConfig;
private FTPConfigSection m_ftpConfig;
// The actual FTP server
private FTPServer ftpServer;
/**
* Class constructor
*
* @param serverConfig ServerConfiguration
*/
public FTPServerBean(ServerConfiguration serverConfig)
{
filesysConfig = serverConfig;
}
/**
* Return the server configuration
*
* @return ServerConfiguration
*/
public final ServerConfiguration getConfiguration()
{
return filesysConfig;
}
/**
* @return Returns true if the server started up without any errors
*/
public boolean isStarted()
{
return (ftpServer != null && filesysConfig.isServerRunning("FTP"));
}
/**
* Start the FTP server components
*
* @exception SocketException If a network error occurs
* @exception IOException If an I/O error occurs
*/
public final void startServer() throws SocketException, IOException
{
try
{
// Create the FTP server, if enabled
m_ftpConfig = (FTPConfigSection) filesysConfig.getConfigSection( FTPConfigSection.SectionName);
if (m_ftpConfig != null)
{
// Create the FTP server
ftpServer = new FTPServer(filesysConfig);
filesysConfig.addServer(ftpServer);
}
// Start the server
if(ftpServer != null)
{
// Start the FTP server
if (logger.isInfoEnabled())
logger.info("Starting server " + ftpServer.getProtocolName() + " ...");
ftpServer.startServer();
}
}
catch (Throwable e)
{
ftpServer = null;
throw new AlfrescoRuntimeException("Failed to start FTP Server", e);
}
// success
}
/**
* Stop the FTP server components
*/
public final void stopServer()
{
if (filesysConfig == null)
{
// initialisation failed
return;
}
// Shutdown the FTP server
if ( ftpServer != null)
{
if (logger.isInfoEnabled())
logger.info("Shutting server " + ftpServer.getProtocolName() + " ...");
// Stop the server
ftpServer.shutdownServer(false);
// Remove the server from the global list
getConfiguration().removeServer(ftpServer.getProtocolName());
ftpServer = null;
}
}
/**
* Runs the FTP server directly
*
* @param args String[]
*/
public static void main(String[] args)
{
PrintStream out = System.out;
out.println("FTP Server Test");
out.println("---------------");
ClassPathXmlApplicationContext ctx = null;
try
{
// Create the configuration service in the same way that Spring creates it
ctx = new ClassPathXmlApplicationContext("alfresco/application-context.xml");
// Get the FTP server bean
FTPServerBean server = (FTPServerBean) ctx.getBean("ftpServer");
if (server == null)
{
throw new AlfrescoRuntimeException("Server bean 'ftpServer' not defined");
}
// Stop the CIFS server components, if running
NetworkServer srv = server.getConfiguration().findServer("SMB");
if ( srv != null)
srv.shutdownServer(true);
srv = server.getConfiguration().findServer("NetBIOS");
if ( srv != null)
srv.shutdownServer(true);
// Only wait for shutdown if the FTP server is enabled
if ( server.getConfiguration().hasConfigSection(FTPConfigSection.SectionName))
{
// FTP server should have automatically started
//
// Wait for shutdown via the console
out.println("Enter 'x' to shutdown ...");
boolean shutdown = false;
// Wait while the server runs, user may stop the server by typing a key
while (shutdown == false)
{
// Wait for the user to enter the shutdown key
int ch = System.in.read();
if (ch == 'x' || ch == 'X')
shutdown = true;
synchronized (server)
{
server.wait(20);
}
}
// Stop the server
server.stopServer();
}
}
catch (Exception ex)
{
ex.printStackTrace();
}
finally
{
if (ctx != null)
{
ctx.close();
}
}
System.exit(1);
}
@Override
protected void onBootstrap(ApplicationEvent event)
{
try
{
startServer();
}
catch (SocketException e)
{
throw new AlfrescoRuntimeException("Failed to start FTP server", e);
}
catch (IOException e)
{
throw new AlfrescoRuntimeException("Failed to start FTP server", e);
}
}
@Override
protected void onShutdown(ApplicationEvent event)
{
stopServer();
// Clear the configuration
filesysConfig = null;
}
}

Some files were not shown because too many files have changed in this diff Show More