3 * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
5 * This program and the accompanying materials are made available under the
6 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
7 * and is available at http://www.eclipse.org/legal/epl-v10.html
10 package org.opendaylight.controller.appauth.authorization;
12 import java.util.ArrayList;
13 import java.util.HashSet;
14 import java.util.List;
15 import java.util.Map.Entry;
17 import java.util.concurrent.ConcurrentMap;
19 import org.opendaylight.controller.containermanager.IContainerAuthorization;
20 import org.opendaylight.controller.sal.authorization.AppRoleLevel;
21 import org.opendaylight.controller.sal.authorization.IResourceAuthorization;
22 import org.opendaylight.controller.sal.authorization.Privilege;
23 import org.opendaylight.controller.sal.authorization.Resource;
24 import org.opendaylight.controller.sal.authorization.ResourceGroup;
25 import org.opendaylight.controller.sal.authorization.UserLevel;
26 import org.opendaylight.controller.sal.utils.ServiceHelper;
27 import org.opendaylight.controller.sal.utils.Status;
28 import org.opendaylight.controller.sal.utils.StatusCode;
29 import org.opendaylight.controller.usermanager.IUserManager;
30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory;
34 * This abstract class implements the methods defined by IResourceAuthorization
35 * interface for each application class.
37 public abstract class Authorization<T> implements IResourceAuthorization {
38 private static final Logger logger = LoggerFactory.getLogger(Authorization.class);
39 private static final String namesRegex = "^[a-zA-Z0-9]+[{\\.|\\_|\\-}[a-zA-Z0-9]]*$";
41 * The configured resource groups
43 protected ConcurrentMap<String, Set<T>> resourceGroups;
45 * The configured roles along with their level
47 protected ConcurrentMap<String, AppRoleLevel> roles;
49 * The association of groups to roles
51 protected ConcurrentMap<String, Set<ResourceGroup>> groupsAuthorizations;
53 * The name of the default group. It is the group which contains all the
56 protected String allResourcesGroupName;
59 public Status createRole(String role, AppRoleLevel level) {
60 if (role == null || role.trim().isEmpty()
61 || !role.matches(Authorization.namesRegex)) {
62 return new Status(StatusCode.BADREQUEST,
63 "Role name must start with alphanumeric, no special characters.");
65 if (isControllerRole(role)) {
66 return new Status(StatusCode.NOTALLOWED,
67 "Controller roles cannot be explicitely "
68 + "created in App context");
70 if (isContainerRole(role)) {
71 return new Status(StatusCode.NOTALLOWED,
72 "Container roles cannot be explicitely "
73 + "created in App context");
75 if (isRoleInUse(role)) {
76 return new Status(StatusCode.CONFLICT, "Role already in use");
78 if (roles.containsKey(role)) {
79 if (roles.get(role).equals(level)) {
80 return new Status(StatusCode.SUCCESS, "Role is already present");
82 return new Status(StatusCode.BADREQUEST,
83 "Role exists and has different level");
87 return createRoleInternal(role, level);
90 protected Status createRoleInternal(String role, AppRoleLevel level) {
91 roles.put(role, level);
92 groupsAuthorizations.put(role, new HashSet<ResourceGroup>());
93 return new Status(StatusCode.SUCCESS);
97 public Status removeRole(String role) {
98 if (role == null || role.trim().isEmpty()) {
99 return new Status(StatusCode.BADREQUEST, "Role name can't be empty");
101 if (isControllerRole(role)) {
102 return new Status(StatusCode.NOTALLOWED,
103 "Controller roles cannot be removed");
105 if (isContainerRole(role)) {
106 return new Status(StatusCode.NOTALLOWED,
107 "Container roles cannot be removed");
109 return removeRoleInternal(role);
112 protected Status removeRoleInternal(String role) {
113 groupsAuthorizations.remove(role);
115 return new Status(StatusCode.SUCCESS);
119 public List<String> getRoles() {
120 return new ArrayList<String>(groupsAuthorizations.keySet());
123 @SuppressWarnings("unchecked")
125 public Status createResourceGroup(String groupName, List<Object> resources) {
126 //verify group name not null/empty
127 if (groupName == null || groupName.trim().isEmpty()
128 || !groupName.matches(Authorization.namesRegex)) {
129 return new Status(StatusCode.BADREQUEST, "Group name must start with alphanumeric, no special characters");
131 //verify group name is not same as all-resources
132 if (groupName.equals(this.allResourcesGroupName)) {
133 return new Status(StatusCode.NOTALLOWED, "All resource group cannot be created");
135 //verify group name is unique
136 if (resourceGroups.containsKey(groupName)) {
137 return new Status(StatusCode.CONFLICT, "Group name already exists");
140 //try adding resources, discard if not of type T
141 Set<T> toBeAdded = new HashSet<T>();
142 boolean allAdded = true;
143 for (Object obj : resources) {
145 toBeAdded.add((T) obj);
146 } catch (ClassCastException e) {
147 logger.debug("Attempt to add a resource with invalid type");
151 resourceGroups.put(groupName, toBeAdded);
152 return (allAdded ? new Status(StatusCode.SUCCESS, "All resources added succesfully") :
153 new Status(StatusCode.SUCCESS, "One or more resources couldn't be added"));
156 @SuppressWarnings("unchecked")
158 public Status addResourceToGroup(String groupName, Object resource) {
159 if (groupName == null || groupName.trim().isEmpty()) {
160 return new Status(StatusCode.BADREQUEST, "Invalid group name");
163 if (resource == null) {
164 return new Status(StatusCode.BADREQUEST, "Null resource");
167 T castedResource = null;
169 castedResource = (T) resource;
170 } catch (ClassCastException e) {
171 logger.debug("Attempt to add a resource with invalid type");
172 return new Status(StatusCode.BADREQUEST, "Incompatible resource");
175 Set<T> group = resourceGroups.get(groupName);
177 return new Status(StatusCode.NOTFOUND, "Group not found");
180 return addResourceToGroupInternal(groupName, castedResource);
184 * Method child classes can overload if they need application specific
185 * checks on the resource
187 protected Status addResourceToGroupInternal(String groupName, T resource) {
188 Set<T> group = resourceGroups.get(groupName);
189 // Update group and cluster
191 resourceGroups.put(groupName, group);
193 return new Status(StatusCode.SUCCESS, "Resource added successfully");
197 private Status removeRoleResourceGroupMapping(String groupName) {
198 List<String> affectedRoles = new ArrayList<String>();
200 for (Entry<String, Set<ResourceGroup>> pairs : groupsAuthorizations.entrySet()) {
201 String role = pairs.getKey();
202 Set<ResourceGroup> groups = pairs.getValue();
203 for (ResourceGroup group : groups) {
204 if (group.getGroupName().equals(groupName)) {
205 affectedRoles.add(role);
210 StringBuffer msg = new StringBuffer();
211 for (String role : affectedRoles) {
212 result = unassignResourceGroupFromRole(groupName, role);
213 if (!result.isSuccess()) {
214 msg.append(result.getDescription());
219 if (msg.length() != 0) {
220 return new Status(StatusCode.BADREQUEST, msg.toString());
222 return new Status(StatusCode.SUCCESS);
227 public Status removeResourceGroup(String groupName) {
228 // Default resource group cannot be deleted
229 if (groupName == null || groupName.trim().isEmpty()) {
230 return new Status(StatusCode.BADREQUEST, "Invalid group name");
232 if (groupName.equals(this.allResourcesGroupName)) {
233 return new Status(StatusCode.NOTALLOWED,
234 "All resource group cannot be removed");
236 resourceGroups.remove(groupName);
237 Status result = removeRoleResourceGroupMapping(groupName);
239 return result.isSuccess() ? result :
240 new Status(StatusCode.SUCCESS, "Failed removing group from: " + result.getDescription());
245 public Status removeResourceFromGroup(String groupName, Object resource) {
246 if (groupName == null || groupName.trim().isEmpty()) {
247 return new Status(StatusCode.BADREQUEST, "Invalid group name");
250 Set<T> group = resourceGroups.get(groupName);
251 if (group != null && group.remove(resource)) {
253 resourceGroups.put(groupName, group);
254 return new Status(StatusCode.SUCCESS, "Resource removed successfully");
257 return new Status(StatusCode.NOTFOUND, "Group/Resource not found");
262 * Relay the call to user manager
265 public UserLevel getUserLevel(String userName) {
266 IUserManager userManager = (IUserManager) ServiceHelper
267 .getGlobalInstance(IUserManager.class, this);
269 if (logger.isDebugEnabled()) {
270 logger.debug("User {} has UserLevel {}", userName, userManager.getUserLevel(userName));
273 return (userManager == null) ? UserLevel.NOUSER : userManager
274 .getUserLevel(userName);
278 public Set<Resource> getAllResourcesforUser(String userName) {
279 Set<Resource> resources = new HashSet<Resource>();
280 IUserManager userManager = (IUserManager) ServiceHelper
281 .getGlobalInstance(IUserManager.class, this);
282 if (userManager == null) {
283 return new HashSet<Resource>(0);
286 // Get the roles associated with this user
287 List<String> roles = userManager.getUserRoles(userName);
292 for (String role : roles) {
293 // Get our resource groups associated with this role
294 List<ResourceGroup> groups = this.getAuthorizedGroups(role);
295 if (groups.isEmpty()) {
298 for (ResourceGroup group : groups) {
299 // Get the list of resources in this group
300 List<Object> list = this.getResources(group.getGroupName());
301 if (list.isEmpty()) {
304 for (Object resource : list) {
305 Resource toBeAdded = new Resource(resource,
306 group.getPrivilege());
308 * Add the resource to the set if the same resource with
309 * higher privilege is not present. If the same resource
310 * with lower privilege is present, remove it. No check on
311 * same privilege resource as set guarantees no duplicates.
313 Resource existing = higherPrivilegeResourcePresent(
314 resources, toBeAdded);
315 if (existing == null) {
316 resources.add(toBeAdded);
318 existing = lowerPrivilegeResourcePresent(resources,
320 if (existing != null) {
321 resources.remove(existing);
327 if (logger.isDebugEnabled()) {
328 logger.debug("User {} has resources {}", userName, resources);
335 public List<Resource> getAuthorizedResources(String role) {
336 Set<Resource> resources = new HashSet<Resource>();
338 // Get our resource groups associated with this role
339 List<ResourceGroup> groups = this.getAuthorizedGroups(role);
340 if (groups.isEmpty()) {
341 return new ArrayList<Resource>(0);
343 for (ResourceGroup group : groups) {
344 // Get the list of resources in this group
345 List<Object> list = this.getResources(group.getGroupName());
346 if (list.isEmpty()) {
349 for (Object resource : list) {
350 Resource toBeAdded = new Resource(resource,
351 group.getPrivilege());
353 * Add the resource to the set if the same resource with higher
354 * privilege is not present. If the same resource with lower
355 * privilege is present, remove it. No check on same privilege
356 * resource as set guarantees no duplicates.
358 Resource existing = higherPrivilegeResourcePresent(resources,
360 if (existing == null) {
361 resources.add(toBeAdded);
363 existing = lowerPrivilegeResourcePresent(resources, toBeAdded);
364 if (existing != null) {
365 resources.remove(existing);
370 if (logger.isDebugEnabled()) {
371 logger.debug("For the Role {}, Authorized Resources are {}", role, resources);
374 return new ArrayList<Resource>(resources);
378 * Given a set of resources and a resource to test, it returns the element
379 * in the set which represent the same resource with a lower privilege
380 * associated to it, if present.
386 private Resource lowerPrivilegeResourcePresent(Set<Resource> resources,
389 if (resource == null || resources == null) {
393 Object resourceElement = resource.getResource();
394 Privilege resourcePrivilege = resource.getPrivilege();
395 for (Resource element : resources) {
396 if (element.getResource().equals(resourceElement)
397 && element.getPrivilege().ordinal() < resourcePrivilege
406 * Given a set of resources and a resource to test, it returns the element
407 * in the set which represents the same resource with an higher privilege,
414 private Resource higherPrivilegeResourcePresent(Set<Resource> resources,
417 if (resource == null || resources == null) {
421 Object resourceElement = resource.getResource();
422 Privilege resourcePrivilege = resource.getPrivilege();
423 for (Resource element : resources) {
424 if (element.getResource().equals(resourceElement)
425 && element.getPrivilege().ordinal() > resourcePrivilege
435 public Privilege getResourcePrivilege(String userName, Object resource) {
437 if (userName == null || userName.trim().isEmpty() || resource == null) {
438 return Privilege.NONE;
441 Set<Resource> hisResources = getAllResourcesforUser(userName);
442 for (Resource element : hisResources) {
443 if (element.getResource().equals(resource)) {
444 return element.getPrivilege();
448 return Privilege.NONE;
452 public List<String> getResourceGroups() {
453 return new ArrayList<String>(resourceGroups.keySet());
457 public Status assignResourceGroupToRole(String group, Privilege privilege,
459 if (group == null || group.trim().isEmpty()) {
460 return new Status(StatusCode.BADREQUEST, "Invalid group name");
462 if (role == null || role.trim().isEmpty()) {
463 return new Status(StatusCode.BADREQUEST, "Role name can't be empty");
465 if (isControllerRole(role)) {
466 return new Status(StatusCode.NOTALLOWED,
467 "No group assignment is accepted for Controller roles");
470 return assignResourceGroupToRoleInternal(group, privilege, role);
473 protected Status assignResourceGroupToRoleInternal(String group, Privilege privilege, String role) {
474 Set<ResourceGroup> roleGroups = groupsAuthorizations.get(role);
475 roleGroups.add(new ResourceGroup(group, privilege));
477 groupsAuthorizations.put(role, roleGroups);
478 return new Status(StatusCode.SUCCESS);
482 public Status assignResourceGroupToRole(String groupName, String roleName) {
483 if (groupName == null || groupName.trim().isEmpty()) {
484 return new Status(StatusCode.BADREQUEST, "Group name can't be empty");
486 if (roleName == null || roleName.trim().isEmpty()) {
487 return new Status(StatusCode.BADREQUEST, "Role name can't be empty");
489 // Infer group privilege from role's level
490 Privilege privilege = Privilege.NONE;
491 switch (this.getApplicationRoleLevel(roleName)) {
493 privilege = Privilege.WRITE;
496 privilege = Privilege.USE;
499 privilege = Privilege.READ;
504 return this.assignResourceGroupToRole(groupName, privilege, roleName);
508 public Status unassignResourceGroupFromRole(String group, String role) {
509 if (group == null || group.trim().isEmpty()) {
510 return new Status(StatusCode.BADREQUEST, "Group name can't be empty");
512 if (role == null || role.trim().isEmpty()) {
513 return new Status(StatusCode.BADREQUEST, "Role name can't be empty");
515 if (isControllerRole(role)) {
516 return new Status(StatusCode.NOTALLOWED,
517 "No group assignment change is allowed for "
518 + "Controller roles");
521 return unassignResourceGroupFromRoleInternal(group, role);
524 protected Status unassignResourceGroupFromRoleInternal(String group, String role) {
525 ResourceGroup target = null;
526 for (ResourceGroup rGroup : groupsAuthorizations.get(role)) {
527 if (rGroup.getGroupName().equals(group)) {
532 if (target == null) {
533 return new Status(StatusCode.SUCCESS, "Group " + group + " was not assigned to " + role);
535 Set<ResourceGroup> groups = groupsAuthorizations.get(role);
536 groups.remove(target);
538 groupsAuthorizations.put(role, groups);
539 return new Status(StatusCode.SUCCESS);
545 public List<ResourceGroup> getAuthorizedGroups(String role) {
546 return (groupsAuthorizations.containsKey(role)) ? new ArrayList<ResourceGroup>(
547 groupsAuthorizations.get(role))
548 : new ArrayList<ResourceGroup>();
552 public List<Object> getResources(String groupName) {
553 return (resourceGroups.containsKey(groupName)) ? new ArrayList<Object>(
554 resourceGroups.get(groupName)) : new ArrayList<Object>(0);
558 public boolean isApplicationRole(String roleName) {
559 if (roleName == null) {
562 return roles.containsKey(roleName);
566 public boolean isApplicationUser(String userName) {
567 IUserManager userManager = (IUserManager) ServiceHelper
568 .getGlobalInstance(IUserManager.class, this);
569 if (userManager == null) {
572 List<String> roles = userManager.getUserRoles(userName);
573 if (roles != null && !roles.isEmpty()) {
574 for (String role : roles) {
575 if (isApplicationRole(role)) {
584 public AppRoleLevel getApplicationRoleLevel(String roleName) {
585 if (roleName == null || roleName.trim().isEmpty()) {
586 return AppRoleLevel.NOUSER;
589 if (isControllerRole(roleName)) {
590 if (roleName.equals(UserLevel.NETWORKADMIN.toString()) ||
591 roleName.equals(UserLevel.SYSTEMADMIN.toString())) {
592 return AppRoleLevel.APPADMIN;
594 return AppRoleLevel.APPOPERATOR;
598 return (roles.containsKey(roleName)) ? roles.get(roleName)
599 : AppRoleLevel.NOUSER;
603 public AppRoleLevel getUserApplicationLevel(String userName) {
604 List<String> roles = null;
605 IUserManager userManager = (IUserManager) ServiceHelper
606 .getGlobalInstance(IUserManager.class, this);
608 if (userName == null || userName.trim().isEmpty()
609 || (userManager == null)
610 || (roles = userManager.getUserRoles(userName)).isEmpty()) {
611 return AppRoleLevel.NOUSER;
613 AppRoleLevel highestLevel = AppRoleLevel.NOUSER;
614 for (String role : roles) {
615 AppRoleLevel level = getApplicationRoleLevel(role);
616 if (level.ordinal() < highestLevel.ordinal()) {
617 highestLevel = level;
624 * Returns the highest role the specified user has in this application
629 * @return The highest role associated to the user in this application
632 public String getHighestUserRole(String user) {
633 IUserManager userManager = (IUserManager) ServiceHelper.getGlobalInstance(IUserManager.class, this);
634 String highestRole = "";
635 if (userManager != null && !userManager.getUserRoles(user).isEmpty()) {
636 List<String> roles = userManager.getUserRoles(user);
637 AppRoleLevel highestLevel = AppRoleLevel.NOUSER;
638 for (String role : roles) {
639 AppRoleLevel current;
640 if (isApplicationRole(role)
641 && (current = getApplicationRoleLevel(role)).ordinal() < highestLevel.ordinal()) {
643 highestLevel = current;
650 private boolean isControllerRole(String role) {
651 return (role.equals(UserLevel.NETWORKADMIN.toString())
652 || role.equals(UserLevel.SYSTEMADMIN.toString()) || role
653 .equals(UserLevel.NETWORKOPERATOR.toString()));
656 private boolean isContainerRole(String role) {
657 IContainerAuthorization containerAuth = (IContainerAuthorization) ServiceHelper.getGlobalInstance(
658 IContainerAuthorization.class, this);
659 if (containerAuth == null) {
662 return containerAuth.isApplicationRole(role);
665 private boolean isRoleInUse(String role) {
666 IUserManager userManager = (IUserManager) ServiceHelper
667 .getGlobalInstance(IUserManager.class, this);
669 if (userManager == null) {
672 return userManager.isRoleInUse(role);