Friday, September 9, 2011

Resetting a Forgotten Alfresco admin password

It may never happen to you, but if you ever lose or forget the password for the administration user in Alfresco, it is possible to reset that password within the database.

While the reset process is easy to do, it involves fiddling directly with the database, something which you should be careful with, especially if that's not something you typically work with.  If you're having problems with a production system, by all means, experiment first on a test system.  You don't want to make a bad situation worse.

First run the following SQL to find out the identifying parameters for how the admin password is stored:

SELECT anp1.node_id,
       anp1.qname_id,
       anp1.string_value as hash_pass,
       anp2.string_value as user_string
FROM alf_node_properties anp1
        INNER JOIN alf_qname aq1
           ON aq1.id       = anp1.qname_id
        INNER JOIN alf_node_properties anp2 
           ON anp2.node_id = anp1.node_id
        INNER JOIN alf_qname aq2            
           ON aq2.id       = anp2.qname_id
WHERE aq1.local_name    = 'password'
AND aq2.local_name    = 'username';


After doing that, you'll see something like the following:

In this example, we can see that the password for admin is set to the default MD5 hash value for 'admin'.  You can set it back to some other MD5 hash value, but then of course, you'd need to calculate the MD5 value for whatever your desired password is.  It's easier to set it back to the default value corresponding to 'admin':  '209c6174da490caeb422f3fa5a7ae634'.  Then, once you can log back into Alfresco again you would be able to change the password to something else.

To update the password, the following SQL works:

UPDATE alf_node_properties 
 SET string_value='209c6174da490caeb422f3fa5a7ae634'
 WHERE 
 node_id=THEADMINNODEID
 and
 qname_id=THEADMINQNAME

Or, from the example shown in the screenshot above:

UPDATE alf_node_properties 
 SET string_value='209c6174da490caeb422f3fa5a7ae634'
 WHERE 
 node_id=4
 and
 qname_id=10

Saturday, August 13, 2011

Alfresco 3 Cookbook: Quick Answers to Common Problems by Snig Bhaumik

Alfresco 3 Cookbook: Quick Answers to Common Problems by Snig Bhaumik is the latest book from PACKT Publishing about the Alfresco Open Source Enterprise Content Management System.(CMS).  The book is now complete and is available in both print and electronic format.  Prior to the book being completed, parts of it had been  made available earlier by PACKT in a pre-release RAW format.

When I heard about this book, I was immediately interested.  The concept of having a cookbook-style reference book filled with easy-to-follow self-contained recipes for how to perform common tasks in Alfresco is very appealing.

The book is about 380 pages long and is organized into 14 chapters, each chapter covering one category of Alfresco usage, like the Administration Console, the Web Client, and the Content Model.  Then within each chapter, there are a number of recipes that describe how to perform specific tasks.  In total, the book covers more than 70 such recipes.  For example, Chapter 6 covers how to customize the Alfresco Web Client and includes recipes like the following:

  • Changing languages in the Login page
  • Changing textbox length and text area size
  • Controlling the Date Picker
  • Controlling the sidebar display

Each of the sections or 'recipes' is then broken down into a brief description of the task followed by a sub-section titled "Getting Ready" and then another one titled "How to do it...".  There's also an occasional "There's more..." section that contains a more in-depth explanation for why or how something works.  The "How to do it..." section is the heart of the recipe and it breaks down each task into step-by-step instructions for how to complete it.

The text descriptions are very clear and there are many illustrations, mostly of UI screenshots.

I like the cookbook style the book uses -- this book is part of PACKT's cookbook series.  But to stay true to the cookbook format, I would have preferred a larger number of recipes with shorter discussions about the background mechanics for how any one particular recipe works.  Some of the recipes are a bit long and stretch to as many as twenty pages.


The book starts out by describing the Alfresco installation process and then discusses topics useful to end users and administrators.  In this part of the book and even in some of the later chapters, while I liked the overall style of presentation and format, it felt like there was a lot of overlap with material already covered in other places like Munwar Shariff's book Alfresco 3 Enterprise Content Management Implementation or even with what can be found in the standard Alfresco documentation.  I would have liked to have seen more 'tips, tricks and gotchas' that go beyond just that material.

Later chapters discuss topics for those who want to customize and develop in the Alfresco environment. Topics include the Content Model, the Alfresco Javascript API, FreeMarker and Workflow. The final chapter describes how to download Alfresco source and set up a build environment.  I did like the discussion in Chapter 10 on Web Scripts.

Chapter 12 had me a bit puzzled.  That chapter discusses integration of Alfresco with Microsoft applications and has a lengthy discussion of Alfresco's MS-Office 2003 plug-in instead of discussing SharePoint protocol integration.  I may be wrong, but I had been under the impression that the Office plugin for Alfresco has been problematic and doesn't support Office 2010.

The main caveat that I have about the book though is its focus on the use of Alfresco's older Explorer client.  Alfresco Share is only mentioned on a couple of pages early on in the chapter that describes installation.  I would have liked to have seen much more about Share.  I think that almost all new deployments of Alfresco today will want to use Share as the web client, not the older Explorer client.

So, in general, I thought this book was well written and I think that you'll find a lot of useful facts about Alfresco here.  I also find the cookbook format used by the book very appealing.  But you'll likely be disappointed if you're looking for a book that covers Alfresco Share. I expect that PACKT has some books in the works on Alfresco Share coming up.  In the near term though, there is a good overview discussion on Share in the latter part of Munwar's book that I reference above.

** I'd like to thank PACKT Publishing for making available to me a complementary copy of this book for review.

Monday, April 4, 2011

Alfresco Share Permissions/Roles -- Part II

Creating Custom Alfresco Permissions/Roles

In the previous blog we saw how we were able to fairly easily replace the Share Manage Permissions dialog with the Manage Permissions page used for the Repository button browser.  This allowed us to be able to assign at a much more granular level permissions to the folders and items that are stored within a Share site Document Library.

Now consider the scenario where we would like to be able to invite users to our site, and these users should be able to see and modify only a selected set of documents within the Document Library.  We want access to the site for the majority of users to be unrestricted.  This scenario doesn't work well with the standard Share site roles of Manager, Collaborator, Contributor and Consumer.

To invite a restricted user to the site, we still need to give them a role.  Even if we give this user the role of Site Consumer, they will be able to see much more content in the site than what we want them to see.

What I propose here to help solve this problem is to add new custom Share site roles.  Alfresco has a wiki page here that provides a good start for what needs to be done in creating a custom Share role.  After following the instructions there, which were targeted for version 3.2r, I ran into some issues, and I noted a number of other people in the Alfresco forums also had some issues with it.

What I describe here should work with a fresh Alfresco install.  Trying to add new roles after Share sites have already been created will likely result in errors being thrown.  The reason why this happens is that the appropriate group authorities will not exist and Share will report that as an error.  Once these new roles are created, Share expects them to exist for all sites.  The absence of these authorities for existing sites likely is  something that can be corrected by manually creating the correct authority objects in Alfresco, but we don't attempt to do that here.

Here we will create three new permissions sets/roles called External Consumer, External Contributor, and External Collaborator. The permissions for each of these roles are identical to those of the corresponding Site Consumer, Site Contributor and Site Collaborator that come standard with Share.  What will be different is how these permissions are applied to content in the Document Library.

To do that, we edit the file sitePermissionDefinitions.xml and replicate the lines for SiteContributor, SiteConsumer, and SiteCollaborator permissionGroups.  This file is then placed into the following directory:
tomcat/shared/classes/alfresco/extension/model/
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE permissions >

<permissions>
    
    <!-- Namespaces used in type references -->
    
   <namespaces>
      <namespace uri="http://www.alfresco.org/model/system/1.0" prefix="sys"/>
      <namespace uri="http://www.alfresco.org/model/content/1.0" prefix="cm"/>
      <namespace uri="http://www.alfresco.org/model/site/1.0" prefix="st"/>
   </namespaces>
   
   <!-- ============================================ -->
   <!-- Permissions specific to the wiki integration -->
   <!-- ============================================ -->
   
   <permissionSet type="st:site" expose="selected">
   
      <permissionGroup name="SiteManager" allowFullControl="true" expose="true" />
      
      <permissionGroup name="SiteCollaborator" allowFullControl="false" expose="true">
         <includePermissionGroup permissionGroup="Collaborator" type="cm:cmobject" />
      </permissionGroup>
      
      <permissionGroup name="SiteContributor" allowFullControl="false" expose="true">
         <includePermissionGroup permissionGroup="Contributor" type="cm:cmobject" />
      </permissionGroup>
      
      <permissionGroup name="SiteConsumer" allowFullControl="false" expose="true">
         <includePermissionGroup permissionGroup="Consumer" type="cm:cmobject" />
      </permissionGroup>

      <permissionGroup name="ExternalCollaborator" allowFullControl="false" expose="true">
         <includePermissionGroup permissionGroup="Collaborator" type="cm:cmobject" />
      </permissionGroup>
      
      <permissionGroup name="ExternalContributor" allowFullControl="false" expose="true">
         <includePermissionGroup permissionGroup="Contributor" type="cm:cmobject" />
      </permissionGroup>
      
      <permissionGroup name="ExternalConsumer" allowFullControl="false" expose="true">
         <includePermissionGroup permissionGroup="Consumer" type="cm:cmobject" />
      </permissionGroup>
      
   </permissionSet>

</permissions>
We need to alert Alfresco that this override file should be loaded on startup.  To do that, we create a new file with the path
tomcat/shared/classes/alfresco/extension/restricted-role-context.xml.
The contents of that file are as follows:
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'>

<!-- This file enables Alfresco Custom Site Roles.  It should be placed in shared/classes/extension -->

<beans>

    <bean id="siteService_permissionBootstrap" parent="permissionModelBootstrap">
     <property name="model" value="alfresco/extension/model/sitePermissionDefinitions.xml"/>
    </bean>

</beans>
Finally, there are a number of files where we add string properties that can be picked up so that the new role names display correctly within the Share UI.
First we create the file
tomcat/shared/classes/alfresco/web-extension/invitation-service-context.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'>
<beans>
     
    <bean id="invitationResourceBundles" class="org.alfresco.i18n.ResourceBundleBootstrapComponent">
     <property name="resourceBundles">
      <list>
       <value>alfresco.web-extension.messages.invitation-service</value>
      </list>
     </property>
   </bean>

</beans>
And the associated property file:
tomcat/shared/classes/alfresco/web-extension/messages/invitation-service.properties:
invitation.invitesender.email.role.ExternalCollaborator=External Collaborator
invitation.invitesender.email.role.ExternalContributor=External Contributor
invitation.invitesender.email.role.ExternalConsumer=External Consumer
We add the following lines to the file:
tomcat/shared/classes/alfresco/web-extension/custom-slingshot-application-context.xml
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'>

<beans>
   <bean id="webscripts.resources" class="org.springframework.extensions.surf.util.ResourceBundleBootstrapComponent">
      <property name="resourceBundles">
         <list>
             <value>alfresco.messages.common</value>
             <value>alfresco.messages.slingshot</value>
            <value>alfresco.web-extension.messages.slingshot</value>
         </list>
      </property>
   </bean>
</beans>
And the referenced new properties are in the file:
tomcat/shared/classes/alfresco/web-extension/messages/slingshot.properties
## Custom Site External Reviewer Roles
role.ExternalCollaborator=External Collaborator
role.ExternalContributor=External Contributor
role.ExternalConsumer=External Consumer
Next we copy the file
tomcat/webapps/share/WEB-INF/classes/alfresco/web-extension/site-webscripts/org/alfresco/components/folder-details/folder-info.get.properties 
to
tomcat/shared/classes/alfresco/web-extension/site-webscripts/org/alfresco/components/folder-details
and include these lines at the end of the file:
folder-info.role.ExternalCollaborator=External Collaborator
folder-info.role.ExternalConsumer=External Consumer
folder-info.role.ExternalContributor=External Contributor
Similarly copy the file
tomcat/webapps/share/WEB-INF/classes/alfresco/web-extension/site-webscripts/org/alfresco/components/document-details/document-info.get.properties 
to
tomcat/shared/classes/alfresco/web-extension/site-webscripts/org/alfresco/components/document-details/document-info.get.properties
and include these lines at the end:
## Customer External Review Role
document-info.role.ExternalCollaborator=External Collaborator
document-info.role.ExternalConsumer=External Consumer
document-info.role.ExternalContributor=External Contributor
Copy the file
tomcat/webapps/share/WEB-INF/classes/alfresco/web-extension/site-webscripts/org/alfresco/components/invite/invitationlist.get.properties 
to
tomcat/shared/classes/alfresco/web-extension/site-webscripts/org/alfresco/components/invite/invitationlist.get.properties
and include these lines at the end:
## External Groups and Roles for Site
group.ExternalCollaborator=External Collaborators
role.ExternalCollaborator=External Collaborators

group.ExternalConsumer=External Consumers
role.ExternalConsumer=External Consumers

group.ExternalCotributor=External Contributors
role.ExternalContributor=External Contributors

And finally, copy the file
tomcat/webapps/share/WEB-INF/classes/alfresco/web-extension/site-webscripts/org/alfresco/modules/documentlibrary/permissions.get.properties 
to
tomcat/shared/classes/alfresco/web-extension/site-webscripts/org/alfresco/modules/documentlibrary/permissions.get.properties
and include these lines at the end:
## External Groups and Roles for Site
group.ExternalCollaborator=External Collaborators
role.ExternalCollaborator=External Collaborator privileges

group.ExternalConsumer=External Consumers
role.ExternalConsumer=External Consumer privileges

group.ExternalCotributor=External Contributors
role.ExternalContributor=External Contributor privileges

External Site Roles in Action
Whew...
After doing that, we stop and restart the Alfresco server.  We can then log in and create a new Share site.

Immediately after creating the site, we can navigate to the root node for the site by using the Repository button in Share.  When we click on the Manage Permissions button for the new site, we can see that our new permissions sets (ExternalConsumer, ExternalCollaborator, and External Consumer) are included automatically and applied to this node.


At the Document Library level, we can create a folder structure where two top level folders are to be accessible only by the standard SiteConsumer, SiteCollaborator and SiteContributor.  And a third folder is available to standard users and is also open for viewing to External Reviewers.


The internal folder permissions look as follows:
With these settings, only internal reviewers will be able to see the content of this folder.

In the External Reviewer folder, we set the permissions as follows:
In this case, we can see that both external and standard internal reviewers are able to access this folder.
And if we navigate one Folder down in the hierarchy of the External Reviewer Folder, we can see that the inheritance of these permissions flow down.  One folder down, we see the permissions are set in the same way:
One last note.  In order for the user/group search capability to work correctly on this form, I found that the "Add User/Group" button on this page does not find Alfresco Share groups.  By default, search is performed for groups within the ALF.DEFAULT zone which does not include the Share zone groups.  In order to find our groups, the following Javascript file was changed (Note the changes in bold made to that file):
tomcat/shared/classes/alfresco/web-extension/site-webscripts/org/alfresco/components/people-finder/authority-query.get.js
var getMappings = function()
{
   var mappings = [],
      authorityType = args.authorityType === null ? "all" : String(args.authorityType).toLowerCase();
   
   if (authorityType === "all" || authorityType == "user")
   {
      mappings.push(
      {
         type: MAPPING_TYPE.API,
         url: "/api/people?filter=" + encodeURIComponent(args.filter),
         rootObject: "people",
         fn: mapUser
      });
   }

   if (authorityType === "all" || authorityType === "group")
   {
      var url = "/api/groups?shortNameFilter=" + encodeURIComponent(args.filter);
//    if (args.zone !== "all")
// All authorities are to be found
      if (args.zone !== "all" && args.zone !== null)
      {
         url += "&zone=" + encodeURIComponent(args.zone === null ? "APP.DEFAULT" : args.zone);
      }
...