Alfresco

image0

Presentation

Alfresco is an ECM/BPM software.

Since 4.0 release, it offers an easy way to configure SSO thanks to authentication subsystems.

Authentication against LL::NG can be done through:

  • HTTP headers (LL::NG Handler)

  • SAML 2 (LL::NG as SAML2 IDP)

Tip

Alfresco now recommends SAML2 method

HTTP headers

Alfresco

Tip

The official documentation can be found here: http://docs.alfresco.com/4.0/tasks/auth-alfrescoexternal-sso.html

You need to find the following files in your Alfresco installation:

  • alfresco-global.properties (ex: tomcat/shared/classes/alfresco-global.properties)

  • share-config-custom.xml (ex: tomcat/shared/classes/alfresco/web-extension/share-config-custom.xml)

The first will allow one to configure SSO for the alfresco webapp, and the other for the share webapp.

Edit first alfresco-global.properties and add the following:

### SSO ###
authentication.chain=external1:external
external.authentication.enabled=true
external.authentication.defaultAdministratorUserNames=
external.authentication.proxyUserName=
external.authentication.proxyHeader=Auth-User
external.authentication.userIdPattern=

Edit then share-config-custom.xml and uncomment the last part. In the <endpoint>, change <connector-id> value to alfrescoHeader and change the <userHeader> value to Auth-User:

<config evaluator="string-compare" condition="Remote">
   <remote>
       <keystore>
          <path>alfresco/web-extension/alfresco-system.p12</path>
          <type>pkcs12</type>
          <password>alfresco-system</password>
      </keystore>

      <connector>
         <id>alfrescoCookie</id>
         <name>Alfresco Connector</name>
         <description>Connects to an Alfresco instance using cookie-based authentication</description>
         <class>org.alfresco.web.site.servlet.SlingshotAlfrescoConnector</class>
      </connector>

      <connector>
         <id>alfrescoHeader</id>
         <name>Alfresco Connector</name>
         <description>Connects to an Alfresco instance using header and cookie-based authentication</description>
         <class>org.alfresco.web.site.servlet.SlingshotAlfrescoConnector</class>
         <userHeader>Auth-User</userHeader>
      </connector>

      <endpoint>
         <id>alfresco</id>
         <name>Alfresco - user access</name>
         <description>Access to Alfresco Repository WebScripts that require user authentication</description>
         <connector-id>alfrescoHeader</connector-id>
         <endpoint-url>http://localhost:8080/alfresco/s</endpoint-url>
         <identity>user</identity>
         <external-auth>true</external-auth>
      </endpoint>
   </remote>
</config>

You need to restart Tomcat to apply changes.

Danger

Now you can log in with a simple HTTP header. You need to restrict access to Alfresco to LL::NG.

LL::NG

Headers

Just set the Auth-User header with the attribute that carries the user login, for example $uid.

Rules

Set the default rule to what you need.

Other rules:

  • Unprotect access to some resources: ^/share/res => unprotect

  • Catch logout: ^/share/page/dologout => logout_app_sso

SAML2

Alfresco

Install SAML Alfresco module package:

cp alfresco-saml-repo-1.0.1.amp <ALFRESCO_HOME>/amps
cp alfresco-saml-share-1.0.1.amp <ALFRESCO_HOME>/amps_share
./bin/apply_amp.sh

Generate SAML certificate:

keytool -genkeypair -alias my-saml-key -keypass change-me -storepass change-me -keystore my-saml.keystore -storetype JCEKS

Export the keystore:

mv my-saml.keystore alf_data/keystore
cat <<EOT > alf_data/keystore/my-saml.keystore-metadata.properties
aliases=my-saml-key
keystore.password=change-me
my-saml-key.password=change-me
EOT
cat <<EOT >> tomcat/shared/classes/alfresco-global.properties

saml.keystore.location=\${dir.keystore}/my-saml.keystore
saml.keystore.keyMetaData.location=\${dir.keystore}/my-saml.keystore-metadata.properties
EOT

Edit then share-config-custom.xml:

...
    <config evaluator="string-compare" condition="CSRFPolicy" replace="true">



    <!--
        If using https make a CSRFPolicy with replace="true" and override the properties section.
        Note, localhost is there to allow local checks to succeed.



        I.e.
        <properties>
            <token>Alfresco-CSRFToken</token>
            <referer>https://your-domain.com/.*|http://localhost:8080/.*</referer>
            <origin>https://your-domain.com|http://localhost:8080</origin>
        </properties>
    -->



        <filter>



            <!-- SAML SPECIFIC CONFIG -  START -->



            <!--
             Since we have added the CSRF filter with filter-mapping of "/*" we will catch all public GET to avoid them
             having to pass through the remaining rules.
             -->
            <rule>
                <request>
                    <method>GET</method>
                    <path>/res/.*</path>
                </request>
            </rule>



            <!-- Incoming posts from IDPs do not require a token -->
            <rule>
                <request>
                    <method>POST</method>
                    <path>/page/saml-authnresponse|/page/saml-logoutresponse|/page/saml-logoutrequest</path>
                </request>
            </rule>



            <!-- SAML SPECIFIC CONFIG -  STOP -->



            <!-- EVERYTHING BELOW FROM HERE IS COPIED FROM share-security-config.xml -->



            <!--
             Certain webscripts shall not be allowed to be accessed directly form the browser.
             Make sure to throw an error if they are used.
             -->
            <rule>
                <request>
                    <path>/proxy/alfresco/remoteadm/.*</path>
                </request>
                <action name="throwError">
                    <param name="message">It is not allowed to access this url from your browser</param>
                </action>
            </rule>



            <!--
             Certain Repo webscripts should be allowed to pass without a token since they have no Share knowledge.
             TODO: Refactor the publishing code so that form that is posted to this URL is a Share webscript with the right tokens.
             -->
            <rule>
                <request>
                    <method>POST</method>
                    <path>/proxy/alfresco/api/publishing/channels/.+</path>
                </request>
                <action name="assertReferer">
                    <param name="referer">{referer}</param>
                </action>
                <action name="assertOrigin">
                    <param name="origin">{origin}</param>
                </action>
            </rule>



            <!--
             Certain Surf POST requests from the WebScript console must be allowed to pass without a token since
             the Surf WebScript console code can't be dependent on a Share specific filter.
             -->
            <rule>
                <request>
                    <method>POST</method>
                    <path>/page/caches/dependency/clear|/page/index|/page/surfBugStatus|/page/modules/deploy|/page/modules/module|/page/api/javascript/debugger|/page/console</path>
                </request>
                <action name="assertReferer">
                    <param name="referer">{referer}</param>
                </action>
                <action name="assertOrigin">
                    <param name="origin">{origin}</param>
                </action>
            </rule>



            <!-- Certain Share POST requests does NOT require a token -->
            <rule>
                <request>
                    <method>POST</method>
                    <path>/page/dologin(\?.+)?|/page/site/[^/]+/start-workflow|/page/start-workflow|/page/context/[^/]+/start-workflow</path>
                </request>
                <action name="assertReferer">
                    <param name="referer">{referer}</param>
                </action>
                <action name="assertOrigin">
                    <param name="origin">{origin}</param>
                </action>
            </rule>



            <!-- Assert logout is done from a valid domain, if so clear the token when logging out -->
            <rule>
                <request>
                    <method>POST</method>
                    <path>/page/dologout(\?.+)?</path>
                </request>
                <action name="assertReferer">
                    <param name="referer">{referer}</param>
                </action>
                <action name="assertOrigin">
                    <param name="origin">{origin}</param>
                </action>
                <action name="clearToken">
                    <param name="session">{token}</param>
                    <param name="cookie">{token}</param>
                </action>
            </rule>



            <!-- Make sure the first token is generated -->
            <rule>
                <request>
                    <session>
                        <attribute name="_alf_USER_ID">.+</attribute>
                        <attribute name="{token}"/>
                        <!-- empty attribute element indicates null, meaning the token has not yet been set -->
                    </session>
                </request>
                <action name="generateToken">
                    <param name="session">{token}</param>
                    <param name="cookie">{token}</param>
                </action>
            </rule>



            <!-- Refresh token on new "page" visit when a user is logged in -->
            <rule>
                <request>
                    <method>GET</method>
                    <path>/page/.*</path>
                    <session>
                        <attribute name="_alf_USER_ID">.+</attribute>
                        <attribute name="{token}">.+</attribute>
                    </session>
                </request>
                <action name="generateToken">
                    <param name="session">{token}</param>
                    <param name="cookie">{token}</param>
                </action>
            </rule>



            <!--
             Verify multipart requests from logged in users contain the token as a parameter
             and also correct referer & origin header if available
             -->
            <rule>
                <request>
                    <method>POST</method>
                    <header name="Content-Type">multipart/.+</header>
                    <session>
                        <attribute name="_alf_USER_ID">.+</attribute>
                    </session>
                </request>
                <action name="assertToken">
                    <param name="session">{token}</param>
                    <param name="parameter">{token}</param>
                </action>
                <action name="assertReferer">
                    <param name="referer">{referer}</param>
                </action>
                <action name="assertOrigin">
                    <param name="origin">{origin}</param>
                </action>
            </rule>



            <!--
             Verify that all remaining state changing requests from logged in users' requests contains a token in the
             header and correct referer & origin headers if available. We "catch" all content types since just setting it to
             "application/json.*" since a webscript that doesn't require a json request body otherwise would be
             successfully executed using i.e."text/plain".
             -->
            <rule>
                <request>
                    <method>POST|PUT|DELETE</method>
                    <session>
                        <attribute name="_alf_USER_ID">.+</attribute>
                    </session>
                </request>
                <action name="assertToken">
                    <param name="session">{token}</param>
                    <param name="header">{token}</param>
                </action>
                <action name="assertReferer">
                    <param name="referer">{referer}</param>
                </action>
                <action name="assertOrigin">
                    <param name="origin">{origin}</param>
                </action>
            </rule>
        </filter>
    </config>
...

Configure SAML service provider using the Alfresco admin console (/alfresco/s/enterprise/admin/admin-saml).

Set the following parameters:

To finish with Alfresco configuration, tick the “Enable SAML authentication (SSO)” box.

LL::NG

Configure SAML service and set a certificate as signature public key in metadata.

Export Alfresco SAML Metadata from admin console and import them in LL::NG.

In the authentication response option, set:

  • Default NameID Format: Unspecified

  • Force NameID session key: uid

And you can define these exported attributes:

  • GivenName

  • Surname

  • Email