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;
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 (isRoleInUse(role)) {
71 return new Status(StatusCode.CONFLICT, "Role already in use");
73 if (roles.containsKey(role)) {
74 if (roles.get(role).equals(level)) {
75 return new Status(StatusCode.SUCCESS, "Role is already present");
77 return new Status(StatusCode.BADREQUEST,
78 "Role exists and has different level");
82 return createRoleInternal(role, level);
85 protected Status createRoleInternal(String role, AppRoleLevel level) {
86 roles.put(role, level);
87 groupsAuthorizations.put(role, new HashSet<ResourceGroup>());
88 return new Status(StatusCode.SUCCESS, null);
92 public Status removeRole(String role) {
93 if (role == null || role.trim().isEmpty()) {
94 return new Status(StatusCode.BADREQUEST, "Role name can't be empty");
96 if (isControllerRole(role)) {
97 return new Status(StatusCode.NOTALLOWED,
98 "Controller roles cannot be removed");
101 return removeRoleInternal(role);
104 protected Status removeRoleInternal(String role) {
105 groupsAuthorizations.remove(role);
107 return new Status(StatusCode.SUCCESS, null);
111 public List<String> getRoles() {
112 return new ArrayList<String>(groupsAuthorizations.keySet());
115 @SuppressWarnings("unchecked")
117 public Status createResourceGroup(String groupName, List<Object> resources) {
118 //verify group name not null/empty
119 if (groupName == null || groupName.trim().isEmpty()
120 || !groupName.matches(Authorization.namesRegex)) {
121 return new Status(StatusCode.BADREQUEST, "Group name must start with alphanumeric, no special characters");
123 //verify group name is not same as all-resources
124 if (groupName.equals(this.allResourcesGroupName)) {
125 return new Status(StatusCode.NOTALLOWED, "All resource group cannot be created");
127 //verify group name is unique
128 if (resourceGroups.containsKey(groupName)) {
129 return new Status(StatusCode.CONFLICT, "Group name already exists");
132 //try adding resources, discard if not of type T
133 Set<T> toBeAdded = new HashSet<T>();
134 boolean allAdded = true;
135 for (Object obj : resources) {
137 toBeAdded.add((T) obj);
138 } catch (ClassCastException e) {
142 /*if (toBeAdded.size() == 0){ // TODO andrekim - we should have the ability to create a group with no resources (so we can add and delete entries)
143 return new Status(StatusCode.NOTACCEPTABLE, "Group not created. No valid resources specified");
145 resourceGroups.put(groupName, toBeAdded);
146 return (allAdded ? new Status(StatusCode.SUCCESS, "All resources added succesfully") :
147 new Status(StatusCode.SUCCESS, "One or more resources couldn't be added"));
150 public Status addResourceToGroup(String groupName, T resource) {
151 if (groupName == null || groupName.trim().isEmpty()) {
152 return new Status(StatusCode.BADREQUEST, "Invalid group name");
155 Set<T> group = resourceGroups.get(groupName);
156 if (group != null && resource != null) {
158 return new Status(StatusCode.SUCCESS, "Resource added successfully");
161 return new Status(StatusCode.NOTFOUND, "Group not found or incompatible resource");
164 public Status removeRoleResourceGroupMapping(String groupName) {
165 List<String> affectedRoles = new ArrayList<String>();
167 for (Entry<String, Set<ResourceGroup>> pairs : groupsAuthorizations
169 String role = pairs.getKey();
170 Set<ResourceGroup> groups = pairs.getValue();
171 for (ResourceGroup group : groups) {
172 if (group.getGroupName().equals(groupName)) {
173 affectedRoles.add(role);
178 StringBuffer msg = new StringBuffer();
179 for (String role : affectedRoles) {
180 result = unassignResourceGroupFromRole(groupName, role);
181 if (!result.isSuccess()) {
182 msg.append(result.getDescription());
187 if (msg.length() != 0) {
188 return new Status(StatusCode.BADREQUEST, msg.toString());
190 return new Status(StatusCode.SUCCESS);
195 public Status removeResourceGroup(String groupName) {
196 // Default resource group cannot be deleted
197 if (groupName == null || groupName.trim().isEmpty()) {
198 return new Status(StatusCode.BADREQUEST, "Invalid group name");
200 if (groupName.equals(this.allResourcesGroupName)) {
201 return new Status(StatusCode.NOTALLOWED,
202 "All resource group cannot be removed");
205 Status result = removeRoleResourceGroupMapping(groupName);
207 resourceGroups.remove(groupName);
209 if (!result.isSuccess()) {
213 return new Status(StatusCode.SUCCESS, null);
217 public Status removeResourceFromGroup(String groupName, T resource) {
218 if (groupName == null || groupName.trim().isEmpty()) {
219 return new Status(StatusCode.BADREQUEST, "Invalid group name");
222 Set<T> group = resourceGroups.get(groupName);
223 if (group != null && group.remove(resource)) {
224 return new Status(StatusCode.SUCCESS, "Resource removed successfully");
227 return new Status(StatusCode.NOTFOUND, "Group/Resource not found");
232 * Relay the call to user manager
235 public UserLevel getUserLevel(String userName) {
236 IUserManager userManager = (IUserManager) ServiceHelper
237 .getGlobalInstance(IUserManager.class, this);
239 if (logger.isDebugEnabled()) {
240 logger.debug("User {} has UserLevel {}", userName, userManager.getUserLevel(userName));
243 return (userManager == null) ? UserLevel.NOUSER : userManager
244 .getUserLevel(userName);
248 public Set<Resource> getAllResourcesforUser(String userName) {
249 Set<Resource> resources = new HashSet<Resource>();
250 IUserManager userManager = (IUserManager) ServiceHelper
251 .getGlobalInstance(IUserManager.class, this);
252 if (userManager == null) {
253 return new HashSet<Resource>(0);
256 // Get the roles associated with this user
257 List<String> roles = userManager.getUserRoles(userName);
262 for (String role : roles) {
263 // Get our resource groups associated with this role
264 List<ResourceGroup> groups = this.getAuthorizedGroups(role);
265 if (groups.isEmpty()) {
268 for (ResourceGroup group : groups) {
269 // Get the list of resources in this group
270 List<Object> list = this.getResources(group.getGroupName());
271 if (list.isEmpty()) {
274 for (Object resource : list) {
275 Resource toBeAdded = new Resource(resource,
276 group.getPrivilege());
278 * Add the resource to the set if the same resource with
279 * higher privilege is not present. If the same resource
280 * with lower privilege is present, remove it. No check on
281 * same privilege resource as set guarantees no duplicates.
283 Resource existing = higherPrivilegeResourcePresent(
284 resources, toBeAdded);
285 if (existing == null) {
286 resources.add(toBeAdded);
288 existing = lowerPrivilegeResourcePresent(resources,
290 if (existing != null) {
291 resources.remove(existing);
297 if (logger.isDebugEnabled()) {
298 logger.debug("User {} has resources {}", userName, resources);
305 public List<Resource> getAuthorizedResources(String role) {
306 Set<Resource> resources = new HashSet<Resource>();
308 // Get our resource groups associated with this role
309 List<ResourceGroup> groups = this.getAuthorizedGroups(role);
310 if (groups.isEmpty()) {
311 return new ArrayList<Resource>(0);
313 for (ResourceGroup group : groups) {
314 // Get the list of resources in this group
315 List<Object> list = this.getResources(group.getGroupName());
316 if (list.isEmpty()) {
319 for (Object resource : list) {
320 Resource toBeAdded = new Resource(resource,
321 group.getPrivilege());
323 * Add the resource to the set if the same resource with higher
324 * privilege is not present. If the same resource with lower
325 * privilege is present, remove it. No check on same privilege
326 * resource as set guarantees no duplicates.
328 Resource existing = higherPrivilegeResourcePresent(resources,
330 if (existing == null) {
331 resources.add(toBeAdded);
333 existing = lowerPrivilegeResourcePresent(resources, toBeAdded);
334 if (existing != null) {
335 resources.remove(existing);
340 if (logger.isDebugEnabled()) {
341 logger.debug("For the Role {}, Authorized Resources are {}", role, resources);
344 return new ArrayList<Resource>(resources);
348 * Given a set of resources and a resource to test, it returns the element
349 * in the set which represent the same resource with a lower privilege
350 * associated to it, if present.
356 private Resource lowerPrivilegeResourcePresent(Set<Resource> resources,
359 if (resource == null || resources == null) {
363 Object resourceElement = resource.getResource();
364 Privilege resourcePrivilege = resource.getPrivilege();
365 for (Resource element : resources) {
366 if (element.getResource().equals(resourceElement)
367 && element.getPrivilege().ordinal() < resourcePrivilege
376 * Given a set of resources and a resource to test, it returns the element
377 * in the set which represents the same resource with an higher privilege,
384 private Resource higherPrivilegeResourcePresent(Set<Resource> resources,
387 if (resource == null || resources == null) {
391 Object resourceElement = resource.getResource();
392 Privilege resourcePrivilege = resource.getPrivilege();
393 for (Resource element : resources) {
394 if (element.getResource().equals(resourceElement)
395 && element.getPrivilege().ordinal() > resourcePrivilege
405 public Privilege getResourcePrivilege(String userName, Object resource) {
407 if (userName == null || userName.trim().isEmpty() || resource == null) {
408 return Privilege.NONE;
411 Set<Resource> hisResources = getAllResourcesforUser(userName);
412 for (Resource element : hisResources) {
413 if (element.getResource().equals(resource)) {
414 return element.getPrivilege();
418 return Privilege.NONE;
422 public List<String> getResourceGroups() {
423 return new ArrayList<String>(resourceGroups.keySet());
427 public Status assignResourceGroupToRole(String group, Privilege privilege,
429 if (group == null || group.trim().isEmpty()) {
430 return new Status(StatusCode.BADREQUEST, "Invalid group name");
432 if (role == null || role.trim().isEmpty()) {
433 return new Status(StatusCode.BADREQUEST, "Role name can't be empty");
435 if (isControllerRole(role)) {
436 return new Status(StatusCode.NOTALLOWED,
437 "No group assignment is accepted for Controller roles");
440 return assignResourceGroupToRoleInternal(group, privilege, role);
443 protected Status assignResourceGroupToRoleInternal(String group,
444 Privilege privilege, String role) {
445 groupsAuthorizations.get(role).add(new ResourceGroup(group, privilege));
446 return new Status(StatusCode.SUCCESS, null);
450 public Status assignResourceGroupToRole(String groupName, String roleName) {
451 if (groupName == null || groupName.trim().isEmpty()) {
452 return new Status(StatusCode.BADREQUEST, "Group name can't be empty");
454 if (roleName == null || roleName.trim().isEmpty()) {
455 return new Status(StatusCode.BADREQUEST, "Role name can't be empty");
457 // Infer group privilege from role's level
458 Privilege privilege = Privilege.NONE;
459 switch (this.getApplicationRoleLevel(roleName)) {
461 privilege = Privilege.WRITE;
464 privilege = Privilege.USE;
467 privilege = Privilege.READ;
472 return this.assignResourceGroupToRole(groupName, privilege, roleName);
476 public Status unassignResourceGroupFromRole(String group, String role) {
477 if (group == null || group.trim().isEmpty()) {
478 return new Status(StatusCode.BADREQUEST, "Group name can't be empty");
480 if (role == null || role.trim().isEmpty()) {
481 return new Status(StatusCode.BADREQUEST, "Role name can't be empty");
483 if (isControllerRole(role)) {
484 return new Status(StatusCode.NOTALLOWED,
485 "No group assignment change is allowed for "
486 + "Controller roles");
489 return unassignResourceGroupFromRoleInternal(group, role);
492 protected Status unassignResourceGroupFromRoleInternal(String group,
494 ResourceGroup target = null;
495 for (ResourceGroup rGroup : groupsAuthorizations.get(role)) {
496 if (rGroup.getGroupName().equals(group)) {
501 if (target == null) {
502 return new Status(StatusCode.SUCCESS, "Group " + group
503 + " was not assigned to " + role);
505 groupsAuthorizations.get(role).remove(target);
506 return new Status(StatusCode.SUCCESS);
512 public List<ResourceGroup> getAuthorizedGroups(String role) {
513 return (groupsAuthorizations.containsKey(role)) ? new ArrayList<ResourceGroup>(
514 groupsAuthorizations.get(role))
515 : new ArrayList<ResourceGroup>();
519 public List<Object> getResources(String groupName) {
520 return (resourceGroups.containsKey(groupName)) ? new ArrayList<Object>(
521 resourceGroups.get(groupName)) : new ArrayList<Object>();
525 public boolean isApplicationRole(String roleName) {
526 if (roleName == null) {
529 return roles.containsKey(roleName);
533 public AppRoleLevel getApplicationRoleLevel(String roleName) {
534 if (roleName == null || roleName.trim().isEmpty()) {
535 return AppRoleLevel.NOUSER;
538 if (isControllerRole(roleName)) {
539 if (roleName.equals(UserLevel.NETWORKADMIN.toString()) ||
540 roleName.equals(UserLevel.SYSTEMADMIN.toString())) {
541 return AppRoleLevel.APPADMIN;
543 return AppRoleLevel.APPOPERATOR;
547 return (roles.containsKey(roleName)) ? roles.get(roleName)
548 : AppRoleLevel.NOUSER;
552 public AppRoleLevel getUserApplicationLevel(String userName) {
553 List<String> roles = null;
554 IUserManager userManager = (IUserManager) ServiceHelper
555 .getGlobalInstance(IUserManager.class, this);
557 if (userName == null || userName.trim().isEmpty()
558 || (userManager == null)
559 || (roles = userManager.getUserRoles(userName)).isEmpty()) {
560 return AppRoleLevel.NOUSER;
562 AppRoleLevel highestLevel = AppRoleLevel.NOUSER;
563 for (String role : roles) {
564 AppRoleLevel level = getApplicationRoleLevel(role);
565 if (level.ordinal() < highestLevel.ordinal()) {
566 highestLevel = level;
573 * Returns the highest role the specified user has in this application
578 * @return The highest role associated to the user in this application
581 public String getHighestUserRole(String user) {
582 IUserManager userManager = (IUserManager) ServiceHelper.getGlobalInstance(IUserManager.class, this);
583 String highestRole = "";
584 if (userManager != null && !userManager.getUserRoles(user).isEmpty()) {
585 List<String> roles = userManager.getUserRoles(user);
586 AppRoleLevel highestLevel = AppRoleLevel.NOUSER;
587 for (String role : roles) {
588 AppRoleLevel current;
589 if (isApplicationRole(role)
590 && (current = getApplicationRoleLevel(role)).ordinal() < highestLevel.ordinal()) {
592 highestLevel = current;
599 private boolean isControllerRole(String role) {
600 return (role.equals(UserLevel.NETWORKADMIN.toString())
601 || role.equals(UserLevel.SYSTEMADMIN.toString()) || role
602 .equals(UserLevel.NETWORKOPERATOR.toString()));
605 private boolean isRoleInUse(String role) {
606 IUserManager userManager = (IUserManager) ServiceHelper
607 .getGlobalInstance(IUserManager.class, this);
609 if (userManager == null) {
612 return userManager.isRoleInUse(role);