/**
* Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.master.security.impl;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.shiro.authz.Permission;
import com.opengamma.core.change.ChangeManager;
import com.opengamma.id.ObjectIdentifiable;
import com.opengamma.id.UniqueId;
import com.opengamma.id.VersionCorrection;
import com.opengamma.master.security.ManageableSecurity;
import com.opengamma.master.security.SecurityDocument;
import com.opengamma.master.security.SecurityHistoryRequest;
import com.opengamma.master.security.SecurityHistoryResult;
import com.opengamma.master.security.SecurityMaster;
import com.opengamma.master.security.SecurityMetaDataRequest;
import com.opengamma.master.security.SecurityMetaDataResult;
import com.opengamma.master.security.SecuritySearchRequest;
import com.opengamma.master.security.SecuritySearchResult;
import com.opengamma.util.ArgumentChecker;
import com.opengamma.util.auth.AuthUtils;
import com.opengamma.util.auth.Permissionable;
/**
* A decorator for a security master that applies permissions.
* <p>
* Two kinds of permissions are applied by this class.
* <p>
* The first kind of permission is master-based.
* These are provided as static constants on this class and cover
* the basic view, add, update and remove operations.
* <p>
* The second kind of permission is entity-based.
* The {@link ManageableSecurity} class implements {@link Permissionable}.
* This provides each security with a set of permissions that a user needs
* to be able to view the data. This master enforces those permissions.
* <p>
* For the {@code search} and {@code history} methods, each restricted document
* is removed from the result. Since this happens after paging, it is possible
* to see pages of data that are smaller than the requested page size.
* <p>
* For the bulk {@code get} method, each restricted document is removed from the result.
* <p>
* For the {@code get} methods, a restricted document causes an exception to be thrown.
*/
public class PermissionedSecurityMaster implements SecurityMaster {
/**
* The permission object for viewing data.
*/
public static final Permission PERMISSION_VIEW = AuthUtils.getPermissionResolver().resolvePermission("SecurityMaster:view");
/**
* The permission object for adding data.
*/
public static final Permission PERMISSION_ADD = AuthUtils.getPermissionResolver().resolvePermission("SecurityMaster:edit:add");
/**
* The permission object for updating data.
*/
public static final Permission PERMISSION_UPDATE = AuthUtils.getPermissionResolver().resolvePermission("SecurityMaster:edit:update");
/**
* The permission object for removing data.
*/
public static final Permission PERMISSION_REMOVE = AuthUtils.getPermissionResolver().resolvePermission("SecurityMaster:edit:remove");
/**
* The permission object for correcting data.
*/
public static final Permission PERMISSION_CORRECT = AuthUtils.getPermissionResolver().resolvePermission("SecurityMaster:edit:correct");
/**
* The underlying security master.
*/
private final SecurityMaster _underlying;
//-------------------------------------------------------------------------
/**
* Wraps an underlying master if appropriate.
* <p>
* No wrapping occurs if permissions are not in use.
*
* @param underlying the underlying master, not null
* @return the master, not null
*/
public static SecurityMaster wrap(SecurityMaster underlying) {
if (AuthUtils.isPermissive()) {
return underlying;
}
return new PermissionedSecurityMaster(underlying);
}
//-------------------------------------------------------------------------
/**
* Creates an instance.
*
* @param underlying the underlying security master, not null
*/
public PermissionedSecurityMaster(SecurityMaster underlying) {
_underlying = ArgumentChecker.notNull(underlying, "underlying");
}
//-------------------------------------------------------------------------
/**
* Gets the underlying security master.
*
* @return the underlying master, not null
*/
protected SecurityMaster getUnderlying() {
return _underlying;
}
//-------------------------------------------------------------------------
@Override
public SecurityDocument get(UniqueId uniqueId) {
AuthUtils.getSubject().checkPermission(PERMISSION_VIEW);
SecurityDocument doc = getUnderlying().get(uniqueId);
AuthUtils.checkPermissions(doc.getValue());
return doc;
}
@Override
public SecurityDocument get(ObjectIdentifiable objectId, VersionCorrection versionCorrection) {
AuthUtils.getSubject().checkPermission(PERMISSION_VIEW);
SecurityDocument doc = getUnderlying().get(objectId, versionCorrection);
AuthUtils.checkPermissions(doc.getValue());
return doc;
}
@Override
public Map<UniqueId, SecurityDocument> get(Collection<UniqueId> uniqueIds) {
AuthUtils.getSubject().checkPermission(PERMISSION_VIEW);
Map<UniqueId, SecurityDocument> result = new HashMap<>(getUnderlying().get(uniqueIds));
for (Iterator<SecurityDocument> it = result.values().iterator(); it.hasNext(); ) {
SecurityDocument doc = (SecurityDocument) it.next();
if (AuthUtils.isPermitted(doc.getValue()) == false) {
it.remove();
}
}
return result;
}
@Override
public SecuritySearchResult search(SecuritySearchRequest request) {
AuthUtils.getSubject().checkPermission(PERMISSION_VIEW);
SecuritySearchResult result = getUnderlying().search(request);
int removed = 0;
for (Iterator<SecurityDocument> it = result.getDocuments().iterator(); it.hasNext(); ) {
SecurityDocument doc = (SecurityDocument) it.next();
if (AuthUtils.isPermitted(doc.getValue()) == false) {
it.remove();
removed++;
}
}
result.setUnauthorizedCount(removed);
return result;
}
@Override
public SecurityHistoryResult history(SecurityHistoryRequest request) {
AuthUtils.getSubject().checkPermission(PERMISSION_VIEW);
SecurityHistoryResult result = getUnderlying().history(request);
int removed = 0;
for (Iterator<SecurityDocument> it = result.getDocuments().iterator(); it.hasNext(); ) {
SecurityDocument doc = (SecurityDocument) it.next();
if (AuthUtils.isPermitted(doc.getValue()) == false) {
it.remove();
removed++;
}
}
result.setUnauthorizedCount(removed);
return result;
}
//-------------------------------------------------------------------------
@Override
public ChangeManager changeManager() {
return getUnderlying().changeManager();
}
@Override
public SecurityMetaDataResult metaData(SecurityMetaDataRequest request) {
AuthUtils.getSubject().checkPermission(PERMISSION_VIEW);
return getUnderlying().metaData(request);
}
@Override
public SecurityDocument add(SecurityDocument document) {
AuthUtils.getSubject().checkPermission(PERMISSION_ADD);
return getUnderlying().add(document);
}
@Override
public SecurityDocument update(SecurityDocument document) {
AuthUtils.getSubject().checkPermission(PERMISSION_UPDATE);
return getUnderlying().update(document);
}
@Override
public void remove(ObjectIdentifiable oid) {
AuthUtils.getSubject().checkPermission(PERMISSION_REMOVE);
getUnderlying().remove(oid);
}
@Override
public SecurityDocument correct(SecurityDocument document) {
AuthUtils.getSubject().checkPermission(PERMISSION_CORRECT);
return getUnderlying().correct(document);
}
@Override
public List<UniqueId> replaceVersion(UniqueId uniqueId, List<SecurityDocument> replacementDocuments) {
AuthUtils.getSubject().checkPermission(PERMISSION_CORRECT);
return getUnderlying().replaceVersion(uniqueId, replacementDocuments);
}
@Override
public List<UniqueId> replaceAllVersions(ObjectIdentifiable objectId, List<SecurityDocument> replacementDocuments) {
AuthUtils.getSubject().checkPermission(PERMISSION_CORRECT);
return getUnderlying().replaceAllVersions(objectId, replacementDocuments);
}
@Override
public List<UniqueId> replaceVersions(ObjectIdentifiable objectId, List<SecurityDocument> replacementDocuments) {
AuthUtils.getSubject().checkPermission(PERMISSION_CORRECT);
return getUnderlying().replaceVersions(objectId, replacementDocuments);
}
@Override
public UniqueId replaceVersion(SecurityDocument replacementDocument) {
AuthUtils.getSubject().checkPermission(PERMISSION_CORRECT);
return getUnderlying().replaceVersion(replacementDocument);
}
@Override
public void removeVersion(UniqueId uniqueId) {
AuthUtils.getSubject().checkPermission(PERMISSION_CORRECT);
getUnderlying().removeVersion(uniqueId);
}
@Override
public UniqueId addVersion(ObjectIdentifiable objectId, SecurityDocument documentToAdd) {
AuthUtils.getSubject().checkPermission(PERMISSION_CORRECT);
return getUnderlying().addVersion(objectId, documentToAdd);
}
//-------------------------------------------------------------------------
@Override
public String toString() {
return String.format("%s[%s]", getClass().getSimpleName(), getUnderlying());
}
}