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.configuration.ConfigurationObject;
20 import org.opendaylight.controller.containermanager.IContainerAuthorization;
21 import org.opendaylight.controller.sal.authorization.AppRoleLevel;
22 import org.opendaylight.controller.sal.authorization.IResourceAuthorization;
23 import org.opendaylight.controller.sal.authorization.Privilege;
24 import org.opendaylight.controller.sal.authorization.Resource;
25 import org.opendaylight.controller.sal.authorization.ResourceGroup;
26 import org.opendaylight.controller.sal.authorization.UserLevel;
27 import org.opendaylight.controller.sal.utils.ServiceHelper;
28 import org.opendaylight.controller.sal.utils.Status;
29 import org.opendaylight.controller.sal.utils.StatusCode;
30 import org.opendaylight.controller.usermanager.IUserManager;
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
35 * This abstract class implements the methods defined by IResourceAuthorization
36 * interface for each application class.
38 public abstract class Authorization<T> implements IResourceAuthorization {
39 private static final Logger logger = LoggerFactory.getLogger(Authorization.class);
40 private static final String namesRegex = ConfigurationObject.getRegularExpression();
42 * The configured resource groups
44 protected ConcurrentMap<String, Set<T>> resourceGroups;
46 * The configured roles along with their level
48 protected ConcurrentMap<String, AppRoleLevel> roles;
50 * The association of groups to roles
52 protected ConcurrentMap<String, Set<ResourceGroup>> groupsAuthorizations;
54 * The name of the default group. It is the group which contains all the
57 protected String allResourcesGroupName;
60 public Status createRole(String role, AppRoleLevel level) {
61 if (role == null || role.trim().isEmpty()
62 || !role.matches(Authorization.namesRegex)) {
63 return new Status(StatusCode.BADREQUEST,
64 "Role name must start with alphanumeric, no special characters.");
66 if (isControllerRole(role)) {
67 return new Status(StatusCode.NOTALLOWED,
68 "Controller roles cannot be explicitely "
69 + "created in App context");
71 if (isContainerRole(role)) {
72 return new Status(StatusCode.NOTALLOWED,
73 "Container roles cannot be explicitely "
74 + "created in App context");
76 if (isRoleInUse(role)) {
77 return new Status(StatusCode.CONFLICT, "Role already in use");
79 if (roles.containsKey(role)) {
80 if (roles.get(role).equals(level)) {
81 return new Status(StatusCode.SUCCESS, "Role is already present");
83 return new Status(StatusCode.BADREQUEST,
84 "Role exists and has different level");
88 return createRoleInternal(role, level);
91 protected Status createRoleInternal(String role, AppRoleLevel level) {
92 roles.put(role, level);
93 groupsAuthorizations.put(role, new HashSet<ResourceGroup>());
94 return new Status(StatusCode.SUCCESS);
98 public Status removeRole(String role) {
99 if (role == null || role.trim().isEmpty()) {
100 return new Status(StatusCode.BADREQUEST, "Role name can't be empty");
102 if (isControllerRole(role)) {
103 return new Status(StatusCode.NOTALLOWED,
104 "Controller roles cannot be removed");
106 if (isContainerRole(role)) {
107 return new Status(StatusCode.NOTALLOWED,
108 "Container roles cannot be removed");
110 return removeRoleInternal(role);
113 protected Status removeRoleInternal(String role) {
114 groupsAuthorizations.remove(role);
116 return new Status(StatusCode.SUCCESS);
120 public List<String> getRoles() {
121 return new ArrayList<String>(groupsAuthorizations.keySet());
124 @SuppressWarnings("unchecked")
126 public Status createResourceGroup(String groupName, List<Object> resources) {
127 //verify group name not null/empty
128 if (groupName == null || groupName.trim().isEmpty()
129 || !groupName.matches(Authorization.namesRegex)) {
130 return new Status(StatusCode.BADREQUEST, "Group name must start with alphanumeric, no special characters");
132 //verify group name is not same as all-resources
133 if (groupName.equals(this.allResourcesGroupName)) {
134 return new Status(StatusCode.NOTALLOWED, "All resource group cannot be created");
136 //verify group name is unique
137 if (resourceGroups.containsKey(groupName)) {
138 return new Status(StatusCode.CONFLICT, "Group name already exists");
141 //try adding resources, discard if not of type T
142 Set<T> toBeAdded = new HashSet<T>();
143 boolean allAdded = true;
144 for (Object obj : resources) {
146 toBeAdded.add((T) obj);
147 } catch (ClassCastException e) {
148 logger.debug("Attempt to add a resource with invalid type");
152 resourceGroups.put(groupName, toBeAdded);
153 return (allAdded ? new Status(StatusCode.SUCCESS, "All resources added succesfully") :
154 new Status(StatusCode.SUCCESS, "One or more resources couldn't be added"));
157 @SuppressWarnings("unchecked")
159 public Status addResourceToGroup(String groupName, Object resource) {
160 if (groupName == null || groupName.trim().isEmpty()) {
161 return new Status(StatusCode.BADREQUEST, "Invalid group name");
164 if (resource == null) {
165 return new Status(StatusCode.BADREQUEST, "Null resource");
168 T castedResource = null;
170 castedResource = (T) resource;
171 } catch (ClassCastException e) {
172 logger.debug("Attempt to add a resource with invalid type");
173 return new Status(StatusCode.BADREQUEST, "Incompatible resource");
176 Set<T> group = resourceGroups.get(groupName);
178 return new Status(StatusCode.NOTFOUND, "Group not found");
181 return addResourceToGroupInternal(groupName, castedResource);
185 * Method child classes can overload if they need application specific
186 * checks on the resource
188 protected Status addResourceToGroupInternal(String groupName, T resource) {
189 Set<T> group = resourceGroups.get(groupName);
190 // Update group and cluster
192 resourceGroups.put(groupName, group);
194 return new Status(StatusCode.SUCCESS, "Resource added successfully");
198 private Status removeRoleResourceGroupMapping(String groupName) {
199 List<String> affectedRoles = new ArrayList<String>();
201 for (Entry<String, Set<ResourceGroup>> pairs : groupsAuthorizations.entrySet()) {
202 String role = pairs.getKey();
203 Set<ResourceGroup> groups = pairs.getValue();
204 for (ResourceGroup group : groups) {
205 if (group.getGroupName().equals(groupName)) {
206 affectedRoles.add(role);
211 StringBuffer msg = new StringBuffer();
212 for (String role : affectedRoles) {
213 result = unassignResourceGroupFromRole(groupName, role);
214 if (!result.isSuccess()) {
215 msg.append(result.getDescription());
220 if (msg.length() != 0) {
221 return new Status(StatusCode.BADREQUEST, msg.toString());
223 return new Status(StatusCode.SUCCESS);
228 public Status removeResourceGroup(String groupName) {
229 // Default resource group cannot be deleted
230 if (groupName == null || groupName.trim().isEmpty()) {
231 return new Status(StatusCode.BADREQUEST, "Invalid group name");
233 if (groupName.equals(this.allResourcesGroupName)) {
234 return new Status(StatusCode.NOTALLOWED,
235 "All resource group cannot be removed");
237 resourceGroups.remove(groupName);
238 Status result = removeRoleResourceGroupMapping(groupName);
240 return result.isSuccess() ? result :
241 new Status(StatusCode.SUCCESS, "Failed removing group from: " + result.getDescription());
246 public Status removeResourceFromGroup(String groupName, Object resource) {
247 if (groupName == null || groupName.trim().isEmpty()) {
248 return new Status(StatusCode.BADREQUEST, "Invalid group name");
251 Set<T> group = resourceGroups.get(groupName);
252 if (group != null && group.remove(resource)) {
254 resourceGroups.put(groupName, group);
255 return new Status(StatusCode.SUCCESS, "Resource removed successfully");
258 return new Status(StatusCode.NOTFOUND, "Group/Resource not found");
263 * Relay the call to user manager
266 public UserLevel getUserLevel(String userName) {
267 IUserManager userManager = (IUserManager) ServiceHelper
268 .getGlobalInstance(IUserManager.class, this);
270 if (logger.isDebugEnabled()) {
271 logger.debug("User {} has UserLevel {}", userName, userManager.getUserLevel(userName));
274 return (userManager == null) ? UserLevel.NOUSER : userManager
275 .getUserLevel(userName);
279 public Set<Resource> getAllResourcesforUser(String userName) {
280 Set<Resource> resources = new HashSet<Resource>();
281 IUserManager userManager = (IUserManager) ServiceHelper
282 .getGlobalInstance(IUserManager.class, this);
283 if (userManager == null) {
284 return new HashSet<Resource>(0);
287 // Get the roles associated with this user
288 List<String> roles = userManager.getUserRoles(userName);
293 for (String role : roles) {
294 // Get our resource groups associated with this role
295 List<ResourceGroup> groups = this.getAuthorizedGroups(role);
296 if (groups.isEmpty()) {
299 for (ResourceGroup group : groups) {
300 // Get the list of resources in this group
301 List<Object> list = this.getResources(group.getGroupName());
302 if (list.isEmpty()) {
305 for (Object resource : list) {
306 Resource toBeAdded = new Resource(resource,
307 group.getPrivilege());
309 * Add the resource to the set if the same resource with
310 * higher privilege is not present. If the same resource
311 * with lower privilege is present, remove it. No check on
312 * same privilege resource as set guarantees no duplicates.
314 Resource existing = higherPrivilegeResourcePresent(
315 resources, toBeAdded);
316 if (existing == null) {
317 resources.add(toBeAdded);
319 existing = lowerPrivilegeResourcePresent(resources,
321 if (existing != null) {
322 resources.remove(existing);
328 if (logger.isDebugEnabled()) {
329 logger.debug("User {} has resources {}", userName, resources);
336 public List<Resource> getAuthorizedResources(String role) {
337 Set<Resource> resources = new HashSet<Resource>();
339 // Get our resource groups associated with this role
340 List<ResourceGroup> groups = this.getAuthorizedGroups(role);
341 if (groups.isEmpty()) {
342 return new ArrayList<Resource>(0);
344 for (ResourceGroup group : groups) {
345 // Get the list of resources in this group
346 List<Object> list = this.getResources(group.getGroupName());
347 if (list.isEmpty()) {
350 for (Object resource : list) {
351 Resource toBeAdded = new Resource(resource,
352 group.getPrivilege());
354 * Add the resource to the set if the same resource with higher
355 * privilege is not present. If the same resource with lower
356 * privilege is present, remove it. No check on same privilege
357 * resource as set guarantees no duplicates.
359 Resource existing = higherPrivilegeResourcePresent(resources,
361 if (existing == null) {
362 resources.add(toBeAdded);
364 existing = lowerPrivilegeResourcePresent(resources, toBeAdded);
365 if (existing != null) {
366 resources.remove(existing);
371 if (logger.isDebugEnabled()) {
372 logger.debug("For the Role {}, Authorized Resources are {}", role, resources);
375 return new ArrayList<Resource>(resources);
379 * Given a set of resources and a resource to test, it returns the element
380 * in the set which represent the same resource with a lower privilege
381 * associated to it, if present.
387 private Resource lowerPrivilegeResourcePresent(Set<Resource> resources,
390 if (resource == null || resources == null) {
394 Object resourceElement = resource.getResource();
395 Privilege resourcePrivilege = resource.getPrivilege();
396 for (Resource element : resources) {
397 if (element.getResource().equals(resourceElement)
398 && element.getPrivilege().ordinal() < resourcePrivilege
407 * Given a set of resources and a resource to test, it returns the element
408 * in the set which represents the same resource with an higher privilege,
415 private Resource higherPrivilegeResourcePresent(Set<Resource> resources,
418 if (resource == null || resources == null) {
422 Object resourceElement = resource.getResource();
423 Privilege resourcePrivilege = resource.getPrivilege();
424 for (Resource element : resources) {
425 if (element.getResource().equals(resourceElement)
426 && element.getPrivilege().ordinal() > resourcePrivilege
436 public Privilege getResourcePrivilege(String userName, Object resource) {
438 if (userName == null || userName.trim().isEmpty() || resource == null) {
439 return Privilege.NONE;
442 Set<Resource> hisResources = getAllResourcesforUser(userName);
443 for (Resource element : hisResources) {
444 if (element.getResource().equals(resource)) {
445 return element.getPrivilege();
449 return Privilege.NONE;
453 public List<String> getResourceGroups() {
454 return new ArrayList<String>(resourceGroups.keySet());
458 public Status assignResourceGroupToRole(String group, Privilege privilege,
460 if (group == null || group.trim().isEmpty()) {
461 return new Status(StatusCode.BADREQUEST, "Invalid group name");
463 if (role == null || role.trim().isEmpty()) {
464 return new Status(StatusCode.BADREQUEST, "Role name can't be empty");
466 if (isControllerRole(role)) {
467 return new Status(StatusCode.NOTALLOWED,
468 "No group assignment is accepted for Controller roles");
471 return assignResourceGroupToRoleInternal(group, privilege, role);
474 protected Status assignResourceGroupToRoleInternal(String group, Privilege privilege, String role) {
475 Set<ResourceGroup> roleGroups = groupsAuthorizations.get(role);
476 roleGroups.add(new ResourceGroup(group, privilege));
478 groupsAuthorizations.put(role, roleGroups);
479 return new Status(StatusCode.SUCCESS);
483 public Status assignResourceGroupToRole(String groupName, String roleName) {
484 if (groupName == null || groupName.trim().isEmpty()) {
485 return new Status(StatusCode.BADREQUEST, "Group name can't be empty");
487 if (roleName == null || roleName.trim().isEmpty()) {
488 return new Status(StatusCode.BADREQUEST, "Role name can't be empty");
490 // Infer group privilege from role's level
491 Privilege privilege = Privilege.NONE;
492 switch (this.getApplicationRoleLevel(roleName)) {
494 privilege = Privilege.WRITE;
497 privilege = Privilege.USE;
500 privilege = Privilege.READ;
505 return this.assignResourceGroupToRole(groupName, privilege, roleName);
509 public Status unassignResourceGroupFromRole(String group, String role) {
510 if (group == null || group.trim().isEmpty()) {
511 return new Status(StatusCode.BADREQUEST, "Group name can't be empty");
513 if (role == null || role.trim().isEmpty()) {
514 return new Status(StatusCode.BADREQUEST, "Role name can't be empty");
516 if (isControllerRole(role)) {
517 return new Status(StatusCode.NOTALLOWED,
518 "No group assignment change is allowed for "
519 + "Controller roles");
522 return unassignResourceGroupFromRoleInternal(group, role);
525 protected Status unassignResourceGroupFromRoleInternal(String group, String role) {
526 ResourceGroup target = null;
527 for (ResourceGroup rGroup : groupsAuthorizations.get(role)) {
528 if (rGroup.getGroupName().equals(group)) {
533 if (target == null) {
534 return new Status(StatusCode.SUCCESS, "Group " + group + " was not assigned to " + role);
536 Set<ResourceGroup> groups = groupsAuthorizations.get(role);
537 groups.remove(target);
539 groupsAuthorizations.put(role, groups);
540 return new Status(StatusCode.SUCCESS);
546 public List<ResourceGroup> getAuthorizedGroups(String role) {
547 return (groupsAuthorizations.containsKey(role)) ? new ArrayList<ResourceGroup>(
548 groupsAuthorizations.get(role))
549 : new ArrayList<ResourceGroup>();
553 public List<Object> getResources(String groupName) {
554 return (resourceGroups.containsKey(groupName)) ? new ArrayList<Object>(
555 resourceGroups.get(groupName)) : new ArrayList<Object>(0);
559 public boolean isApplicationRole(String roleName) {
560 if (roleName == null) {
563 return roles.containsKey(roleName);
567 public boolean isApplicationUser(String userName) {
568 IUserManager userManager = (IUserManager) ServiceHelper
569 .getGlobalInstance(IUserManager.class, this);
570 if (userManager == null) {
573 List<String> roles = userManager.getUserRoles(userName);
574 if (roles != null && !roles.isEmpty()) {
575 for (String role : roles) {
576 if (isApplicationRole(role)) {
585 public AppRoleLevel getApplicationRoleLevel(String roleName) {
586 if (roleName == null || roleName.trim().isEmpty()) {
587 return AppRoleLevel.NOUSER;
590 if (isControllerRole(roleName)) {
591 if (roleName.equals(UserLevel.NETWORKADMIN.toString()) ||
592 roleName.equals(UserLevel.SYSTEMADMIN.toString())) {
593 return AppRoleLevel.APPADMIN;
595 return AppRoleLevel.APPOPERATOR;
599 return (roles.containsKey(roleName)) ? roles.get(roleName)
600 : AppRoleLevel.NOUSER;
604 public AppRoleLevel getUserApplicationLevel(String userName) {
605 List<String> roles = null;
606 IUserManager userManager = (IUserManager) ServiceHelper
607 .getGlobalInstance(IUserManager.class, this);
609 if (userName == null || userName.trim().isEmpty()
610 || (userManager == null)
611 || (roles = userManager.getUserRoles(userName)).isEmpty()) {
612 return AppRoleLevel.NOUSER;
614 AppRoleLevel highestLevel = AppRoleLevel.NOUSER;
615 for (String role : roles) {
616 AppRoleLevel level = getApplicationRoleLevel(role);
617 if (level.ordinal() < highestLevel.ordinal()) {
618 highestLevel = level;
625 * Returns the highest role the specified user has in this application
630 * @return The highest role associated to the user in this application
633 public String getHighestUserRole(String user) {
634 IUserManager userManager = (IUserManager) ServiceHelper.getGlobalInstance(IUserManager.class, this);
635 String highestRole = "";
636 if (userManager != null && !userManager.getUserRoles(user).isEmpty()) {
637 List<String> roles = userManager.getUserRoles(user);
638 AppRoleLevel highestLevel = AppRoleLevel.NOUSER;
639 for (String role : roles) {
640 AppRoleLevel current;
641 if (isApplicationRole(role)
642 && (current = getApplicationRoleLevel(role)).ordinal() < highestLevel.ordinal()) {
644 highestLevel = current;
651 private boolean isControllerRole(String role) {
652 return (role.equals(UserLevel.NETWORKADMIN.toString())
653 || role.equals(UserLevel.SYSTEMADMIN.toString()) || role
654 .equals(UserLevel.NETWORKOPERATOR.toString()));
657 private boolean isContainerRole(String role) {
658 IContainerAuthorization containerAuth = (IContainerAuthorization) ServiceHelper.getGlobalInstance(
659 IContainerAuthorization.class, this);
660 if (containerAuth == null) {
663 return containerAuth.isApplicationRole(role);
666 private boolean isRoleInUse(String role) {
667 IUserManager userManager = (IUserManager) ServiceHelper
668 .getGlobalInstance(IUserManager.class, this);
670 if (userManager == null) {
673 return userManager.isRoleInUse(role);