Dynamic groups. Advances. Part IV

Last week I was trying to solve the problem related to ugly security model in Documentum: business user must able to see all documents related to workflow task which is assigned to him. Actually, it’s a true “case management” problem: someone sent me a task, that task has related documents, those documents have another relations and so on – to be able to perform the task I should able to see all related documents. Unfortunately, Documentum does not have any OOTB instruments/functions to resole such problem, for example, take a look at Webtop – it has some predefined distribution workflows (dmSendTo*), but to take advantage of these workflows supervisor must explicitly grant access permissions to performers on related documents, yes, when designing workflows with BPM we can add special automatic activities which grant required permissions to manual performers, but what to do with delegations (both manual and automatic) and chains of related documents?

My WDK solutions was:

PrivilegedFormProcessor:

public class PrivilegedFormProcessor extends FormProcessor {

    public PrivilegedFormProcessor() {
        super();
    }

    @Override
    public void processAction(PageContext pageContext, String formClass,
            String nlsClass) {
        boolean notifyAtEnd = notifyStart(pageContext);
        try {
            super.processAction(pageContext, formClass, formClass);
        } finally {
            if (notifyAtEnd) {
                notifyFinish(pageContext);
            }
        }
    }

}

PrivilegedRequestListener:

@SuppressWarnings("deprecation")
public class PrivilegedRequestListener implements IFormRenderListener,
        com.documentum.web.form.IRequestListener, IApplicationListener,
        com.documentum.web.env.IRequestListener {

    private static final int RECURSION_MAX_DEPTH = 2;

    private static final ThreadLocal<DfRoleSpec> PRIVILEGED_GROUP = new ThreadLocal<DfRoleSpec>();

    public PrivilegedRequestListener() {
        super();
    }

    @Override
    public void notifyRequestStart(HttpServletRequest httpServletRequest,
            HttpServletResponse httpServletResponse) {
        startPrivilegedRequest(httpServletRequest, null);
    }

    @Override
    public void notifyRequestFinish(HttpServletRequest httpServletRequest,
            HttpServletResponse httpServletResponse) {
        stopPrivilegedRequest(httpServletRequest);
    }

    @Override
    public void notifyFormRenderStart(Form form) {
        startPrivilegedRequest(form.getPageContext().getRequest(),
                form.getPageContext());
    }

    @Override
    public void notifyFormRenderFinish(Form form) {
        stopPrivilegedRequest(form.getPageContext().getRequest());
    }

    @Override
    @SuppressWarnings("deprecation")
    public void notifyStart(PageContext context) {
        startPrivilegedRequest(context.getRequest(), context);
    }

    @Override
    @SuppressWarnings("deprecation")
    public void notifyFinish(PageContext context) {
        stopPrivilegedRequest(context.getRequest());
    }

    @Override
    @SuppressWarnings("deprecation")
    public void notifyApplicationStart(ServletContext servletContext) {
        PrivilegedFormProcessor.addRequestListener(this);
    }

    @Override
    public void notifyApplicationFinish(ServletContext servletContext) {

    }

    public static void startPrivilegedRequest(ServletRequest request,
            PageContext context) {
        if (isPrivilegedRequest()) {
            return;
        }
        Form form = getForm(request, context);
        if (form == null) {
            return;
        }
        IPrivilegedComponent component = ControlUtils.findEnclosingControl(
                IPrivilegedComponent.class, form);
        if (component == null) {
            Control handler = getHandler(context, form);
            if (handler != null) {
                component = ControlUtils.findEnclosingControl(
                        IPrivilegedComponent.class, handler);
            }
        }
        if (component == null) {
            component = getPrivilegedCallerForm(form, RECURSION_MAX_DEPTH);
        }
        if (component == null) {
            return;
        }
        String privilegedGroup = component.getPrivilegedGroupName();
        if (StringUtils.isBlank(privilegedGroup)) {
            return;
        }
        String docbaseName = SessionManagerHttpBinding.getCurrentDocbase();
        if (StringUtils.isBlank(docbaseName)) {
            return;
        }
        RoleRequestManager requestManager = RoleRequestManager.getInstance();
        DfRoleSpec roleSpec = new DfRoleSpec(privilegedGroup, docbaseName);
        requestManager.push(roleSpec);
        PRIVILEGED_GROUP.set(roleSpec);
    }

    public static void stopPrivilegedRequest(ServletRequest request) {
        if (!isPrivilegedRequest()) {
            return;
        }

        RoleRequestManager requestManager = RoleRequestManager.getInstance();
        requestManager.pop(PRIVILEGED_GROUP.get());
        PRIVILEGED_GROUP.remove();
    }

    private static Form getForm(ServletRequest request, PageContext context) {
        Form form = (Form) request.getAttribute(IParams.FORM);
        if (form != null) {
            return form;
        }
        if (context == null) {
            return null;
        }
        FormRequest formRequest = (FormRequest) context.getAttribute(
                IParams.FORM_REQUEST, PageContext.REQUEST_SCOPE);
        if (formRequest != null) {
            return formRequest.getForm();
        }
        FormHistory history = (FormHistory) context.getAttribute(
                IParams.FORM_HISTORY, PageContext.REQUEST_SCOPE);
        if (history != null) {
            return history.getCurrentSnapshot().getForm();
        }

        Map historyMap = getFormHistoryMap(context);
        if (historyMap == null) {
            return null;
        }
        String requestId = request.getParameter(IParams.REQUEST_ID);
        if (StringUtils.isBlank(requestId)) {
            return null;
        }
        history = (FormHistory) historyMap.get(new FormRequestId(requestId)
                .getClientId());
        if (history == null) {
            return null;
        }
        return history.getCurrentSnapshot().getForm();
    }

    private static Map getFormHistoryMap(PageContext pageContext) {
        synchronized (pageContext.getSession()) {
            FormProcessor.HistoryByClientIdMap mapHistory = (FormProcessor.HistoryByClientIdMap) pageContext
                    .getAttribute("__dmfFormHistoryByClientId",
                            PageContext.SESSION_SCOPE);
            if (mapHistory != null) {
                return mapHistory.getHistoryByClientIdMap();
            }
            return null;
        }
    }

    private static Control getHandler(PageContext context, Form form) {
        String formId = Util.getRequestParameter(context.getRequest(),
                IParams.FORM_ID);
        String handlerParam = Util.getRequestParameter(context.getRequest(),
                IParams.HANDLER);
        String handlerName = null;
        if ((formId != null) && (handlerParam != null)
                && (handlerParam.startsWith(formId))) {
            handlerName = handlerParam.substring(formId.length() + 1);
        } else if (formId == null) {
            handlerName = handlerParam;
        }
        Control handler = form;
        if (handlerName == null) {
            return null;
        }
        if (!handler.getElementName().equals(handlerName)) {
            handler = form.getControlByElement(handlerName);
        }
        return handler;
    }

    private static IPrivilegedComponent getPrivilegedCallerForm(Form form,
            int depth) {
        Form callerForm = form.getCallerForm();
        if (callerForm == null) {
            return null;
        }
        if (callerForm instanceof IPrivilegedComponent) {
            return (IPrivilegedComponent) callerForm;
        }
        if (callerForm instanceof Container) {
            Container container = (Container) callerForm;
            String includedComponentName = container
                    .getContainedComponentName();
            if (StringUtils.isNotBlank(includedComponentName)) {
                Component component = ControlUtils.findInnerControl(
                        Component.class, container, includedComponentName);
                if (component instanceof IPrivilegedComponent) {
                    return (IPrivilegedComponent) component;
                }
            }
        }
        if (depth == 0) {
            return null;
        }
        return getPrivilegedCallerForm(callerForm, --depth);
    }

    public static boolean isPrivilegedRequest() {
        return PRIVILEGED_GROUP.get() != null;
    }

    public static String getPrivilegedGroup() {
        DfRoleSpec roleSpec = PRIVILEGED_GROUP.get();
        if (roleSpec == null) {
            return null;
        }
        return roleSpec.getRoleName();
    }

}

ControlUtils:

public class ControlUtils {

    private ControlUtils() {
        super();
    }

    public static <T> T findEnclosingControl(Class<T> cls, Control control) {
        FindEnclosingControl<T> visitor = new FindEnclosingControl<T>(cls);
        control.visitContainer(visitor);
        return visitor.getControl();
    }

    @SuppressWarnings("unchecked")
    static class FindEnclosingControl<E> implements IVisitor {
        private E _control;

        private Class<E> _controlClass;

        FindEnclosingControl(Class<E> cls) {
            _controlClass = cls;
        }

        public boolean visit(Control control) {
            boolean keepLooking = true;
            if (_controlClass.isInstance(control)) {
                _control = (E) control;
                keepLooking = false;
            }
            return keepLooking;
        }

        public E getControl() {
            return _control;
        }
    }

}

IPrivilegedComponent:

public interface IPrivilegedComponent {

    String getPrivilegedGroupName();

}

app.xml:

<listeners>
    <application-listeners>

        ...

        <listener>
            <class>com.documentum.web.form.PrivilegedRequestListener</class>
        </listener>
    </application-listeners>
    <request-listeners>

        ...

        <listener>
            <class>com.documentum.web.form.PrivilegedRequestListener</class>
        </listener>
    </request-listeners>
    <formrender-listeners>
        
        ...

        <listener>
            <class>com.documentum.web.form.PrivilegedRequestListener</class>
        </listener>
    </formrender-listeners>
</listeners>

WEB-INF/classes/com/documentum/web/form/FormProcessorProp.properties:

formProcessorClass=com.documentum.web.form.PrivilegedFormProcessor

and now, to solve my problem, I should just write something like:

public class TaskMgrContainerCustom extends TaskMgrContainer implements
        IPrivilegedComponent {

    public TaskMgrContainerCustom() {
        super();
    }

    @Override
    public String getPrivilegedGroupName() {
        return "dm_read_all_dynamic";
    }

}

3 thoughts on “Dynamic groups. Advances. Part IV

  1. Pingback: Q&A. II | Documentum in a (nuts)HELL
  2. Pingback: Power Pivot | Documentum in a (nuts)HELL
  3. Pingback: ACL performance | Documentum in a (nuts)HELL

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s