/*
* The contents of this file are subject to the terms of the Common Development and
* Distribution License (the License). You may not use this file except in compliance with the
* License.
*
* You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
* specific language governing permission and limitations under the License.
*
* When distributing Covered Software, include this CDDL Header Notice in each file and include
* the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
* Header, with the fields enclosed by brackets [] replaced by your own identifying
* information: "Portions copyright [year] [name of copyright owner]".
*
* Copyright 2014-2016 ForgeRock AS.
*/
package com.sun.identity.entitlement.xacml3;
import static org.forgerock.openam.xacml.v3.XACMLApplicationUtils.ApplicationTypeService;
import java.io.InputStream;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.inject.Inject;
import javax.inject.Named;
import javax.security.auth.Subject;
import org.forgerock.openam.entitlement.service.ApplicationService;
import org.forgerock.openam.entitlement.service.ApplicationServiceFactory;
import org.forgerock.openam.entitlement.service.ResourceTypeService;
import org.forgerock.openam.xacml.v3.ImportStep;
import org.forgerock.openam.xacml.v3.PersistableImportStep;
import org.forgerock.openam.xacml.v3.ImportStepGenerator;
import com.sun.identity.entitlement.EntitlementException;
import com.sun.identity.entitlement.Privilege;
import com.sun.identity.entitlement.PrivilegeManager;
import com.sun.identity.entitlement.util.SearchFilter;
import com.sun.identity.entitlement.xacml3.core.PolicySet;
import com.sun.identity.entitlement.xacml3.validation.PrivilegeValidator;
import com.sun.identity.shared.debug.Debug;
/**
* Utility for handling the Export and subsequent Import of Policies into
* the OpenAM Entitlements framework.
*
* @since 12.0.0
*/
public class XACMLExportImport {
public static final String PREFIX = XACMLExportImport.class.getSimpleName();
// Injected
private final XACMLReaderWriter xacmlReaderWriter;
private final SearchFilterFactory searchFilterFactory;
private final Debug debug;
private final PrivilegeValidator privilegeValidator;
private final PrivilegeManagerFactory privilegeManagerFactory;
private final ApplicationServiceFactory applicationServiceFactory;
private final ResourceTypeService resourceTypeService;
/**
* Creates an instance of the XACMLExportImport with dependencies provided.
*
* @param privilegeManagerFactory Non null, required to create PrivilegeManager instances.
* @param xacmlReaderWriter Non null, required for translating privileges to/from XACML XML.
* @param privilegeValidator Non null, required for validation of imported privileges.
* @param searchFilterFactory Non null, required for SearchFilter operations.
* @param debug Non null.
* @param applicationServiceFactory Application service factory responsible for creating the application service.
* @param resourceTypeService Resource type service responsible for creating resource types.
*/
@Inject
public XACMLExportImport(PrivilegeManagerFactory privilegeManagerFactory,
XACMLReaderWriter xacmlReaderWriter,
PrivilegeValidator privilegeValidator,
SearchFilterFactory searchFilterFactory,
@Named(XACMLConstants.DEBUG) Debug debug,
ApplicationServiceFactory applicationServiceFactory,
ResourceTypeService resourceTypeService) {
this.privilegeManagerFactory = privilegeManagerFactory;
this.xacmlReaderWriter = xacmlReaderWriter;
this.searchFilterFactory = searchFilterFactory;
this.privilegeValidator = privilegeValidator;
this.debug = debug;
this.applicationServiceFactory = applicationServiceFactory;
this.resourceTypeService = resourceTypeService;
}
/**
* Performs the Import based on the given Stream. The stream must contain XML in XACML.
*
* @param realm Non null Realm to populate with the Policies.
* @param xacml Non null stream to read.
* @param admin Non null admin Subject.
* @param dryRun boolean flag, indicating import steps should be reported but not applied.
* @return The sequence steps that could or have been used to carry out the import.
* @throws EntitlementException If there was any unexpected error.
*/
public List<ImportStep> importXacml(String realm, InputStream xacml, Subject admin, boolean dryRun)
throws EntitlementException {
PrivilegeSet privilegeSet = xacmlToPrivilegeSet(xacml);
List<PersistableImportStep> importSteps = generateImportSteps(realm, privilegeSet, admin);
applyIfRequired(dryRun, importSteps);
return new ArrayList<ImportStep>(importSteps);
}
private PrivilegeSet xacmlToPrivilegeSet(InputStream xacml) throws EntitlementException {
return xacmlReaderWriter.read(xacml);
}
/**
* Establishes the sequence of ImportSteps required to import the provided privileges into the specified realm.
*
* @param realm Non null Realm to populate with the Policies.
* @param privilegeSet Non null, collection of Privileges, ReferralPrivileges etc. to import.
* @param admin Non null admin Subject.
* @return The sequence steps that can be used to carry out the import.
* @throws EntitlementException If there was any unexpected error.
*/
private List<PersistableImportStep> generateImportSteps(String realm, PrivilegeSet privilegeSet, Subject admin)
throws EntitlementException {
ApplicationService applicationService = applicationServiceFactory.create(admin, realm);
PrivilegeManager privilegeManager = privilegeManagerFactory.createReferralPrivilegeManager(realm, admin);
ApplicationTypeService applicationTypeService = new ApplicationTypeService();
ImportStepGenerator importStepGenerator = new ImportStepGenerator(applicationService,
resourceTypeService, privilegeManager, privilegeValidator, applicationTypeService,
realm, admin, privilegeSet);
importStepGenerator.generateImportSteps();
return importStepGenerator.getAllImportSteps();
}
private void applyIfRequired(boolean dryRun, List<PersistableImportStep> importSteps) throws EntitlementException {
if (!dryRun) {
message("Import: Policies to Import {0}", importSteps.size());
for (PersistableImportStep importStep : importSteps) {
message("Import: {0} {1} {2}",
importStep.getDiffStatus().name(), importStep.getType(), importStep.getName());
importStep.apply();
}
message("Import: Complete");
}
}
/**
* Performs an export of all Policies found in the Privilege Manager that match the
* provided filters.
*
* @param realm Non null realm.
* @param admin Non null admin subject to authenticate as.
* @param filters Non null, but maybe empty filters to select Privileges against.
* @return A non null but possibly empty collection of Policies.
* @throws EntitlementException If there was any problem with the generation of Policies.
*/
public PolicySet exportXACML(String realm, Subject admin, List<String> filters)
throws EntitlementException {
PrivilegeManager pm = privilegeManagerFactory.createReferralPrivilegeManager(realm, admin);
Set<SearchFilter> filterSet = new HashSet<SearchFilter>();
if (filters != null) {
for (String filter : filters) {
SearchFilter searchFilter = searchFilterFactory.getFilter(filter);
message("Export: Search Filter: {0}", searchFilter);
filterSet.add(searchFilter);
}
}
Set<String> privilegeNames = pm.searchNames(filterSet);
message("Export: Privilege Matches {0}", privilegeNames.size());
PrivilegeSet privilegeSet = new PrivilegeSet();
for (String name : privilegeNames) {
Privilege privilege = pm.findByName(name, admin);
message("Export: Privilege {0}", privilege.getName());
privilegeSet.addPrivilege(privilege);
}
PolicySet policySet = xacmlReaderWriter.toXACML(realm, privilegeSet);
message("Export: Complete");
return policySet;
}
private void message(String format, Object... args) {
if (debug.messageEnabled()) {
debug.message(MessageFormat.format(PREFIX + format, args));
}
}
/**
* Factory to allow PrivilegeManager to be mocked in tests
*/
public static class PrivilegeManagerFactory {
PrivilegeManager createReferralPrivilegeManager(String realm, Subject admin) {
return PrivilegeManager.getInstance(realm, admin);
}
}
}