package org.opennaas.core.security.acl; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.Dictionary; import java.util.Hashtable; import java.util.Properties; import java.util.UUID; import javax.persistence.EntityManager; import javax.persistence.EntityTransaction; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.opennaas.core.resources.Resource; import org.opennaas.core.resources.configurationadmin.ConfigurationAdminUtil; import org.opennaas.core.security.Activator; import org.opennaas.core.security.persistence.SecurityRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.PermissionEvaluator; import org.springframework.security.acls.domain.BasePermission; import org.springframework.security.acls.domain.ObjectIdentityImpl; import org.springframework.security.acls.domain.PrincipalSid; import org.springframework.security.acls.model.MutableAcl; import org.springframework.security.acls.model.MutableAclService; import org.springframework.security.acls.model.NotFoundException; import org.springframework.security.acls.model.ObjectIdentity; import org.springframework.security.acls.model.Permission; import org.springframework.security.core.context.SecurityContextHolder; /** * * @author Julio Carlos Barrera * */ public class ACLManager implements IACLManager { private static Log log = LogFactory.getLog(ACLManager.class); // OpenNaaS-Security SQL scripts to initialize Spring Security ACLs SQL schema private static String SQL_ACLS; // Properties containing users to add to DB private String usersPropertiesFile; private static final String usersPropertiesUsersPrefix = "users."; private static final String usersPropertiesUsersSize = "size"; private SecurityRepository securityRepository; @Autowired private MutableAclService aclService; @Autowired private PermissionEvaluator permissionEvaluator; public void init() throws IOException { initializeSpringSecurityAclDB(); initializeUsers(); initializeClasses(); registerOSGiService(); } private void initializeSpringSecurityAclDB() { log.debug("Reading OpenNaaS-Security SQL init scripts contents..."); SQL_ACLS = Activator.getBundleTextFileContents("/security_db_scripts/acls.sql"); log.debug("OpenNaaS-Security SQL init scripts contents read."); log.debug("Executing OpenNaaS-Security SQL init scripts..."); executeSqlQuery(SQL_ACLS); log.debug("OpenNaaS-Security SQL init scripts executed."); } private void initializeUsers() throws IOException, NumberFormatException { Properties usersProperties = getProperties(usersPropertiesFile); // getting users.size int usersSize = Integer.parseInt(usersProperties.getProperty(usersPropertiesUsersPrefix + usersPropertiesUsersSize)); for (int i = 0; i < usersSize; i++) { // adding users.{i} String user = usersProperties.getProperty(usersPropertiesUsersPrefix + i); if (user != null) { insertAclSid(i, true, new PrincipalSid(user)); } } } public Properties getProperties(final String propertyFile) throws IOException { final Properties properties = new Properties(); final URL resource = Activator.class.getClassLoader().getResource(propertyFile + ".properties"); if (null == resource) { throw new FileNotFoundException(propertyFile + " could not be found"); } final InputStream propertyStream = resource.openStream(); properties.load(propertyStream); return properties; } private void initializeClasses() { insertAclClass(0, Resource.class); } private void insertAclSid(long id, boolean isPrincipal, PrincipalSid sid) { String query = "insert into acl_sid (id, principal, sid) values ( " + id + ", " + (isPrincipal ? 1 : 0) + ", '" + sid .getPrincipal() + "');"; executeSqlQuery(query); } private void insertAclClass(long id, Class<?> clazz) { String query = "insert into acl_class (id, class) values ( " + id + ", '" + clazz.getName() + "');"; executeSqlQuery(query); } private void insertAcl(long id, PrincipalSid principalSid, Permission permission) { ObjectIdentity oi = new ObjectIdentityImpl(Resource.class, id); // Create or update the relevant ACL MutableAcl acl = null; try { acl = (MutableAcl) aclService.readAclById(oi); } catch (NotFoundException nfe) { acl = aclService.createAcl(oi); } // Now grant some permissions via an access control entry (ACE) acl.insertAce(acl.getEntries().size(), permission, principalSid, true); aclService.updateAcl(acl); } @Override public void secureResource(String resourceId, String user) { insertAcl(ResourceIdToSecureId(resourceId), new PrincipalSid(user), BasePermission.READ); } @Override public Boolean isResourceAccessible(String resourceId) { return permissionEvaluator.hasPermission(SecurityContextHolder.getContext().getAuthentication(), ResourceIdToSecureId(resourceId), Resource.class.getName(), BasePermission.READ); } private static long ResourceIdToSecureId(String resourceId) { // generate a long secure Id (64 bits) from Resource ID (128 bits)to use in ACL DB return UUID.fromString(resourceId).getMostSignificantBits(); } private void executeSqlQuery(String sqlQuery) { log.debug("Executing SQL query: [ " + sqlQuery + " ]"); EntityManager em = securityRepository.getEntityManager(); EntityTransaction et = em.getTransaction(); et.begin(); try { em.createNativeQuery(sqlQuery).executeUpdate(); em.flush(); et.commit(); log.debug("SQL query executed."); } catch (Exception e) { log.error("Error executing SQL query, rollbacking", e); et.rollback(); } } /** * It is necessary to register the OSGi service here, because OSGi ConfigurationAdmin service has access to "ws.rest.url", required to register * our REST API * * @throws IOException */ private void registerOSGiService() throws IOException { Dictionary<String, String> props = new Hashtable<String, String>(); ConfigurationAdminUtil configurationAdmin = new ConfigurationAdminUtil(Activator.getBundleContext()); String url = configurationAdmin.getProperty("org.opennaas", "ws.rest.url"); if (props != null) { props.put("service.exported.interfaces", "*"); props.put("service.exported.configs", "org.apache.cxf.rs"); props.put("service.exported.intents", "HTTP"); props.put("org.apache.cxf.rs.httpservice.context", url + "/aclmanager"); props.put("org.apache.cxf.rs.address", "/"); props.put("org.apache.cxf.httpservice.requirefilter", "true"); } log.info("Registering ws in url: " + props.get("org.apache.cxf.rs.httpservice.context")); Activator.getBundleContext().registerService(IACLManager.class.getName(), this, props); } public void setUsersPropertiesFile(String usersPropertiesFile) { this.usersPropertiesFile = usersPropertiesFile; } public void setSecurityRepository(SecurityRepository securityRepository) { this.securityRepository = securityRepository; } public void setAclService(MutableAclService aclService) { this.aclService = aclService; } public void setPermissionEvaluator(PermissionEvaluator permissionEvaluator) { this.permissionEvaluator = permissionEvaluator; } }