/*
* Copyright (c) 2006-2011 Nuxeo SA (http://nuxeo.com/) and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Florent Guillaume
*/
package org.eclipse.ecr.core.storage.sql.coremodel;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.ecr.core.api.DocumentException;
import org.eclipse.ecr.core.api.security.ACE;
import org.eclipse.ecr.core.api.security.ACL;
import org.eclipse.ecr.core.api.security.ACP;
import org.eclipse.ecr.core.api.security.Access;
import org.eclipse.ecr.core.api.security.SecurityConstants;
import org.eclipse.ecr.core.api.security.impl.ACLImpl;
import org.eclipse.ecr.core.api.security.impl.ACPImpl;
import org.eclipse.ecr.core.model.Document;
import org.eclipse.ecr.core.model.Property;
import org.eclipse.ecr.core.model.Session;
import org.eclipse.ecr.core.security.SecurityException;
import org.eclipse.ecr.core.security.SecurityManager;
import org.eclipse.ecr.core.storage.sql.ACLRow;
/**
* @author Florent Guillaume
*/
public class SQLSecurityManager implements SecurityManager {
/*
* ----- org.eclipse.ecr.core.security.SecurityManager -----
*/
@Override
public ACP getACP(Document doc) throws SecurityException {
try {
Property property = ((SQLDocument) doc).getACLProperty();
return aclRowsToACP((ACLRow[]) property.getValue());
} catch (DocumentException e) {
throw new SecurityException(e.getMessage(), e);
}
}
@Override
public void setACP(Document doc, ACP acp, boolean overwrite)
throws SecurityException {
if (!overwrite && acp == null) {
return;
}
try {
Property property = ((SQLDocument) doc).getACLProperty();
ACLRow[] aclrows;
if (overwrite) {
aclrows = acp == null ? null : acpToAclRows(acp);
} else {
aclrows = updateAclRows((ACLRow[]) property.getValue(), acp);
}
property.setValue(aclrows);
} catch (DocumentException e) {
throw new SecurityException(e.getMessage(), e);
}
}
@Override
public ACP getMergedACP(Document doc) throws SecurityException {
try {
Document base = doc.isVersion() ? doc.getSourceDocument() : doc;
if (base == null) {
return null;
}
ACP acp = getACP(base);
if (doc.getParent() == null) {
return acp;
}
ACL acl = getInheritedACLs(doc);
if (acp == null) {
if (acl == null) {
return null;
}
acp = new ACPImpl();
}
if (acl != null) {
acp.addACL(acl);
}
return acp;
} catch (DocumentException e) {
throw new SecurityException("Failed to get merged acp", e);
}
}
@Override
public boolean checkPermission(Document doc, String username,
String permission) throws SecurityException {
return getAccess(doc, username, permission).toBoolean();
}
@Override
public Access getAccess(Document doc, String username, String permission)
throws SecurityException {
ACP acp = getMergedACP(doc);
return acp == null ? Access.UNKNOWN : acp.getAccess(username,
permission);
}
@Override
public void invalidateCache(Session session) {
}
/*
* ----- internal methods -----
*/
// unit tested
protected static ACP aclRowsToACP(ACLRow[] acls) {
ACP acp = new ACPImpl();
ACL acl = null;
String name = null;
for (ACLRow aclrow : acls) {
if (!aclrow.name.equals(name)) {
if (acl != null) {
acp.addACL(acl);
}
name = aclrow.name;
acl = new ACLImpl(name);
}
// XXX should prefix user/group
String user = aclrow.user;
if (user == null) {
user = aclrow.group;
}
acl.add(new ACE(user, aclrow.permission, aclrow.grant));
}
if (acl != null) {
acp.addACL(acl);
}
return acp;
}
// unit tested
protected static ACLRow[] acpToAclRows(ACP acp) {
List<ACLRow> aclrows = new LinkedList<ACLRow>();
for (ACL acl : acp.getACLs()) {
String name = acl.getName();
if (name.equals(ACL.INHERITED_ACL)) {
continue;
}
for (ACE ace : acl.getACEs()) {
addACLRow(aclrows, name, ace);
}
}
ACLRow[] array = new ACLRow[aclrows.size()];
return aclrows.toArray(array);
}
// unit tested
protected static ACLRow[] updateAclRows(ACLRow[] aclrows, ACP acp) {
List<ACLRow> newaclrows = new LinkedList<ACLRow>();
Map<String, ACL> aclmap = new HashMap<String, ACL>();
for (ACL acl : acp.getACLs()) {
String name = acl.getName();
if (ACL.INHERITED_ACL.equals(name)) {
continue;
}
aclmap.put(name, acl);
}
List<ACE> aces = Collections.emptyList();
Set<String> aceKeys = null;
String name = null;
for (ACLRow aclrow : aclrows) {
// new acl?
if (!aclrow.name.equals(name)) {
// finish remaining aces
for (ACE ace : aces) {
addACLRow(newaclrows, name, ace);
}
// start next round
name = aclrow.name;
ACL acl = aclmap.remove(name);
aces = acl == null ? Collections.<ACE> emptyList()
: new LinkedList<ACE>(Arrays.asList(acl.getACEs()));
aceKeys = new HashSet<String>();
for (ACE ace : aces) {
aceKeys.add(getACEkey(ace));
}
}
if (!aceKeys.contains(getACLrowKey(aclrow))) {
// no match, keep the aclrow info instead of the ace
newaclrows.add(new ACLRow(newaclrows.size(), name,
aclrow.grant, aclrow.permission, aclrow.user,
aclrow.group));
}
}
// finish remaining aces for last acl done
for (ACE ace : aces) {
addACLRow(newaclrows, name, ace);
}
// do non-done acls
for (ACL acl : aclmap.values()) {
name = acl.getName();
for (ACE ace : acl.getACEs()) {
addACLRow(newaclrows, name, ace);
}
}
ACLRow[] array = new ACLRow[newaclrows.size()];
return newaclrows.toArray(array);
}
/** Key to distinguish ACEs */
protected static String getACEkey(ACE ace) {
// TODO separate user/group
return ace.getUsername() + '|' + ace.getPermission();
}
/** Key to distinguish ACLRows */
protected static String getACLrowKey(ACLRow aclrow) {
// TODO separate user/group
String user = aclrow.user;
if (user == null) {
user = aclrow.group;
}
return user + '|' + aclrow.permission;
}
protected static void addACLRow(List<ACLRow> aclrows, String name, ACE ace) {
// XXX should prefix user/group
String user = ace.getUsername();
if (user == null) {
// JCR implementation logs null and skips it
return;
}
String group = null; // XXX all in user for now
aclrows.add(new ACLRow(aclrows.size(), name, ace.isGranted(),
ace.getPermission(), user, group));
}
protected ACL getInheritedACLs(Document doc) throws DocumentException {
doc = doc.getParent();
ACL merged = null;
while (doc != null) {
ACP acp = getACP(doc);
if (acp != null) {
ACL acl = acp.getMergedACLs(ACL.INHERITED_ACL);
if (merged == null) {
merged = acl;
} else {
merged.addAll(acl);
}
if (acp.getAccess(SecurityConstants.EVERYONE,
SecurityConstants.EVERYTHING) == Access.DENY) {
break;
}
}
doc = doc.getParent();
}
return merged;
}
}