/* * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ package org.opendaylight.controller.web; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.List; import java.util.Set; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import org.opendaylight.controller.clustering.services.IClusterGlobalServices; import org.opendaylight.controller.connectionmanager.IConnectionManager; import org.opendaylight.controller.sal.authorization.UserLevel; import org.opendaylight.controller.sal.core.Description; import org.opendaylight.controller.sal.core.Node; import org.opendaylight.controller.sal.utils.GlobalConstants; import org.opendaylight.controller.sal.utils.ServiceHelper; import org.opendaylight.controller.sal.utils.Status; import org.opendaylight.controller.sal.utils.StatusCode; import org.opendaylight.controller.switchmanager.ISwitchManager; import org.opendaylight.controller.usermanager.IUserManager; import org.opendaylight.controller.usermanager.UserConfig; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import com.google.gson.Gson; @Controller @RequestMapping("/admin") public class DaylightWebAdmin { Gson gson = new Gson(); /** * Returns list of clustered controllers. Highlights "this" controller and * if controller is coordinator * * @return List */ @RequestMapping("/cluster") @ResponseBody public String getClusteredControllers() { IClusterGlobalServices clusterServices = (IClusterGlobalServices) ServiceHelper.getGlobalInstance( IClusterGlobalServices.class, this); if (clusterServices == null) { return null; } IConnectionManager connectionManager = (IConnectionManager) ServiceHelper.getGlobalInstance( IConnectionManager.class, this); if (connectionManager == null) { return null; } List clusterNodes = new ArrayList(); List controllers = clusterServices.getClusteredControllers(); for (InetAddress controller : controllers) { ClusterNodeBean.Builder clusterBeanBuilder = new ClusterNodeBean.Builder(controller); // get number of connected nodes Set connectedNodes = connectionManager.getNodes(controller); int numNodes = connectedNodes == null ? 0 : connectedNodes.size(); clusterBeanBuilder.nodesConnected(numNodes); // determine if this is the executing controller if (controller.equals(clusterServices.getMyAddress())) { clusterBeanBuilder.highlightMe(); } // determine whether this is coordinator if (clusterServices.getCoordinatorAddress().equals(controller)) { clusterBeanBuilder.iAmCoordinator(); } clusterNodes.add(clusterBeanBuilder.build()); } return gson.toJson(clusterNodes); } /** * Return nodes connected to controller {controller} * * @param controller * - byte[] of the address of the controller * @return List */ @RequestMapping("/cluster/controller/{controller}") @ResponseBody public String getNodesConnectedToController(@PathVariable("controller") String controller) { IClusterGlobalServices clusterServices = (IClusterGlobalServices) ServiceHelper.getGlobalInstance( IClusterGlobalServices.class, this); if (clusterServices == null) { return null; } IConnectionManager connectionManager = (IConnectionManager) ServiceHelper.getGlobalInstance( IConnectionManager.class, this); if (connectionManager == null) { return null; } ISwitchManager switchManager = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, GlobalConstants.DEFAULT.toString(), this); if (switchManager == null) { return null; } byte[] address = gson.fromJson(controller, byte[].class); InetAddress controllerAddress = null; try { controllerAddress = InetAddress.getByAddress(address); } catch (UnknownHostException e) { return null; } List result = new ArrayList(); Set nodes = connectionManager.getNodes(controllerAddress); if (nodes == null) { return gson.toJson(result); } for (Node node : nodes) { Description description = (Description) switchManager.getNodeProp(node, Description.propertyName); NodeBean nodeBean; if (description == null || description.getValue().equals("None")) { nodeBean = new NodeBean(node); } else { nodeBean = new NodeBean(node, description.getValue()); } result.add(nodeBean); } return gson.toJson(result); } @RequestMapping(value = "/users", method = RequestMethod.GET) @ResponseBody public List getUsers() { IUserManager userManager = (IUserManager) ServiceHelper.getGlobalInstance(IUserManager.class, this); if (userManager == null) { return null; } List result = new ArrayList(); List configs = userManager.getLocalUserList(); for (UserConfig config : configs) { UserBean bean = new UserBean(config); result.add(bean); } return result; } /* * Password in clear text, moving to HTTP/SSL soon */ @RequestMapping(value = "/users", method = RequestMethod.POST) @ResponseBody public Status saveLocalUserConfig(@RequestParam(required = true) String json, @RequestParam(required = true) String action, HttpServletRequest request) { IUserManager userManager = (IUserManager) ServiceHelper.getGlobalInstance(IUserManager.class, this); if (userManager == null) { return new Status(StatusCode.NOSERVICE, "User Manager unavailable"); } if (!authorize(userManager, UserLevel.NETWORKADMIN, request)) { return new Status(StatusCode.UNAUTHORIZED, "Operation not permitted"); } Gson gson = new Gson(); UserConfig plainConfig = gson.fromJson(json, UserConfig.class); // Recreate using the proper constructor which will hash the password UserConfig config = new UserConfig(plainConfig.getUser(), plainConfig.getPassword(), plainConfig.getRoles()); Status result = (action.equals("add")) ? userManager.addLocalUser(config) : userManager.removeLocalUser(config); if (result.isSuccess()) { if (action.equals("add")) { DaylightWebUtil.auditlog("User", request.getUserPrincipal().getName(), "added", config.getUser() + " as " + config.getRoles().toString()); } else { DaylightWebUtil.auditlog("User", request.getUserPrincipal().getName(), "removed", config.getUser()); } } return result; } @RequestMapping(value = "/user/modify", method = RequestMethod.POST) @ResponseBody public Status modifyUser(@RequestParam(required = true) String json, @RequestParam(required = true) String action, HttpServletRequest request) { IUserManager userManager = (IUserManager) ServiceHelper.getGlobalInstance(IUserManager.class, this); if (userManager == null) { return new Status(StatusCode.NOSERVICE, "User Manager unavailable"); } if (!authorize(userManager, UserLevel.NETWORKADMIN, request)) { return new Status(StatusCode.UNAUTHORIZED, "Operation not permitted"); } UserConfig newConfig = gson.fromJson(json, UserConfig.class); List currentUserConfig = userManager.getLocalUserList(); String password = null; byte[] salt = null; String user = newConfig.getUser(); for (UserConfig userConfig : currentUserConfig) { if(userConfig.getUser().equals(user)){ password = userConfig.getPassword(); salt = userConfig.getSalt(); break; } } if (password == null) { String msg = String.format("User %s not found in configuration database", user); return new Status(StatusCode.NOTFOUND, msg); } //While modifying a user role, the password is not provided from GUI for any user. //The password is stored in hash mode, hence it cannot be retrieved and added to UserConfig object //The hashed password is injected below to the json string containing username and new roles before //converting to UserConfig object. Gson gson = new Gson(); json = json.replace("\"roles\"", "\"salt\":" + gson.toJson(salt, salt.getClass()) + ",\"password\":\""+ password + "\",\"roles\""); newConfig = gson.fromJson(json, UserConfig.class); Status result = userManager.modifyLocalUser(newConfig); if (result.isSuccess()) { DaylightWebUtil.auditlog("Roles of", request.getUserPrincipal().getName(), "updated", newConfig.getUser() + " to " + newConfig.getRoles().toString()); } return result; } @RequestMapping(value = "/users/{username}", method = RequestMethod.POST) @ResponseBody public Status removeLocalUser(@PathVariable("username") String userName, HttpServletRequest request) { String loggedInUser = request.getUserPrincipal().getName(); if (loggedInUser.equals(userName)) { String msg = "Invalid Request: User cannot delete itself"; return new Status(StatusCode.NOTALLOWED, msg); } IUserManager userManager = (IUserManager) ServiceHelper.getGlobalInstance(IUserManager.class, this); if (userManager == null) { return new Status(StatusCode.NOSERVICE, "User Manager unavailable"); } if (!authorize(userManager, UserLevel.NETWORKADMIN, request)) { return new Status(StatusCode.UNAUTHORIZED, "Operation not permitted"); } Status status = userManager.removeLocalUser(userName); if (status.isSuccess()) { DaylightWebUtil.auditlog("User", request.getUserPrincipal().getName(), "removed", userName); return status; } return status; } @RequestMapping(value = "/users/password/{username}", method = RequestMethod.POST) @ResponseBody public Status changePassword( @PathVariable("username") String username, HttpServletRequest request, @RequestParam(value = "currentPassword", required=false) String currentPassword, @RequestParam("newPassword") String newPassword) { IUserManager userManager = (IUserManager) ServiceHelper.getGlobalInstance(IUserManager.class, this); if (userManager == null) { return new Status(StatusCode.NOSERVICE, "User Manager unavailable"); } Status status; String requestingUser = request.getUserPrincipal().getName(); //changing own password if (requestingUser.equals(username) ) { status = userManager.changeLocalUserPassword(username, currentPassword, newPassword); //enforce the user to re-login with new password if (status.isSuccess() && !newPassword.equals(currentPassword)) { userManager.userLogout(username); HttpSession session = request.getSession(false); if ( session != null) { session.invalidate(); } } //admin level user resetting other's password } else if (authorize(userManager, UserLevel.NETWORKADMIN, request)) { //Since User Manager doesn't have an unprotected password change API, //we re-create the user with the new password (and current roles). List roles = userManager.getUserRoles(username); UserConfig newConfig = new UserConfig(username, newPassword, roles); //validate before removing existing config, so we don't remove but fail to add status = newConfig.validate(); if (!status.isSuccess()) { return status; } userManager.userLogout(username); status = userManager.removeLocalUser(username); if (!status.isSuccess()) { return status; } if (userManager.addLocalUser(newConfig).isSuccess()) { status = new Status(StatusCode.SUCCESS, "Password for user " + username + " reset successfully."); } else { //unexpected status = new Status(StatusCode.INTERNALERROR, "Failed resetting password for user " + username + ". User is now removed."); } //unauthorized } else { status = new Status(StatusCode.UNAUTHORIZED, "Operation not permitted"); } if (status.isSuccess()) { DaylightWebUtil.auditlog("User", request.getUserPrincipal().getName(), "changed password for", username); } return status; } /** * Is the operation permitted for the given level * * @param level */ private boolean authorize(IUserManager userManager, UserLevel level, HttpServletRequest request) { String username = request.getUserPrincipal().getName(); UserLevel userLevel = userManager.getUserLevel(username); return userLevel.toNumber() <= level.toNumber(); } }