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.sal.authorization.AppRoleLevel;
20 import org.opendaylight.controller.sal.authorization.IResourceAuthorization;
21 import org.opendaylight.controller.sal.authorization.Privilege;
22 import org.opendaylight.controller.sal.authorization.Resource;
23 import org.opendaylight.controller.sal.authorization.ResourceGroup;
24 import org.opendaylight.controller.sal.authorization.UserLevel;
25 import org.opendaylight.controller.sal.utils.ServiceHelper;
26 import org.opendaylight.controller.sal.utils.Status;
27 import org.opendaylight.controller.sal.utils.StatusCode;
28 import org.opendaylight.controller.usermanager.IUserManager;
29 import org.slf4j.Logger;
30 import org.slf4j.LoggerFactory;
33 * This abstract class implements the methods defined by IResourceAuthorization
34 * interface for each application class.
36 public abstract class Authorization<T> implements IResourceAuthorization {
37 private static final Logger logger = LoggerFactory.getLogger(Authorization.class);
38 private static final String namesRegex = "^[a-zA-Z0-9]+[{\\.|\\_|\\-}[a-zA-Z0-9]]*$";
40 * The configured resource groups
42 protected ConcurrentMap<String, Set<T>> resourceGroups;
44 * The configured roles along with their level
46 protected ConcurrentMap<String, AppRoleLevel> roles;
48 * The association of groups to roles
50 protected ConcurrentMap<String, Set<ResourceGroup>> groupsAuthorizations;
52 * The name of the default group. It is the group which contains all the
55 protected String allResourcesGroupName;
58 public Status createRole(String role, AppRoleLevel level) {
59 if (role == null || role.trim().isEmpty()
60 || !role.matches(Authorization.namesRegex)) {
61 return new Status(StatusCode.BADREQUEST,
62 "Role name must start with alphanumeric, no special characters.");
64 if (isControllerRole(role)) {
65 return new Status(StatusCode.NOTALLOWED,
66 "Controller roles cannot be explicitely "
67 + "created in App context");
69 if (isRoleInUse(role)) {
70 return new Status(StatusCode.CONFLICT, "Role already in use");
72 if (roles.containsKey(role)) {
73 if (roles.get(role).equals(level)) {
74 return new Status(StatusCode.SUCCESS, "Role is already present");
76 return new Status(StatusCode.BADREQUEST,
77 "Role exists and has different level");
81 return createRoleInternal(role, level);
84 protected Status createRoleInternal(String role, AppRoleLevel level) {
85 roles.put(role, level);
86 groupsAuthorizations.put(role, new HashSet<ResourceGroup>());
87 return new Status(StatusCode.SUCCESS);
91 public Status removeRole(String role) {
92 if (role == null || role.trim().isEmpty()) {
93 return new Status(StatusCode.BADREQUEST, "Role name can't be empty");
95 if (isControllerRole(role)) {
96 return new Status(StatusCode.NOTALLOWED,
97 "Controller roles cannot be removed");
100 return removeRoleInternal(role);
103 protected Status removeRoleInternal(String role) {
104 groupsAuthorizations.remove(role);
106 return new Status(StatusCode.SUCCESS);
110 public List<String> getRoles() {
111 return new ArrayList<String>(groupsAuthorizations.keySet());
114 @SuppressWarnings("unchecked")
116 public Status createResourceGroup(String groupName, List<Object> resources) {
117 //verify group name not null/empty
118 if (groupName == null || groupName.trim().isEmpty()
119 || !groupName.matches(Authorization.namesRegex)) {
120 return new Status(StatusCode.BADREQUEST, "Group name must start with alphanumeric, no special characters");
122 //verify group name is not same as all-resources
123 if (groupName.equals(this.allResourcesGroupName)) {
124 return new Status(StatusCode.NOTALLOWED, "All resource group cannot be created");
126 //verify group name is unique
127 if (resourceGroups.containsKey(groupName)) {
128 return new Status(StatusCode.CONFLICT, "Group name already exists");
131 //try adding resources, discard if not of type T
132 Set<T> toBeAdded = new HashSet<T>();
133 boolean allAdded = true;
134 for (Object obj : resources) {
136 toBeAdded.add((T) obj);
137 } catch (ClassCastException e) {
141 resourceGroups.put(groupName, toBeAdded);
142 return (allAdded ? new Status(StatusCode.SUCCESS, "All resources added succesfully") :
143 new Status(StatusCode.SUCCESS, "One or more resources couldn't be added"));
146 public Status addResourceToGroup(String groupName, T resource) {
147 if (groupName == null || groupName.trim().isEmpty()) {
148 return new Status(StatusCode.BADREQUEST, "Invalid group name");
151 Set<T> group = resourceGroups.get(groupName);
152 if (group != null && resource != null) {
155 resourceGroups.put(groupName, group);
156 return new Status(StatusCode.SUCCESS, "Resource added successfully");
159 return new Status(StatusCode.NOTFOUND, "Group not found or incompatible resource");
162 public Status removeRoleResourceGroupMapping(String groupName) {
163 List<String> affectedRoles = new ArrayList<String>();
165 for (Entry<String, Set<ResourceGroup>> pairs : groupsAuthorizations.entrySet()) {
166 String role = pairs.getKey();
167 Set<ResourceGroup> groups = pairs.getValue();
168 for (ResourceGroup group : groups) {
169 if (group.getGroupName().equals(groupName)) {
170 affectedRoles.add(role);
175 StringBuffer msg = new StringBuffer();
176 for (String role : affectedRoles) {
177 result = unassignResourceGroupFromRole(groupName, role);
178 if (!result.isSuccess()) {
179 msg.append(result.getDescription());
184 if (msg.length() != 0) {
185 return new Status(StatusCode.BADREQUEST, msg.toString());
187 return new Status(StatusCode.SUCCESS);
192 public Status removeResourceGroup(String groupName) {
193 // Default resource group cannot be deleted
194 if (groupName == null || groupName.trim().isEmpty()) {
195 return new Status(StatusCode.BADREQUEST, "Invalid group name");
197 if (groupName.equals(this.allResourcesGroupName)) {
198 return new Status(StatusCode.NOTALLOWED,
199 "All resource group cannot be removed");
201 resourceGroups.remove(groupName);
202 Status result = removeRoleResourceGroupMapping(groupName);
204 return result.isSuccess() ? result :
205 new Status(StatusCode.SUCCESS, "Failed removing group from: " + result.getDescription());
209 public Status removeResourceFromGroup(String groupName, T resource) {
210 if (groupName == null || groupName.trim().isEmpty()) {
211 return new Status(StatusCode.BADREQUEST, "Invalid group name");
214 Set<T> group = resourceGroups.get(groupName);
215 if (group != null && group.remove(resource)) {
217 resourceGroups.put(groupName, group);
218 return new Status(StatusCode.SUCCESS, "Resource removed successfully");
221 return new Status(StatusCode.NOTFOUND, "Group/Resource not found");
226 * Relay the call to user manager
229 public UserLevel getUserLevel(String userName) {
230 IUserManager userManager = (IUserManager) ServiceHelper
231 .getGlobalInstance(IUserManager.class, this);
233 if (logger.isDebugEnabled()) {
234 logger.debug("User {} has UserLevel {}", userName, userManager.getUserLevel(userName));
237 return (userManager == null) ? UserLevel.NOUSER : userManager
238 .getUserLevel(userName);
242 public Set<Resource> getAllResourcesforUser(String userName) {
243 Set<Resource> resources = new HashSet<Resource>();
244 IUserManager userManager = (IUserManager) ServiceHelper
245 .getGlobalInstance(IUserManager.class, this);
246 if (userManager == null) {
247 return new HashSet<Resource>(0);
250 // Get the roles associated with this user
251 List<String> roles = userManager.getUserRoles(userName);
256 for (String role : roles) {
257 // Get our resource groups associated with this role
258 List<ResourceGroup> groups = this.getAuthorizedGroups(role);
259 if (groups.isEmpty()) {
262 for (ResourceGroup group : groups) {
263 // Get the list of resources in this group
264 List<Object> list = this.getResources(group.getGroupName());
265 if (list.isEmpty()) {
268 for (Object resource : list) {
269 Resource toBeAdded = new Resource(resource,
270 group.getPrivilege());
272 * Add the resource to the set if the same resource with
273 * higher privilege is not present. If the same resource
274 * with lower privilege is present, remove it. No check on
275 * same privilege resource as set guarantees no duplicates.
277 Resource existing = higherPrivilegeResourcePresent(
278 resources, toBeAdded);
279 if (existing == null) {
280 resources.add(toBeAdded);
282 existing = lowerPrivilegeResourcePresent(resources,
284 if (existing != null) {
285 resources.remove(existing);
291 if (logger.isDebugEnabled()) {
292 logger.debug("User {} has resources {}", userName, resources);
299 public List<Resource> getAuthorizedResources(String role) {
300 Set<Resource> resources = new HashSet<Resource>();
302 // Get our resource groups associated with this role
303 List<ResourceGroup> groups = this.getAuthorizedGroups(role);
304 if (groups.isEmpty()) {
305 return new ArrayList<Resource>(0);
307 for (ResourceGroup group : groups) {
308 // Get the list of resources in this group
309 List<Object> list = this.getResources(group.getGroupName());
310 if (list.isEmpty()) {
313 for (Object resource : list) {
314 Resource toBeAdded = new Resource(resource,
315 group.getPrivilege());
317 * Add the resource to the set if the same resource with higher
318 * privilege is not present. If the same resource with lower
319 * privilege is present, remove it. No check on same privilege
320 * resource as set guarantees no duplicates.
322 Resource existing = higherPrivilegeResourcePresent(resources,
324 if (existing == null) {
325 resources.add(toBeAdded);
327 existing = lowerPrivilegeResourcePresent(resources, toBeAdded);
328 if (existing != null) {
329 resources.remove(existing);
334 if (logger.isDebugEnabled()) {
335 logger.debug("For the Role {}, Authorized Resources are {}", role, resources);
338 return new ArrayList<Resource>(resources);
342 * Given a set of resources and a resource to test, it returns the element
343 * in the set which represent the same resource with a lower privilege
344 * associated to it, if present.
350 private Resource lowerPrivilegeResourcePresent(Set<Resource> resources,
353 if (resource == null || resources == null) {
357 Object resourceElement = resource.getResource();
358 Privilege resourcePrivilege = resource.getPrivilege();
359 for (Resource element : resources) {
360 if (element.getResource().equals(resourceElement)
361 && element.getPrivilege().ordinal() < resourcePrivilege
370 * Given a set of resources and a resource to test, it returns the element
371 * in the set which represents the same resource with an higher privilege,
378 private Resource higherPrivilegeResourcePresent(Set<Resource> resources,
381 if (resource == null || resources == null) {
385 Object resourceElement = resource.getResource();
386 Privilege resourcePrivilege = resource.getPrivilege();
387 for (Resource element : resources) {
388 if (element.getResource().equals(resourceElement)
389 && element.getPrivilege().ordinal() > resourcePrivilege
399 public Privilege getResourcePrivilege(String userName, Object resource) {
401 if (userName == null || userName.trim().isEmpty() || resource == null) {
402 return Privilege.NONE;
405 Set<Resource> hisResources = getAllResourcesforUser(userName);
406 for (Resource element : hisResources) {
407 if (element.getResource().equals(resource)) {
408 return element.getPrivilege();
412 return Privilege.NONE;
416 public List<String> getResourceGroups() {
417 return new ArrayList<String>(resourceGroups.keySet());
421 public Status assignResourceGroupToRole(String group, Privilege privilege,
423 if (group == null || group.trim().isEmpty()) {
424 return new Status(StatusCode.BADREQUEST, "Invalid group name");
426 if (role == null || role.trim().isEmpty()) {
427 return new Status(StatusCode.BADREQUEST, "Role name can't be empty");
429 if (isControllerRole(role)) {
430 return new Status(StatusCode.NOTALLOWED,
431 "No group assignment is accepted for Controller roles");
434 return assignResourceGroupToRoleInternal(group, privilege, role);
437 protected Status assignResourceGroupToRoleInternal(String group, Privilege privilege, String role) {
438 Set<ResourceGroup> roleGroups = groupsAuthorizations.get(role);
439 roleGroups.add(new ResourceGroup(group, privilege));
441 groupsAuthorizations.put(role, roleGroups);
442 return new Status(StatusCode.SUCCESS);
446 public Status assignResourceGroupToRole(String groupName, String roleName) {
447 if (groupName == null || groupName.trim().isEmpty()) {
448 return new Status(StatusCode.BADREQUEST, "Group name can't be empty");
450 if (roleName == null || roleName.trim().isEmpty()) {
451 return new Status(StatusCode.BADREQUEST, "Role name can't be empty");
453 // Infer group privilege from role's level
454 Privilege privilege = Privilege.NONE;
455 switch (this.getApplicationRoleLevel(roleName)) {
457 privilege = Privilege.WRITE;
460 privilege = Privilege.USE;
463 privilege = Privilege.READ;
468 return this.assignResourceGroupToRole(groupName, privilege, roleName);
472 public Status unassignResourceGroupFromRole(String group, String role) {
473 if (group == null || group.trim().isEmpty()) {
474 return new Status(StatusCode.BADREQUEST, "Group name can't be empty");
476 if (role == null || role.trim().isEmpty()) {
477 return new Status(StatusCode.BADREQUEST, "Role name can't be empty");
479 if (isControllerRole(role)) {
480 return new Status(StatusCode.NOTALLOWED,
481 "No group assignment change is allowed for "
482 + "Controller roles");
485 return unassignResourceGroupFromRoleInternal(group, role);
488 protected Status unassignResourceGroupFromRoleInternal(String group, String role) {
489 ResourceGroup target = null;
490 for (ResourceGroup rGroup : groupsAuthorizations.get(role)) {
491 if (rGroup.getGroupName().equals(group)) {
496 if (target == null) {
497 return new Status(StatusCode.SUCCESS, "Group " + group + " was not assigned to " + role);
499 Set<ResourceGroup> groups = groupsAuthorizations.get(role);
500 groups.remove(target);
502 groupsAuthorizations.put(role, groups);
503 return new Status(StatusCode.SUCCESS);
509 public List<ResourceGroup> getAuthorizedGroups(String role) {
510 return (groupsAuthorizations.containsKey(role)) ? new ArrayList<ResourceGroup>(
511 groupsAuthorizations.get(role))
512 : new ArrayList<ResourceGroup>();
516 public List<Object> getResources(String groupName) {
517 return (resourceGroups.containsKey(groupName)) ? new ArrayList<Object>(
518 resourceGroups.get(groupName)) : new ArrayList<Object>(0);
522 public boolean isApplicationRole(String roleName) {
523 if (roleName == null) {
526 return roles.containsKey(roleName);
530 public AppRoleLevel getApplicationRoleLevel(String roleName) {
531 if (roleName == null || roleName.trim().isEmpty()) {
532 return AppRoleLevel.NOUSER;
535 if (isControllerRole(roleName)) {
536 if (roleName.equals(UserLevel.NETWORKADMIN.toString()) ||
537 roleName.equals(UserLevel.SYSTEMADMIN.toString())) {
538 return AppRoleLevel.APPADMIN;
540 return AppRoleLevel.APPOPERATOR;
544 return (roles.containsKey(roleName)) ? roles.get(roleName)
545 : AppRoleLevel.NOUSER;
549 public AppRoleLevel getUserApplicationLevel(String userName) {
550 List<String> roles = null;
551 IUserManager userManager = (IUserManager) ServiceHelper
552 .getGlobalInstance(IUserManager.class, this);
554 if (userName == null || userName.trim().isEmpty()
555 || (userManager == null)
556 || (roles = userManager.getUserRoles(userName)).isEmpty()) {
557 return AppRoleLevel.NOUSER;
559 AppRoleLevel highestLevel = AppRoleLevel.NOUSER;
560 for (String role : roles) {
561 AppRoleLevel level = getApplicationRoleLevel(role);
562 if (level.ordinal() < highestLevel.ordinal()) {
563 highestLevel = level;
570 * Returns the highest role the specified user has in this application
575 * @return The highest role associated to the user in this application
578 public String getHighestUserRole(String user) {
579 IUserManager userManager = (IUserManager) ServiceHelper.getGlobalInstance(IUserManager.class, this);
580 String highestRole = "";
581 if (userManager != null && !userManager.getUserRoles(user).isEmpty()) {
582 List<String> roles = userManager.getUserRoles(user);
583 AppRoleLevel highestLevel = AppRoleLevel.NOUSER;
584 for (String role : roles) {
585 AppRoleLevel current;
586 if (isApplicationRole(role)
587 && (current = getApplicationRoleLevel(role)).ordinal() < highestLevel.ordinal()) {
589 highestLevel = current;
596 private boolean isControllerRole(String role) {
597 return (role.equals(UserLevel.NETWORKADMIN.toString())
598 || role.equals(UserLevel.SYSTEMADMIN.toString()) || role
599 .equals(UserLevel.NETWORKOPERATOR.toString()));
602 private boolean isRoleInUse(String role) {
603 IUserManager userManager = (IUserManager) ServiceHelper
604 .getGlobalInstance(IUserManager.class, this);
606 if (userManager == null) {
609 return userManager.isRoleInUse(role);