001package org.apache.archiva.redback.role.validator; 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022import org.apache.archiva.components.graph.base.SimpleGraph; 023import org.apache.archiva.components.graph.base.SimpleNode; 024import org.apache.archiva.components.graph.util.Traversal; 025import org.apache.archiva.redback.role.RoleManagerException; 026import org.apache.archiva.redback.role.model.*; 027import org.apache.archiva.redback.role.util.RoleModelUtils; 028import org.slf4j.Logger; 029import org.slf4j.LoggerFactory; 030import org.springframework.stereotype.Service; 031 032import java.util.ArrayList; 033import java.util.List; 034 035/** 036 * DefaultRoleModelValidator: validates completeness of the model 037 * 038 * @author: Jesse McConnell 039 */ 040@Service("roleModelValidator") 041public class DefaultRoleModelValidator 042 implements RoleModelValidator { 043 044 private static final Logger log = LoggerFactory.getLogger(DefaultRoleModelValidator.class); 045 046 private List<String> validationErrors; 047 048 public boolean validate(RedbackRoleModel model) 049 throws RoleManagerException { 050 validationErrors = null; 051 052 validateRequiredStructure(model); 053 validateResourceClosure(model); 054 validateOperationClosure(model); 055 validateChildRoleClosure(model); 056 validateParentRoleClosure(model); 057 validateTemplateClosure(model); 058 validateNoRoleCycles(model); 059 validateNoTemplateCycles(model); 060 061 if (validationErrors == null) { 062 return true; 063 } else { 064 return false; 065 } 066 } 067 068 public List<String> getValidationErrors() { 069 return validationErrors; 070 } 071 072 private void addValidationError(String error) { 073 if (validationErrors == null) { 074 validationErrors = new ArrayList<String>(0); 075 } 076 077 validationErrors.add(error); 078 } 079 080 /** 081 * FIXME this should be taken care of by <required/> in modello, figure out why its not 082 * in the meantime, implement the basics 083 * 084 * @param model 085 */ 086 @SuppressWarnings("unchecked") 087 private void validateRequiredStructure(RedbackRoleModel model) { 088 // validate model has name 089 090 for (ModelApplication application : model.getApplications()) { 091 if (application.getId() == null) { 092 addValidationError("model is missing application name"); 093 } 094 095 // validate model has version 096 if (application.getVersion() == null) { 097 addValidationError(application.getId() + " is missing version"); 098 } 099 100 // validate resource bits 101 for (ModelResource resource : application.getResources()) { 102 if (resource.getName() == null) { 103 addValidationError(resource.toString() + " missing name"); 104 } 105 106 if (resource.getId() == null) { 107 addValidationError(resource.toString() + " missing id"); 108 } 109 } 110 111 // validate the operations 112 for (ModelOperation operation : application.getOperations()) { 113 if (operation.getName() == null) { 114 addValidationError(operation.toString() + " missing name"); 115 } 116 117 if (operation.getId() == null) { 118 addValidationError(operation.toString() + " missing id"); 119 } 120 } 121 122 for (ModelRole role : application.getRoles()) { 123 if (role.getId() == null) { 124 addValidationError(role.toString() + " missing id"); 125 } 126 127 if (role.getName() == null) { 128 addValidationError(role.toString() + " missing name"); 129 } 130 131 if (role.getPermissions() != null) { 132 for (ModelPermission permission : role.getPermissions()) { 133 if (permission.getName() == null) { 134 addValidationError(permission.toString() + " missing name"); 135 } 136 137 if (permission.getId() == null) { 138 addValidationError(permission.toString() + " missing id"); 139 } 140 141 if (permission.getOperation() == null) { 142 addValidationError(permission.toString() + " missing operations"); 143 } 144 145 if (permission.getResource() == null) { 146 addValidationError(permission.toString() + " missing resource"); 147 } 148 } 149 } 150 } 151 152 for (ModelTemplate template : application.getTemplates()) { 153 if (template.getId() == null) { 154 addValidationError(template.toString() + " missing id"); 155 } 156 157 if (template.getNamePrefix() == null) { 158 addValidationError(template.toString() + " missing name prefix"); 159 } 160 161 if (template.getPermissions() != null) { 162 for (ModelPermission permission : template.getPermissions()) { 163 if (permission.getName() == null) { 164 addValidationError(permission.toString() + " missing name"); 165 } 166 167 if (permission.getId() == null) { 168 addValidationError(permission.toString() + " missing id"); 169 } 170 171 if (permission.getOperation() == null) { 172 addValidationError(permission.toString() + " missing operations"); 173 } 174 175 if (permission.getResource() == null) { 176 addValidationError(permission.toString() + " missing resource"); 177 } 178 } 179 } 180 } 181 } 182 } 183 184 /** 185 * validate all operations in all declared permissions exist as declared in the operations section 186 * 187 * @param model 188 */ 189 private void validateOperationClosure(RedbackRoleModel model) { 190 List<String> operationIdList = RoleModelUtils.getOperationIdList(model); 191 192 // check the operations in role permissions 193 for (ModelApplication application : model.getApplications()) { 194 for (ModelRole role : application.getRoles()) { 195 if (role.getPermissions() != null) { 196 for (ModelPermission permission : role.getPermissions()) { 197 if (!operationIdList.contains(permission.getOperation())) { 198 addValidationError("missing operation: " + permission.getOperation() + " in permission " 199 + permission.getId()); 200 } 201 } 202 } 203 } 204 205 // check the operations in template permissions 206 for (ModelTemplate template : application.getTemplates()) { 207 if (template.getPermissions() != null) { 208 for (ModelPermission permission : template.getPermissions()) { 209 if (!operationIdList.contains(permission.getOperation())) { 210 addValidationError("missing operation: " + permission.getOperation() + " in permission " 211 + permission.getId()); 212 } 213 } 214 } 215 } 216 } 217 } 218 219 private void validateResourceClosure(RedbackRoleModel model) { 220 List<String> resourceIdList = RoleModelUtils.getResourceIdList(model); 221 for (ModelApplication application : model.getApplications()) { 222 for (ModelRole role : application.getRoles()) { 223 if (role.getPermissions() != null) { 224 for (ModelPermission permission : role.getPermissions()) { 225 if (!resourceIdList.contains(permission.getResource())) { 226 addValidationError("missing operation: " + permission.getResource() + " in permission " 227 + permission.getId()); 228 } 229 } 230 } 231 } 232 } 233 } 234 235 private void validateChildRoleClosure(RedbackRoleModel model) { 236 List<String> roleIdList = RoleModelUtils.getRoleIdList(model); 237 for (ModelApplication application : model.getApplications()) { 238 for (ModelRole role : application.getRoles()) { 239 if (role.getChildRoles() != null) { 240 for (String childRoleId : role.getChildRoles()) { 241 if (!roleIdList.contains(childRoleId)) { 242 addValidationError( 243 "missing role id: " + childRoleId + " in child roles of role " + role.getId()); 244 } 245 } 246 } 247 } 248 249 for (ModelTemplate template : application.getTemplates()) { 250 if (template.getChildRoles() != null) { 251 for (String childRoleId : template.getChildRoles()) { 252 if (!roleIdList.contains(childRoleId)) { 253 addValidationError( 254 "missing role id: " + childRoleId + " in child roles of template " + template.getId()); 255 } 256 } 257 } 258 } 259 } 260 } 261 262 @SuppressWarnings("unchecked") 263 private void validateParentRoleClosure(RedbackRoleModel model) { 264 List roleIdList = RoleModelUtils.getRoleIdList(model); 265 266 for (ModelApplication application : model.getApplications()) { 267 for (ModelRole role : application.getRoles()) { 268 if (role.getParentRoles() != null) { 269 for (String parentRoleId : role.getParentRoles()) { 270 if (!roleIdList.contains(parentRoleId)) { 271 addValidationError( 272 "missing role id: " + parentRoleId + " in parent roles of role " + role.getId()); 273 } 274 } 275 } 276 } 277 278 for (ModelTemplate template : application.getTemplates()) { 279 if (template.getParentRoles() != null) { 280 for (String parentRoleId : template.getParentRoles()) { 281 if (!roleIdList.contains(parentRoleId)) { 282 addValidationError("missing role id: " + parentRoleId + " in parent roles of template " 283 + template.getId()); 284 } 285 } 286 } 287 } 288 } 289 } 290 291 private void validateTemplateClosure(RedbackRoleModel model) { 292 List templateIdList = RoleModelUtils.getTemplateIdList(model); 293 294 // template name prefix must be unique 295 List<String> templateNamePrefixList = new ArrayList<String>(); 296 297 for (ModelApplication application : model.getApplications()) { 298 for (ModelTemplate template : application.getTemplates()) { 299 if (template.getParentTemplates() != null) { 300 for (String parentTemplateId : template.getParentTemplates()) { 301 if (!templateIdList.contains(parentTemplateId)) { 302 addValidationError( 303 "missing template id: " + parentTemplateId + " in parent templates of template " 304 + template.getId()); 305 } 306 } 307 } 308 309 if (template.getChildTemplates() != null) { 310 for (String childTemplateId : template.getChildTemplates()) { 311 if (!templateIdList.contains(childTemplateId)) { 312 addValidationError( 313 "missing template id: " + childTemplateId + " in child templates of template " 314 + template.getId()); 315 } 316 } 317 } 318 319 if (!templateNamePrefixList.contains(template.getNamePrefix())) { 320 templateNamePrefixList.add(template.getNamePrefix()); 321 } else { 322 addValidationError("duplicate name prefix detected: " + template.getNamePrefix()); 323 } 324 } 325 } 326 } 327 328 /** 329 * We are not allowed to have cycles between roles, this method is to detect and raise a red flag when that happens. 330 * 331 * @param model 332 */ 333 private void validateNoRoleCycles(RedbackRoleModel model) { 334 log.debug("Validating cycles in role model"); 335 SimpleGraph graph = RoleModelUtils.generateRoleGraph(model); 336 SimpleNode rootNode = graph.getNode(RoleModelUtils.ROOT); 337 SimpleNode n; 338 if ((n = Traversal.findFirstCycleNode(rootNode))!=null) { 339 log.debug("Adding template cycle validation error for node {}", n.getId()); 340 addValidationError("Cycle detected at "+n.getId()); 341 } 342 } 343 344 /** 345 * We are not allowed to have cycles between template either, this method is to detect and 346 * raise a red flag when that happens. Templates are a bit more complex since they have both 347 * child and parent roles, as well as runtime parent and child templates 348 * <p> 349 * the id should be sufficient to test cycles here even though in runtime the id's do not need to be 350 * unique since it is the binding of a namePrefix and a resource that makes them unique 351 * 352 * @param model 353 */ 354 private void validateNoTemplateCycles(RedbackRoleModel model) { 355 log.debug("Validating cycles in role template model "); 356 SimpleGraph graph = RoleModelUtils.generateTemplateGraph(model); 357 SimpleNode rootNode = graph.getNode(RoleModelUtils.ROOT); 358 SimpleNode n; 359 if ((n = Traversal.findFirstCycleNode(rootNode)) != null) { 360 log.debug("Adding template cycle validation error for node {}", n.getId()); 361 addValidationError("Template cycle detected at "+n.getId()); 362 } 363 } 364}