Unlocking a page with system user

lir

08-02-2019

Hi,

While coding a WorkflowProcess implementation, I need to create a "system user" who can unlock pages.

Pages are locked through a WorkflowSession on a previous workflow step.

How can I give the "system user" the permissions to unlock pages?

Obviously I don't what to use an admin session, and I'm wondering how can I grant unlock permissions in AEM Security Permissions tab!

Thank you,

Lir

Accepted Solutions (1)

Accepted Solutions (1)

Gaurav-Behl

MVP

14-02-2019

I'd read a thread somewhere which I'll try to find again & post here. It mentioned that 'page.canUnlock()' won't work but 'page.unlock()' will.

Answers (24)

Answers (24)

Gaurav-Behl

MVP

14-02-2019

Finally!!

Its probably because the second user who is trying to unlock doesn't have the read permissions on /home/users/..  path to read it's own ACL to find out that can it unlock that specific /content path?

lir

14-02-2019

That was it!

It works now with admin AND with the system user for which I set "jcr:all"

I will reduce the privileges to only what's required of unlocking and that would be it.

It is strange why page.canUnlock() would return false, but no harm now that I know.

Thank you

lir

14-02-2019

In any case the question remains:

Shouldn't the code below always work on an admin session?

According to AEM docs it should, but it does not!

Resource pageResource = adminResourceResolver.getResource(pagePath);

Page page = pageResource.adaptTo(Page.class);

if (page.canUnlock()) { // should always return true on admin session

    page.unlock();

}

lir

14-02-2019

I can easily use the same "system user" to lock and then later unlock the page without even the need of impersonation, but that would not satisfy this requirement:

- The workflow initiator should be able to manually unlock the page at any time, no matter the state of the workflow. (he can't, unless my workflow code locks the page under his session)

I expected that AEM had a clear straightforward implementation of the page locking mechanism, so that developers did not need to dig into the inner workings of jcr locking.

It will take some time for me, as I'm new to AEM and JCR.

Thank you for the prompt responses, very appreciated.

Gaurav-Behl

MVP

14-02-2019

I don't have a 6.2 setup for now.

If you've setup the system user properly and given all ACLs then probably the next thing to try would be impersonation or other take the jcr properties/node route.

I would recommend you to go through JCR specs that explain more about lock tokens and other aspects, that would definitely throw other options to try out - JCR 2.0: 17 Locking (Content Repository for Java Technology API v2.0)

Additionally, the content repository may give permission to some sessions to remove locks for which they are not the owner. ...

In order to use the lock token as a key, it must be added to the session, thus permitting that session to alter the nodes to which the lock applies or to remove the lock. When a lock token is attached to a Session, the session becomes an owner of the lock.

I found a couple of links, check if these help -

lir

13-02-2019

Hi,

I checked: my system user already has "jcr:lockManagement" privileges.

I even gave him "jcr:all" but still he could not unlock the page.

Then for the sake of "proof of concept" I tried unlocking by making use of "admin" user via the deprecated :

getAdministrativeResourceResolver(adminAuthenticationInfo)

as Gaurav hinted.

Even the admin user could not unlock!

Can someone please try unlocking via a "system user" or admin (through code) in 6.2.

On the other hand, me and my colleagues have noticed that even unlocking through the UI (as admin) is sometimes a hit and miss. You have to log out, login again or wait for a while etc.

Is there a problem with unlocking in 6.2, do we have to wait till we upgrade to 6.4?

Thank you!

lir

13-02-2019

Where can I grant "jcr:lockManagement" to my system user and on my specific content path?

I see no way of doing it in /useradmin Permissions tab of my user.

Thanks again

Gaurav-Behl

MVP

11-02-2019

If you follow ACLs on "admin" user, you can clearly notice that it has "jcr:lockManagement"  that's why admin user can unlock any other user's locks.

I doubt that the custom system user has that kind of granular access to each path in repo. Debug and you would find it out.  I assume that would work with getAdministrativeResourceResolver("admin, "admin, <path>); and not with getAdministrativeResourceResolver(null);

lir

11-02-2019

I will, try but not quite optimistic because as a stated earlier:

- before asking the question in this forum, I made a quick attempt using the deprecated getAdministrativeResourceResolver(null); to get an admin session (who according to documentation should be able to unlock everything), but unlocking failed because page.canUnlock() returned false. Even under admin session!!?

Gaurav-Behl

MVP

11-02-2019

You could add that system user to the "admin" group or otherwise grant "jcr:lockManagement" and potentially, "jcr:versionManagement", "jcr:modifyAccessControl" ACLs to that system-user on that specific content path. That ways the system user would behave as "admin" for that content path. Follow "admin" user's ACLs and provide the same to system user in a restricted way.

lir

11-02-2019

Hi, I tested and confirmed that a "system user" cannot unlock a page unless he owns the lock, no matter the permissions I give to him.

He owns the lock only if he did the locking, in which case he can unlock.

We are using version 6.2

This is a problem!! can you please advise

Thanks

lir

11-02-2019

Thank you for the detailed reply!

A few things again about my case:

- the user who locks the page is the workflow initiator, NOT a system user

- my code needs to unlock the pages, no matter who did lock them.

- I'm not sure if in your example, you mapped a different user when unlocking under your "datapersist" subServiceName

- before asking the question in this forum, I made a quick attempt using the deprecated getAdministrativeResourceResolver(null); to get an admin session (who according to documentation should be able to unlock everything), but unlocking failed because page.canUnlock() returned false. Even under admin session!!?

I will try giving my system user just read/write permissions to that area of JCR, and will let you know how it goes.

Thanks again

lir

08-02-2019

1- an AEM user initiates my custom workflow on a page

2- my WorkflowProcess implementation code, locks the page under the initiator session. This means that the initiator owns the lock of that page.

3- on a further down workflow step, my code needs to unlock the page, but the AEM user completing the step is not the same user as the one who owns the lock.

Therefore I have to manually (not programmatically) already have created a system user with permissions to unlock ANY page, AND have my code unlock the page under his session.

Gaurav-Behl

MVP

08-02-2019

that's even a better approach!

I assume that you don't plan to create a system user on the fly and then assign permissions to it programmatically? Could you share your requirements/solution approach if you are plan to go down this path?

Gaurav-Behl

MVP

08-02-2019

The system user should be created as a read/write service user which means the user would have read/write access to that content path/ specific nodes.

For unlocking the pages, the service user would simply delete specific properties on that content node.

Properties for locking/unlocking:

cr:lockIsDeep

Boolean

true

jcr:lockOwner

String

<username>

jcr:mixinTypes

Name[]

mix:lockable

smacdonald2008

11-02-2019

Interesting - in my test - I used the same system used to lock and unlock. Never really thought about using 2 different system users.

Look at Gaurav's suggestion.

smacdonald2008

10-02-2019

I have worked with the Page Manager API to test this use case.

First Lock API works.

Code that uses a System User:

import org.apache.sling.settings.SlingSettingsService;

import org.osgi.service.component.annotations.Activate;

import org.osgi.service.component.annotations.Component;

import org.osgi.service.component.annotations.ConfigurationPolicy;

import org.osgi.service.component.annotations.Reference;

import org.osgi.service.metatype.annotations.Designate;

import java.util.HashMap;

import java.util.Map;

 

import javax.jcr.Node;

import javax.jcr.Session;

 

import org.apache.jackrabbit.commons.JcrUtils;

import org.apache.sling.api.resource.LoginException;

import org.apache.sling.api.resource.ResourceResolver;

import org.apache.sling.api.resource.ResourceResolverFactory;

 

import com.adobe.cq.sightly.WCMUsePojo;

import com.day.cq.commons.jcr.JcrConstants;

import com.day.cq.wcm.api.Page;

import com.day.cq.wcm.api.PageManager;

import org.apache.sling.api.resource.Resource;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

@Component

public class PageServiceImp implements PageService {

    

private final Logger logger = LoggerFactory.getLogger(getClass());

private String user = "";

    

    private Session session;

    

    //Inject a Sling ResourceResolverFactory

    @Reference

    private ResourceResolverFactory resolverFactory;

    

    

    public String MovePage()

    {

    String pagePath = "/content/MovePage64/fr";

    String newPagePath ="/content/MovePage64/en" ;

    String templatePath = "/apps/AEMPage/templates/page-home";

    String pageTitle = "AEM home page";

    Page newPage;

    PageManager pageManager;

    

    

    ResourceResolver resolver = null;

   

   

     String newPageName = "";  

     Map<String, Object> param = new HashMap<String, Object>();

     param.put(ResourceResolverFactory.SUBSERVICE, "datapersist");

        

      

       try {

                     

           //Invoke the adaptTo method to create a Session used to create a QueryManager

           resolver = resolverFactory.getServiceResourceResolver(param);

       

       

        Resource res = resolver.getResource(pagePath);   

       

        //Adapts the resource to another type - in this example to a     com.day.cq.wcm.api.page

         Page page = res.adaptTo(Page.class);

    

        

       //Lock this page

       page.lock(); 

      

      

       logger.info("The page is locked!");

         // session = resolver.adaptTo(Session.class);

                

       

        

        return ""

    } catch (Exception e) {

        // TODO Auto-generated catch block

        logger.info("&&&&& BIG ERROR" +e.getMessage());

    }

   

   

    

    return ""  ;

}

}

The result is the page is locked:

LockA.png

Second the UnLock API works.

Code that uses a System User:

import org.apache.sling.settings.SlingSettingsService;

import org.osgi.service.component.annotations.Activate;

import org.osgi.service.component.annotations.Component;

import org.osgi.service.component.annotations.ConfigurationPolicy;

import org.osgi.service.component.annotations.Reference;

import org.osgi.service.metatype.annotations.Designate;

import java.util.HashMap;

import java.util.Map;

 

import javax.jcr.Node;

import javax.jcr.Session;

 

import org.apache.jackrabbit.commons.JcrUtils;

import org.apache.sling.api.resource.LoginException;

import org.apache.sling.api.resource.ResourceResolver;

import org.apache.sling.api.resource.ResourceResolverFactory;

 

import com.adobe.cq.sightly.WCMUsePojo;

import com.day.cq.commons.jcr.JcrConstants;

import com.day.cq.wcm.api.Page;

import com.day.cq.wcm.api.PageManager;

import org.apache.sling.api.resource.Resource;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

@Component

public class PageServiceImp implements PageService {

    

private final Logger logger = LoggerFactory.getLogger(getClass());

private String user = "";

    

    private Session session;

    

    //Inject a Sling ResourceResolverFactory

    @Reference

    private ResourceResolverFactory resolverFactory;

    

    

    public String MovePage()

    {

    String pagePath = "/content/MovePage64/fr";

    String newPagePath ="/content/MovePage64/en" ;

    String templatePath = "/apps/AEMPage/templates/page-home";

    String pageTitle = "AEM home page";

    Page newPage;

    PageManager pageManager;

    

    

    ResourceResolver resolver = null;

   

   

     String newPageName = "";  

     Map<String, Object> param = new HashMap<String, Object>();

     param.put(ResourceResolverFactory.SUBSERVICE, "datapersist");

        

      

       try {

                     

           //Invoke the adaptTo method to create a Session used to create a QueryManager

           resolver = resolverFactory.getServiceResourceResolver(param);

       

       

        Resource res = resolver.getResource(pagePath);   

       

        //Adapts the resource to another type - in this example to a     com.day.cq.wcm.api.page

         Page page = res.adaptTo(Page.class);

    

        

       //Lock this page

       page.unlock(); 

      

      

       logger.info("The page is locked!");

         // session = resolver.adaptTo(Session.class);

                

       

        

        return ""

    } catch (Exception e) {

        // TODO Auto-generated catch block

        logger.info("&&&&& BIG ERROR" +e.getMessage());

    }

   

   

    

    return ""  ;

}

}

The result is the page is unlocked - notice the lock props are gone:

LockB.png

smacdonald2008

09-02-2019

"Can't I use the unlock() method of com.day.cq.wcm.api.Page ?"

You should be able to use this method while getting a session of a system user that has read/write privileges of this part of the AEM JCR.

Keep in mind - you cannot use a SYstem User to manually perform tasks. A system user is used in code.

I will test this use case.