/**
* TNTConcept Easy Enterprise Management by Autentia Real Bussiness Solution S.L.
* Copyright (C) 2007 Autentia Real Bussiness Solution S.L.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License.
*
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.autentia.tnt.manager.security.impl.fixed;
import java.util.HashMap;
import java.util.Map;
import org.acegisecurity.acls.Acl;
import org.acegisecurity.acls.AclService;
import org.acegisecurity.acls.NotFoundException;
import org.acegisecurity.acls.Permission;
import org.acegisecurity.acls.domain.AclImpl;
import org.acegisecurity.acls.domain.AuditLogger;
import org.acegisecurity.acls.domain.BasePermission;
import org.acegisecurity.acls.objectidentity.ObjectIdentity;
import org.acegisecurity.acls.sid.PrincipalSid;
import org.acegisecurity.acls.sid.Sid;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.autentia.tnt.dao.IDataAccessObject;
import com.autentia.tnt.dao.ITransferObject;
import com.autentia.tnt.manager.security.Principal;
import com.autentia.tnt.util.SpringUtils;
/**
* This is the default implementation for AclService. This class creates ACLs
* for different entity objects based on the object type, object owner,
* current logged in user role, and the permissions hashed matrices.
* @author Ivan Zaera Avellon
*/
public class DefaultAclService implements AclService
{
private static final Log log = LogFactory.getLog( DefaultAclService.class );
/** Default audit logger object */
private static final AuditLogger DEFAULT_AUDIT_LOGGER = new DefaultAuditLogger();
private boolean ignoreUnownedObjects = false;
private ISecurityConfiguration secCfg;
private Map<String,IDataAccessObject> daoMap;
public DefaultAclService( ISecurityConfiguration secCfg, Map<String,IDataAccessObject> daoMap )
{
this.secCfg = secCfg;
this.daoMap = daoMap;
}
public boolean isIgnoreUnownedObjects()
{
return ignoreUnownedObjects;
}
public void setIgnoreUnownedObjects(boolean ignoreUnownedObjects)
{
this.ignoreUnownedObjects = ignoreUnownedObjects;
}
public Acl readAclById( ObjectIdentity id ) throws NotFoundException
{
DefaultAclAuthorizationStrategy strategy = new DefaultAclAuthorizationStrategy();
AclImpl acl = new AclImpl(id,id.getIdentifier(),strategy,DEFAULT_AUDIT_LOGGER);
// Get data about saved object from database and then remove it from Hibernate cache if necessary
// boolean isObjectLoaded = HibernateUtil.currentSession().contains(
// ((TransferObjectIdentity)id).getTransferObject()
// );
ITransferObject dto = loadObject(id);
dto.getOwnerId();
// this is to force fetch of entire object from database so that further getter calls don't fail
// if( !isObjectLoaded )
// {
// HibernateUtil.evictFullObject(dto);
// }
// Add object levels to ACL
addAclLevel( secCfg.getWriteMatrix(), acl, id.getJavaType(), dto, BasePermission.WRITE );
addAclLevel( secCfg.getDeleteMatrix(), acl, id.getJavaType(), dto, BasePermission.DELETE );
addAclLevel( secCfg.getReadMatrix(), acl, id.getJavaType(), dto, BasePermission.READ );
// Lock ACL so that nobody can change it any more
strategy.freeze();
return acl;
}
private void addAclLevel( Map<AclMatrixKey,AclMatrixValue> matrix, AclImpl acl, Class type, ITransferObject dto, Permission perm )
{
Principal principal = SpringUtils.getPrincipal();
Sid sid = new PrincipalSid(principal.getUsername());
AclMatrixKey key = new AclMatrixKey(type,principal.getRoleId());
AclMatrixValue level = matrix.get( key );
if (log.isDebugEnabled()){
log.debug("addAclLevel -"+
" permission=["+perm.getPattern()+"]"+
" type="+type.getSimpleName()+
" id="+dto.getId()+
" ownerId="+dto.getOwnerId()+
" departmentId="+dto.getDepartmentId()+
" userId="+principal.getId()+
" roleId="+principal.getRoleId()+
" level="+level );
}
if( level==null )
{
throw new UnsupportedOperationException("Write permission level for "+key+" not defined");
}
switch( level )
{
case ALL:
acl.insertAce(null,perm,sid,true);
break;
case OWN:
if( isIgnoreUnownedObjects() && (dto.getOwnerId()==null) )
{
acl.insertAce(null,perm,sid,true);
log.warn("addAclLevel - allowing permission ["+perm.getPattern()+"] on object "+
type.getSimpleName()+"["+dto.getId()+"] "+
"because it is not owned by any user and ignoreUnknownedObjects=true in DefaultAclService");
}
else
{
if( dto.getOwnerId() == principal.getId() )
{
acl.insertAce(null,perm,sid,true);
}
}
break;
case AREA:
if( isIgnoreUnownedObjects() && (dto.getDepartmentId()==null) )
{
acl.insertAce(null,perm,sid,true);
log.warn("addAclLevel - allowing permission ["+perm.getPattern()+"] on object "+
type.getSimpleName()+"["+dto.getId()+"] "+
"because it is not owned by any department and ignoreUnknownedObjects=true in DefaultAclService");
}
else
{
if( dto.getDepartmentId() == principal.getDepartmentId() )
{
acl.insertAce(null,perm,sid,true);
}
}
break;
case DENY:
// Do nothing
break;
case OWNERS:
if (dto.getOwnersId() != null && dto.getOwnersId().contains(principal.getId())) {
acl.insertAce(null, perm, sid, true);
}
break;
default:
throw new UnsupportedOperationException("AclMatrixValue("+level+") not supported by write permission in readAclById()");
}
}
public Acl readAclById( ObjectIdentity id, Sid[] sids ) throws NotFoundException
{
return readAclById(id);
}
public Map readAclsById( ObjectIdentity[] ids ) throws NotFoundException
{
Map ret = new HashMap();
for( ObjectIdentity id : ids )
{
ret.put(id,readAclById(id));
}
return ret;
}
public Map readAclsById( ObjectIdentity[] ids, Sid[] sids ) throws NotFoundException
{
return readAclsById(ids);
}
public ObjectIdentity[] findChildren( ObjectIdentity parentIdentity )
{
// Our hierachy of objects is flat, so don't return any parent
return null;
}
/**
* Generically load a DTO from the database given its ObjectIdentity
* @param objid object id
* @return the DTO as read from Hibernate
*/
private ITransferObject loadObject(ObjectIdentity objid)
{
Class type = objid.getJavaType();
Integer id = (Integer)objid.getIdentifier();
IDataAccessObject dao = daoMap.get(type.getName());
if( dao==null )
{
throw new IllegalStateException("No DAO for "+objid.getJavaType().getName()+" objects defined. "+
"Please fix your ACEGI configuration file.");
}
return dao.getById(id);
}
}