/**
* Licensed to The Apereo Foundation under one or more contributor license
* agreements. See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*
*
* The Apereo Foundation licenses this file to you under the Educational
* Community License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of the License
* at:
*
* http://opensource.org/licenses/ecl2.txt
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*
*/
package org.opencastproject.authorization.xacml.manager.impl;
import org.opencastproject.authorization.xacml.XACMLUtils;
import org.opencastproject.authorization.xacml.manager.api.AclService;
import org.opencastproject.authorization.xacml.manager.api.AclServiceException;
import org.opencastproject.authorization.xacml.manager.api.AclServiceFactory;
import org.opencastproject.authorization.xacml.manager.api.ManagedAcl;
import org.opencastproject.security.api.AccessControlList;
import org.opencastproject.security.api.Organization;
import org.opencastproject.security.api.OrganizationDirectoryService;
import org.opencastproject.security.api.SecurityService;
import org.opencastproject.util.NotFoundException;
import org.opencastproject.util.data.Option;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.felix.fileinstall.ArtifactInstaller;
import org.osgi.framework.BundleContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.xml.bind.JAXBException;
public class AclScanner implements ArtifactInstaller {
/** The directory name that has the XACML file defining the Acl **/
public static final String ACL_DIRECTORY = "acl";
/** The logging instance */
private static final Logger logger = LoggerFactory.getLogger(AclScanner.class);
/** The organization directory service */
private OrganizationDirectoryService organizationDirectoryService;
/** The Access Control service */
private AclServiceFactory aclServiceFactory;
private SecurityService securityService;
/**
* A map linking the Acl file name concatenate with the organization id {@code filename_organizationId} to the related
* managed Acl Id
*/
private Map<String, Long> managedAcls = new HashMap<String, Long>();
/**
* OSGI service activation method
*/
void activate(BundleContext ctx) {
logger.info("Activated Acl scanner");
}
/**
* OSGI deactivation method
*/
void deactivate(BundleContext ctx) {
logger.info("Deactivated Acl scanner");
}
/** OSGi DI. */
void setOrganizationDirectoryService(OrganizationDirectoryService organizationDirectoryService) {
this.organizationDirectoryService = organizationDirectoryService;
}
/** OSGi callback for setting persistence. */
void setAclServiceFactory(AclServiceFactory aclServiceFactory) {
this.aclServiceFactory = aclServiceFactory;
}
/** OSGi DI */
void setSecurityService(SecurityService securityService) {
this.securityService = securityService;
}
/**
* {@inheritDoc}
*
* @see org.apache.felix.fileinstall.ArtifactListener#canHandle(java.io.File)
*/
@Override
public boolean canHandle(File artifact) {
return ACL_DIRECTORY.equals(artifact.getParentFile().getName()) && artifact.getName().endsWith(".xml");
}
/**
* Add an ACL based upon an XACML file to all the organizations.
*
* @param artifact
* The File representing the XACML File.
* @throws IOException
* @throws JAXBException
*/
private void addAcl(File artifact) throws IOException, JAXBException {
List<Organization> organizations = organizationDirectoryService.getOrganizations();
logger.debug("Adding Acl {}", artifact.getAbsolutePath());
String fileName = FilenameUtils.removeExtension(artifact.getName());
AccessControlList acl = parseToAcl(artifact);
Option<ManagedAcl> managedAcl = Option.<ManagedAcl> none();
// Add the Acl to all the organizations
for (Organization org : organizations) {
securityService.setOrganization(org);
// If there are already (not-default) Acl defined for this organization, we skip this one.
boolean skip = false;
for (ManagedAcl a : getAclService(org).getAcls()) {
if (managedAcls.get(generateAclId(a.getName(), org)) == null) {
logger.debug(
"The Acl {} will be not added to the organisation {} as it already contains other not-default Acls.",
fileName, org.getName());
skip = true;
continue;
}
}
if (!skip) {
managedAcl = getAclService(org).createAcl(acl, fileName);
if (managedAcl.isSome()) {
managedAcls.put(generateAclId(fileName, org), managedAcl.get().getId());
logger.debug("Acl from '{}' has been added for the organisation {}", fileName, org.getName());
} else {
logger.debug("Acl from '{}' has already been added to the organisation {}.", fileName, org.getName());
}
}
}
}
/**
* Update an ACL based upon an XACML file on all the organizations.
*
* @param artifact
* The File representing the XACML File.
* @throws IOException
* @throws JAXBException
*/
private void updateAcl(File artifact) throws IOException, JAXBException {
List<Organization> organizations = organizationDirectoryService.getOrganizations();
logger.debug("Updating Acl {}", artifact.getAbsolutePath());
String fileName = FilenameUtils.removeExtension(artifact.getName());
AccessControlList acl = parseToAcl(artifact);
// Update the Acl on all the organizations
for (Organization org : organizations) {
securityService.setOrganization(org);
Long id = managedAcls.get(generateAclId(fileName, org));
if (id != null) {
// If the Acl Id is in the managedAcls map, we update the Acl
if (!getAclService(org).updateAcl(new ManagedAclImpl(id, fileName, org.getId(), acl))) {
logger.warn("No Acl found with the id {} for the organisation {}.", id, org.getName());
} else {
logger.debug("Acl from XACML file {} has been updated for the organisation {}", fileName, org.getName());
}
} else {
logger.info("The XACML file {} has not been added to the organisation {} and will therefore not be updated",
fileName, org.getName());
}
}
}
/**
* Remove an ACL based upon an XACML file from all the organizations.
*
* @param artifact
* The File representing the XACML File.
* @throws IOException
* @throws JAXBException
*/
private void removeAcl(File artifact) throws IOException, JAXBException {
List<Organization> organizations = organizationDirectoryService.getOrganizations();
logger.debug("Removing Acl {}", artifact.getAbsolutePath());
String fileName = FilenameUtils.removeExtension(artifact.getName());
// Remove the Acl on all the organizations
for (Organization org : organizations) {
securityService.setOrganization(org);
Long id = managedAcls.get(generateAclId(fileName, org));
if (id != null) {
try {
getAclService(org).deleteAcl(id);
} catch (NotFoundException e) {
logger.warn("Unable to delete managec acl {}: Managed acl already deleted!", id);
} catch (AclServiceException e) {
logger.error("Unable to delete managed acl {}: {}", id, ExceptionUtils.getStackTrace(e));
}
} else {
logger.debug("No Acl found with the id {}.", id);
}
}
}
/**
* Generate an Acl Id with the XACML filename and the organization id
*
* @param fileName
* the XACML file
* @param org
* the organization
* @return the generated Acl Id
*/
private String generateAclId(String fileName, Organization org) {
return fileName + "_" + org.getId();
}
/**
* Parse the given XACML file into an Access Control List
*
* @param artifact
* @return
* @throws FileNotFoundException
* @throws JAXBException
*/
private AccessControlList parseToAcl(File artifact) throws FileNotFoundException, JAXBException {
FileInputStream in = null;
AccessControlList acl = null;
try {
in = new FileInputStream(artifact);
acl = XACMLUtils.parseXacml(in);
} finally {
IOUtils.closeQuietly(in);
}
return acl;
}
private AclService getAclService(Organization organization) {
return aclServiceFactory.serviceFor(organization);
}
@Override
public void install(File artifact) throws Exception {
logger.info("Installing Acl {}", artifact.getAbsolutePath());
addAcl(artifact);
}
@Override
public void update(File artifact) throws Exception {
logger.info("Updating Acl {}", artifact.getAbsolutePath());
updateAcl(artifact);
}
@Override
public void uninstall(File artifact) throws Exception {
logger.info("Removing Acl {}", artifact.getAbsolutePath());
removeAcl(artifact);
}
}