AggregateLicensePolicyEnforcer.java
/*
* Copyright (C) 2008-2022 Mycila (mathieu.carbou@gmail.com)
*
* Licensed under the Apache 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* 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 com.mycila.maven.plugin.license.dependencies;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.model.License;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.stream.Collectors;
/**
* Aggregate license policy enforcement with default enforcer bindings based on {@link LicensePolicy.Type}.
* <p>
* Rules are applied in the following order:
* 1) defaultPolicy: unless overridden via setDefaultPolicy, this will DENY all artifacts.
* 2) APPROVE policies: any policy in the Set which have {@link LicensePolicy.Rule.APPROVE}
* 3) DENY policies: any policy in the Set which have {@link LIcensePolicy.Rule.DENY}
*
* @author Royce Remer
*/
@SuppressWarnings("rawtypes")
public class AggregateLicensePolicyEnforcer {
private final Set<LicensePolicy> policies;
private LicensePolicyEnforcer defaultPolicy;
private Set<LicensePolicyEnforcer> enforcers;
public AggregateLicensePolicyEnforcer(final Set<LicensePolicy> policies) {
this.policies = policies;
this.defaultPolicy = new DefaultLicensePolicyEnforcer();
this.enforcers = policies.stream().map(policy -> initPolicyEnforcer(policy)).collect(Collectors.toSet());
}
/**
* Initialize an {@LicensePolicyEnforcer} implementation based on its {@link LicensePolicy.Type}.
*
* @param policy - a single license policy which needs enforcement.
* @return
*/
private static LicensePolicyEnforcer<?> initPolicyEnforcer(final LicensePolicy policy) {
switch (policy.getType()) {
case LICENSE_NAME:
return new LicenseNameLicensePolicyEnforcer(policy);
case ARTIFACT_PATTERN:
return new ArtifactLicensePolicyEnforcer(policy);
case LICENSE_URL:
return new LicenseURLLicensePolicyEnforcer(policy);
default:
return new DefaultLicensePolicyEnforcer();
}
}
/**
* Get a Set of policy enforces that have a given rule (approve/deny) and type (artifact/license).
*
* @param rule - the {@link LicensePolicy.Rule} to filter all enforcers by.
* @return
*/
private Set<LicensePolicyEnforcer> getEnforcers(final LicensePolicy.Rule rule) {
return enforcers.stream()
.filter(e -> e.getPolicy().getRule() == rule)
.collect(Collectors.toSet());
}
/**
* Helper method for taking a single iteration of license to set of artifacts, and applying a policy enforcer.
*
* @param license
* @param artifacts
* @param enforcer
* @return
*/
@SuppressWarnings("unchecked")
private Map<Artifact, LicensePolicyEnforcerResult> apply(final License license, final Set<Artifact> artifacts, final LicensePolicyEnforcer enforcer) {
final Map<Artifact, LicensePolicyEnforcerResult> results = new HashMap<>();
final LicensePolicy.Rule filter = enforcer.getPolicy().getRule();
artifacts.forEach(artifact -> {
LicensePolicy.Rule ruling = LicensePolicy.Rule.DENY;
if (enforcer.getType() == License.class) {
ruling = LicensePolicy.Rule.valueOf(enforcer.apply(license));
} else if (enforcer.getType() == Artifact.class) {
ruling = LicensePolicy.Rule.valueOf(enforcer.apply(artifact));
}
results.put(artifact, new LicensePolicyEnforcerResult(enforcer.getPolicy(), license, artifact, ruling));
});
// if this was an APPROVE rule, only return approvals. If a DENY rule, only return denials
return results.entrySet().stream()
.filter(result -> filter.equals(result.getValue().getRuling()))
.collect(Collectors.toMap(Entry::getKey, Entry::getValue));
}
// Helper method for taking a full map of License:Set<Artifact> and building a rulings map from a policy enforcer.
private Map<Artifact, LicensePolicyEnforcerResult> apply(final Map<License, Set<Artifact>> licenseMap, final LicensePolicyEnforcer enforcer) {
final Map<Artifact, LicensePolicyEnforcerResult> results = new HashMap<>();
licenseMap.forEach((license, artifactSet) -> {
results.putAll(apply(license, artifactSet, enforcer));
});
return results;
}
/**
* Take a map of {@link License} keys and the Set of {@link Artifact} attributed to them,
* applying the internal set of {@link LicensePolicyEnforcer} implementations on them,
* and returning a mapping of Artifact keys to the boolean enforcement decision made.
*
* @param licenseMap - the underlying LicenseMap interface types
* @return final policy decision map on each artifact
*/
@SuppressWarnings("unchecked")
public Map<Artifact, LicensePolicyEnforcerResult> apply(final Map<License, Set<Artifact>> licenseMap) {
final Map<Artifact, LicensePolicyEnforcerResult> results = new HashMap<>();
// apply the default policy to all artifacts, populating the map
licenseMap.entrySet().stream().forEach(entry -> {
License license = entry.getKey();
entry.getValue().forEach(
artifact -> results.putIfAbsent(artifact, new LicensePolicyEnforcerResult(defaultPolicy.getPolicy(),
license, artifact, LicensePolicy.Rule.valueOf(defaultPolicy.apply(artifact)))));
});
// apply approval rules, updating the map
getEnforcers(LicensePolicy.Rule.APPROVE).forEach(enforcer -> {
results.putAll(apply(licenseMap, enforcer));
});
// apply deny rules, updating the map
getEnforcers(LicensePolicy.Rule.DENY).forEach(enforcer -> {
results.putAll(apply(licenseMap, enforcer));
});
return results;
}
/**
* Take an {@link LicenseMap} implementation, getting its licenseMap and
* applying the internal set of {@link LicensePolicyEnforcer} implementations on them,
* and returning a mapping of Artifact keys to the boolean enforcement decision made.
*
* @return final policy decision map on each artifact
*/
public Map<Artifact, LicensePolicyEnforcerResult> apply(final LicenseMap licenseMap) {
return apply(licenseMap.getLicenseMap());
}
public void setEnforcers(final Set<LicensePolicyEnforcer> enforcers) {
this.enforcers = enforcers;
}
public Set<LicensePolicyEnforcer> getEnforcers() {
return enforcers;
}
public Set<LicensePolicy> getPolicies() {
return policies;
}
public LicensePolicyEnforcer<?> getDefaultPolicy() {
return defaultPolicy;
}
public void setDefaultPolicy(final LicensePolicyEnforcer defaultPolicy) {
this.defaultPolicy = defaultPolicy;
}
}