blob: 6745659f0997f3e502a02dfc58b5ff4e961b2c86 [file] [log] [blame]
package org.apache.archiva.redback.role.validator;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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
*
* http://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.
*/
import org.apache.archiva.components.graph.base.SimpleGraph;
import org.apache.archiva.components.graph.base.SimpleNode;
import org.apache.archiva.components.graph.util.Traversal;
import org.apache.archiva.redback.role.RoleManagerException;
import org.apache.archiva.redback.role.model.*;
import org.apache.archiva.redback.role.util.RoleModelUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
/**
* DefaultRoleModelValidator: validates completeness of the model
*
* @author: Jesse McConnell
*/
@Service("roleModelValidator")
public class DefaultRoleModelValidator
implements RoleModelValidator {
private static final Logger log = LoggerFactory.getLogger(DefaultRoleModelValidator.class);
private List<String> validationErrors;
public boolean validate(RedbackRoleModel model)
throws RoleManagerException {
validationErrors = null;
validateRequiredStructure(model);
validateResourceClosure(model);
validateOperationClosure(model);
validateChildRoleClosure(model);
validateParentRoleClosure(model);
validateTemplateClosure(model);
validateNoRoleCycles(model);
validateNoTemplateCycles(model);
if (validationErrors == null) {
return true;
} else {
return false;
}
}
public List<String> getValidationErrors() {
return validationErrors;
}
private void addValidationError(String error) {
if (validationErrors == null) {
validationErrors = new ArrayList<String>(0);
}
validationErrors.add(error);
}
/**
* FIXME this should be taken care of by <required/> in modello, figure out why its not
* in the meantime, implement the basics
*
* @param model
*/
@SuppressWarnings("unchecked")
private void validateRequiredStructure(RedbackRoleModel model) {
// validate model has name
for (ModelApplication application : model.getApplications()) {
if (application.getId() == null) {
addValidationError("model is missing application name");
}
// validate model has version
if (application.getVersion() == null) {
addValidationError(application.getId() + " is missing version");
}
// validate resource bits
for (ModelResource resource : application.getResources()) {
if (resource.getName() == null) {
addValidationError(resource.toString() + " missing name");
}
if (resource.getId() == null) {
addValidationError(resource.toString() + " missing id");
}
}
// validate the operations
for (ModelOperation operation : application.getOperations()) {
if (operation.getName() == null) {
addValidationError(operation.toString() + " missing name");
}
if (operation.getId() == null) {
addValidationError(operation.toString() + " missing id");
}
}
for (ModelRole role : application.getRoles()) {
if (role.getId() == null) {
addValidationError(role.toString() + " missing id");
}
if (role.getName() == null) {
addValidationError(role.toString() + " missing name");
}
if (role.getPermissions() != null) {
for (ModelPermission permission : role.getPermissions()) {
if (permission.getName() == null) {
addValidationError(permission.toString() + " missing name");
}
if (permission.getId() == null) {
addValidationError(permission.toString() + " missing id");
}
if (permission.getOperation() == null) {
addValidationError(permission.toString() + " missing operations");
}
if (permission.getResource() == null) {
addValidationError(permission.toString() + " missing resource");
}
}
}
}
for (ModelTemplate template : application.getTemplates()) {
if (template.getId() == null) {
addValidationError(template.toString() + " missing id");
}
if (template.getNamePrefix() == null) {
addValidationError(template.toString() + " missing name prefix");
}
if (template.getPermissions() != null) {
for (ModelPermission permission : template.getPermissions()) {
if (permission.getName() == null) {
addValidationError(permission.toString() + " missing name");
}
if (permission.getId() == null) {
addValidationError(permission.toString() + " missing id");
}
if (permission.getOperation() == null) {
addValidationError(permission.toString() + " missing operations");
}
if (permission.getResource() == null) {
addValidationError(permission.toString() + " missing resource");
}
}
}
}
}
}
/**
* validate all operations in all declared permissions exist as declared in the operations section
*
* @param model
*/
private void validateOperationClosure(RedbackRoleModel model) {
List<String> operationIdList = RoleModelUtils.getOperationIdList(model);
// check the operations in role permissions
for (ModelApplication application : model.getApplications()) {
for (ModelRole role : application.getRoles()) {
if (role.getPermissions() != null) {
for (ModelPermission permission : role.getPermissions()) {
if (!operationIdList.contains(permission.getOperation())) {
addValidationError("missing operation: " + permission.getOperation() + " in permission "
+ permission.getId());
}
}
}
}
// check the operations in template permissions
for (ModelTemplate template : application.getTemplates()) {
if (template.getPermissions() != null) {
for (ModelPermission permission : template.getPermissions()) {
if (!operationIdList.contains(permission.getOperation())) {
addValidationError("missing operation: " + permission.getOperation() + " in permission "
+ permission.getId());
}
}
}
}
}
}
private void validateResourceClosure(RedbackRoleModel model) {
List<String> resourceIdList = RoleModelUtils.getResourceIdList(model);
for (ModelApplication application : model.getApplications()) {
for (ModelRole role : application.getRoles()) {
if (role.getPermissions() != null) {
for (ModelPermission permission : role.getPermissions()) {
if (!resourceIdList.contains(permission.getResource())) {
addValidationError("missing operation: " + permission.getResource() + " in permission "
+ permission.getId());
}
}
}
}
}
}
private void validateChildRoleClosure(RedbackRoleModel model) {
List<String> roleIdList = RoleModelUtils.getRoleIdList(model);
for (ModelApplication application : model.getApplications()) {
for (ModelRole role : application.getRoles()) {
if (role.getChildRoles() != null) {
for (String childRoleId : role.getChildRoles()) {
if (!roleIdList.contains(childRoleId)) {
addValidationError(
"missing role id: " + childRoleId + " in child roles of role " + role.getId());
}
}
}
}
for (ModelTemplate template : application.getTemplates()) {
if (template.getChildRoles() != null) {
for (String childRoleId : template.getChildRoles()) {
if (!roleIdList.contains(childRoleId)) {
addValidationError(
"missing role id: " + childRoleId + " in child roles of template " + template.getId());
}
}
}
}
}
}
@SuppressWarnings("unchecked")
private void validateParentRoleClosure(RedbackRoleModel model) {
List roleIdList = RoleModelUtils.getRoleIdList(model);
for (ModelApplication application : model.getApplications()) {
for (ModelRole role : application.getRoles()) {
if (role.getParentRoles() != null) {
for (String parentRoleId : role.getParentRoles()) {
if (!roleIdList.contains(parentRoleId)) {
addValidationError(
"missing role id: " + parentRoleId + " in parent roles of role " + role.getId());
}
}
}
}
for (ModelTemplate template : application.getTemplates()) {
if (template.getParentRoles() != null) {
for (String parentRoleId : template.getParentRoles()) {
if (!roleIdList.contains(parentRoleId)) {
addValidationError("missing role id: " + parentRoleId + " in parent roles of template "
+ template.getId());
}
}
}
}
}
}
private void validateTemplateClosure(RedbackRoleModel model) {
List templateIdList = RoleModelUtils.getTemplateIdList(model);
// template name prefix must be unique
List<String> templateNamePrefixList = new ArrayList<String>();
for (ModelApplication application : model.getApplications()) {
for (ModelTemplate template : application.getTemplates()) {
if (template.getParentTemplates() != null) {
for (String parentTemplateId : template.getParentTemplates()) {
if (!templateIdList.contains(parentTemplateId)) {
addValidationError(
"missing template id: " + parentTemplateId + " in parent templates of template "
+ template.getId());
}
}
}
if (template.getChildTemplates() != null) {
for (String childTemplateId : template.getChildTemplates()) {
if (!templateIdList.contains(childTemplateId)) {
addValidationError(
"missing template id: " + childTemplateId + " in child templates of template "
+ template.getId());
}
}
}
if (!templateNamePrefixList.contains(template.getNamePrefix())) {
templateNamePrefixList.add(template.getNamePrefix());
} else {
addValidationError("duplicate name prefix detected: " + template.getNamePrefix());
}
}
}
}
/**
* We are not allowed to have cycles between roles, this method is to detect and raise a red flag when that happens.
*
* @param model
*/
private void validateNoRoleCycles(RedbackRoleModel model) {
log.debug("Validating cycles in role model");
SimpleGraph graph = RoleModelUtils.generateRoleGraph(model);
SimpleNode rootNode = graph.getNode(RoleModelUtils.ROOT);
SimpleNode n;
if ((n = Traversal.findFirstCycleNode(rootNode))!=null) {
log.debug("Adding template cycle validation error for node {}", n.getId());
addValidationError("Cycle detected at "+n.getId());
}
}
/**
* We are not allowed to have cycles between template either, this method is to detect and
* raise a red flag when that happens. Templates are a bit more complex since they have both
* child and parent roles, as well as runtime parent and child templates
* <p>
* the id should be sufficient to test cycles here even though in runtime the id's do not need to be
* unique since it is the binding of a namePrefix and a resource that makes them unique
*
* @param model
*/
private void validateNoTemplateCycles(RedbackRoleModel model) {
log.debug("Validating cycles in role template model ");
SimpleGraph graph = RoleModelUtils.generateTemplateGraph(model);
SimpleNode rootNode = graph.getNode(RoleModelUtils.ROOT);
SimpleNode n;
if ((n = Traversal.findFirstCycleNode(rootNode)) != null) {
log.debug("Adding template cycle validation error for node {}", n.getId());
addValidationError("Template cycle detected at "+n.getId());
}
}
}