org.phenotips.data.permissions.internal.DefaultPatientAccessHelper.java Source code

Java tutorial

Introduction

Here is the source code for org.phenotips.data.permissions.internal.DefaultPatientAccessHelper.java

Source

/*
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see http://www.gnu.org/licenses/
 */
package org.phenotips.data.permissions.internal;

import org.phenotips.data.Patient;
import org.phenotips.data.permissions.AccessLevel;
import org.phenotips.data.permissions.Collaborator;
import org.phenotips.data.permissions.Owner;
import org.phenotips.data.permissions.PermissionsManager;
import org.phenotips.data.permissions.Visibility;
import org.xwiki.bridge.DocumentAccessBridge;
import org.xwiki.component.annotation.Component;
import org.xwiki.context.Execution;
import org.xwiki.model.EntityType;
import org.xwiki.model.reference.DocumentReference;
import org.xwiki.model.reference.DocumentReferenceResolver;
import org.xwiki.model.reference.EntityReference;
import org.xwiki.model.reference.EntityReferenceSerializer;
import org.xwiki.security.authorization.AuthorizationManager;
import org.xwiki.security.authorization.Right;

import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.TreeMap;

import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;

import com.xpn.xwiki.XWiki;
import com.xpn.xwiki.XWikiContext;
import com.xpn.xwiki.XWikiException;
import com.xpn.xwiki.doc.XWikiDocument;
import com.xpn.xwiki.objects.BaseObject;
import com.xpn.xwiki.user.api.XWikiGroupService;

/**
 * @version $Id: 1d24d0248fafa14eaab48d83e6214c7969032f62 $
 */
@Component
@Singleton
public class DefaultPatientAccessHelper implements PatientAccessHelper {
    private static final EntityReference USER_CLASS = new EntityReference("XWikiUsers", EntityType.DOCUMENT,
            new EntityReference(XWiki.SYSTEM_SPACE, EntityType.SPACE));

    private static final EntityReference GROUP_CLASS = new EntityReference("XWikiGroups", EntityType.DOCUMENT,
            new EntityReference(XWiki.SYSTEM_SPACE, EntityType.SPACE));

    @Inject
    private Logger logger;

    @Inject
    private DocumentAccessBridge bridge;

    @Inject
    private Execution execution;

    @Inject
    @Named("currentmixed")
    private DocumentReferenceResolver<EntityReference> partialEntityResolver;

    @Inject
    @Named("currentmixed")
    private DocumentReferenceResolver<String> stringEntityResolver;

    @Inject
    private EntityReferenceSerializer<String> entitySerializer;

    @Inject
    private PermissionsManager manager;

    @Inject
    private AuthorizationManager rights;

    @Override
    public DocumentReference getCurrentUser() {
        return this.bridge.getCurrentUserReference();
    }

    @Override
    public boolean isAdministrator(Patient patient) {
        if (patient == null || patient.getDocumentReference() == null) {
            return false;
        }
        return this.rights.hasAccess(Right.ADMIN, getCurrentUser(), patient.getDocumentReference());
    }

    @Override
    public boolean isAdministrator(Patient patient, DocumentReference user) {
        if (patient == null || patient.getDocumentReference() == null) {
            return false;
        }
        return this.rights.hasAccess(Right.ADMIN, user, patient.getDocumentReference());
    }

    @Override
    public Owner getOwner(Patient patient) {
        if (patient == null || patient.getDocumentReference() == null) {
            return null;
        }
        DocumentReference classReference = this.partialEntityResolver.resolve(Owner.CLASS_REFERENCE,
                patient.getDocumentReference());
        String owner = this.getStringProperty(patient.getXDocument(), classReference, "owner");
        if (StringUtils.isNotBlank(owner) && !"null".equals(owner)) {
            return new DefaultOwner(this.stringEntityResolver.resolve(owner, patient.getDocumentReference()), this);
        }
        return new DefaultOwner(null, this);
    }

    @Override
    public boolean setOwner(Patient patient, EntityReference userOrGroup) {
        DocumentReference classReference = this.partialEntityResolver.resolve(Owner.CLASS_REFERENCE,
                patient.getDocumentReference());
        try {
            EntityReference previousOwner = getOwner(patient).getUser();
            DocumentReference absoluteUserOrGroup = this.partialEntityResolver.resolve(userOrGroup);
            String owner = userOrGroup != null ? this.entitySerializer.serialize(absoluteUserOrGroup) : "";
            this.setProperty(patient.getXDocument(), classReference, "owner", owner);
            if (!previousOwner.equals(userOrGroup)) {
                addCollaborator(patient,
                        new DefaultCollaborator(previousOwner, this.manager.resolveAccessLevel("manage"), null),
                        false);
            }
            removeCollaborator(patient, new DefaultCollaborator(userOrGroup, null, null), false);

            XWikiContext context = getXWikiContext();
            context.getWiki().saveDocument(patient.getXDocument(), "Set owner: " + owner, true, context);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    @Override
    public Visibility getVisibility(Patient patient) {
        DocumentReference classReference = this.partialEntityResolver.resolve(Visibility.CLASS_REFERENCE,
                patient.getDocumentReference());
        String visibility = this.getStringProperty(patient.getXDocument(), classReference, "visibility");
        if (StringUtils.isNotBlank(visibility)) {
            return this.manager.resolveVisibility(visibility);
        }
        return null;
    }

    @Override
    public boolean setVisibility(Patient patient, Visibility visibility) {
        DocumentReference classReference = this.partialEntityResolver.resolve(Visibility.CLASS_REFERENCE,
                patient.getDocumentReference());
        try {
            String visibilityAsString = (visibility != null) ? visibility.getName() : "";
            String currentVisibility = this.getStringProperty(patient.getXDocument(), classReference, "visibility");
            if (!visibilityAsString.equals(currentVisibility)) {
                this.setProperty(patient.getXDocument(), classReference, "visibility", visibilityAsString);
                XWikiContext context = getXWikiContext();
                context.getWiki().saveDocument(patient.getXDocument(), "Set visibility: " + visibilityAsString,
                        true, context);
            }
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    @Override
    public AccessLevel getAccessLevel(Patient patient, EntityReference user) {
        AccessLevel result = this.manager.resolveAccessLevel("none");
        if (patient == null || user == null) {
            return result;
        }
        try {
            EntityReference owner = getOwner(patient).getUser();
            Collection<Collaborator> collaborators = getCollaborators(patient);
            Set<DocumentReference> processedEntities = new HashSet<DocumentReference>();
            Queue<DocumentReference> entitiesToCheck = new LinkedList<DocumentReference>();
            entitiesToCheck.add((DocumentReference) user);
            AccessLevel currentItemAccess = null;
            DocumentReference currentItem;
            XWikiContext context = getXWikiContext();
            XWikiGroupService groupService = context.getWiki().getGroupService(context);
            while (!entitiesToCheck.isEmpty()) {
                currentItem = entitiesToCheck.poll();
                currentItemAccess = getAccessLevel(currentItem, owner, collaborators);
                if (currentItemAccess.compareTo(result) > 0) {
                    result = currentItemAccess;
                }
                processedEntities.add(currentItem);
                Collection<DocumentReference> groups = groupService.getAllGroupsReferencesForMember(currentItem, 0,
                        0, context);
                groups.removeAll(processedEntities);
                entitiesToCheck.addAll(groups);
            }
        } catch (XWikiException ex) {
            this.logger.warn("Failed to compute access level for [{}] on [{}]: {}", user, patient.getId(),
                    ex.getMessage());
        }
        return result;
    }

    @Override
    public Collection<Collaborator> getCollaborators(Patient patient) {
        try {
            XWikiDocument patientDoc = patient.getXDocument();
            DocumentReference classReference = this.partialEntityResolver.resolve(Collaborator.CLASS_REFERENCE,
                    patient.getDocumentReference());
            Map<EntityReference, Collaborator> collaborators = new TreeMap<EntityReference, Collaborator>();
            for (BaseObject o : patientDoc.getXObjects(classReference)) {
                if (o == null) {
                    continue;
                }
                String collaboratorName = o.getStringValue("collaborator");
                String accessName = o.getStringValue("access");
                if (StringUtils.isBlank(collaboratorName) || StringUtils.isBlank(accessName)) {
                    continue;
                }
                EntityReference userOrGroup = this.stringEntityResolver.resolve(collaboratorName,
                        patient.getDocumentReference());
                AccessLevel access = this.manager.resolveAccessLevel(accessName);
                if (collaborators.containsKey(userOrGroup)) {
                    Collaborator oldCollaborator = collaborators.get(userOrGroup);
                    AccessLevel oldAccess = oldCollaborator.getAccessLevel();
                    if (access.compareTo(oldAccess) <= 0) {
                        continue;
                    }
                }
                Collaborator collaborator = new DefaultCollaborator(userOrGroup, access, this);
                collaborators.put(userOrGroup, collaborator);
            }
            return collaborators.values();
        } catch (Exception e) {
            // This should not happen;
        }
        return Collections.emptySet();
    }

    @Override
    public boolean setCollaborators(Patient patient, Collection<Collaborator> newCollaborators) {
        try {
            XWikiDocument patientDoc = patient.getXDocument();
            DocumentReference classReference = this.partialEntityResolver.resolve(Collaborator.CLASS_REFERENCE,
                    patient.getDocumentReference());
            XWikiContext context = getXWikiContext();
            patientDoc.removeXObjects(classReference);
            for (Collaborator collaborator : newCollaborators) {
                BaseObject o = patientDoc.newXObject(classReference, context);
                o.setStringValue("collaborator", this.entitySerializer.serialize(collaborator.getUser()));
                o.setStringValue("access", collaborator.getAccessLevel().getName());
            }
            context.getWiki().saveDocument(patientDoc, "Updated collaborators", true, context);
            return true;
        } catch (Exception e) {
            // This should not happen;
        }
        return false;
    }

    @Override
    public boolean addCollaborator(Patient patient, Collaborator collaborator) {
        return this.addCollaborator(patient, collaborator, true);
    }

    private boolean addCollaborator(Patient patient, Collaborator collaborator, boolean saveDocument) {
        try {
            XWikiDocument patientDoc = patient.getXDocument();
            DocumentReference classReference = this.partialEntityResolver.resolve(Collaborator.CLASS_REFERENCE,
                    patient.getDocumentReference());
            XWikiContext context = (XWikiContext) this.execution.getContext().getProperty("xwikicontext");

            DocumentReference absoluteUserOrGroup = this.partialEntityResolver.resolve(collaborator.getUser());
            String user = collaborator.getUser() != null ? this.entitySerializer.serialize(absoluteUserOrGroup)
                    : "";

            BaseObject o = patientDoc.getXObject(classReference, "collaborator", user, false);
            if (o == null) {
                o = patientDoc.newXObject(classReference, context);
            }

            o.setStringValue("collaborator", StringUtils.defaultString(user));
            o.setStringValue("access", collaborator.getAccessLevel().getName());

            if (saveDocument) {
                context.getWiki().saveDocument(patientDoc, "Added collaborator: " + user, true, context);
            }

            return true;
        } catch (Exception e) {
            // This should not happen;
        }
        return false;
    }

    @Override
    public boolean removeCollaborator(Patient patient, Collaborator collaborator) {
        return this.removeCollaborator(patient, collaborator, true);
    }

    private boolean removeCollaborator(Patient patient, Collaborator collaborator, boolean saveDocument) {
        try {
            XWikiDocument patientDoc = patient.getXDocument();
            DocumentReference classReference = this.partialEntityResolver.resolve(Collaborator.CLASS_REFERENCE,
                    patient.getDocumentReference());
            XWikiContext context = (XWikiContext) this.execution.getContext().getProperty("xwikicontext");
            DocumentReference absoluteUserOrGroup = this.partialEntityResolver.resolve(collaborator.getUser());
            String user = collaborator.getUser() != null ? this.entitySerializer.serialize(absoluteUserOrGroup)
                    : "";

            BaseObject o = patientDoc.getXObject(classReference, "collaborator", user, false);
            if (o != null) {
                patientDoc.removeXObject(o);
                context.getWiki().saveDocument(patientDoc, "Removed collaborator: " + user, true, context);
                return true;
            }
        } catch (Exception e) {
            // This should not happen;
        }
        return false;
    }

    @Override
    public String getType(EntityReference userOrGroup) {
        try {
            XWikiDocument doc = (XWikiDocument) this.bridge.getDocument((DocumentReference) userOrGroup);
            if (doc.getXObject(USER_CLASS) != null) {
                return "user";
            } else if (doc.getXObject(GROUP_CLASS) != null) {
                return "group";
            }
        } catch (Exception ex) {
        }
        return "unknown";
    }

    private AccessLevel getAccessLevel(EntityReference userOrGroup, EntityReference owner,
            Collection<Collaborator> collaborators) {
        if (userOrGroup.equals(owner)) {
            return this.manager.resolveAccessLevel("owner");
        }
        for (Collaborator collaborator : collaborators) {
            if (collaborator.getUser().equals(userOrGroup)) {
                return collaborator.getAccessLevel();
            }
        }
        return this.manager.resolveAccessLevel("none");
    }

    private XWikiContext getXWikiContext() {
        return (XWikiContext) this.execution.getContext().getProperty("xwikicontext");
    }

    private String getStringProperty(XWikiDocument doc, DocumentReference classReference, String propertyName) {
        try {
            BaseObject object = doc.getXObject(classReference);
            if (object != null) {
                String property = object.getStringValue(propertyName);
                if (!StringUtils.isEmpty(property)) {
                    return property;
                }
            }
        } catch (Exception ex) {
            this.logger.error("Failed to get object property", ex);
        }
        return null;
    }

    private void setProperty(XWikiDocument doc, DocumentReference classReference, String propertyName,
            Object propertyValue) throws Exception {
        XWikiContext xcontext = getXWikiContext();
        BaseObject obj = doc.getXObject(classReference, true, xcontext);
        if (obj != null) {
            obj.set(propertyName, propertyValue, xcontext);
        }
    }
}