/* * Copyright (c) 2014, 2015 Hewlett-Packard Development Company, L.P. 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.aaa.idm.rest; import java.util.ArrayList; import java.util.List; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.Context; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; import org.opendaylight.aaa.api.IDMStoreException; import org.opendaylight.aaa.api.model.Claim; import org.opendaylight.aaa.api.model.Domain; import org.opendaylight.aaa.api.model.Domains; import org.opendaylight.aaa.api.model.Grant; import org.opendaylight.aaa.api.model.Grants; import org.opendaylight.aaa.api.model.IDMError; import org.opendaylight.aaa.api.model.Role; import org.opendaylight.aaa.api.model.Roles; import org.opendaylight.aaa.api.model.User; import org.opendaylight.aaa.api.model.UserPwd; import org.opendaylight.aaa.api.model.Users; import org.opendaylight.aaa.idm.IdmLightProxy; import org.opendaylight.yang.gen.v1.config.aaa.authn.idmlight.rev151204.AAAIDMLightModule; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * REST application used to manipulate the H2 database domains table. The REST * endpoint is /auth/v1/domains. * * The following provides examples of curl commands and payloads to utilize the * domains REST endpoint: * * Get All Domains * curl -u admin:admin http://{HOST}:{PORT}/auth/v1/domains * * Get A Specific Domain * curl -u admin:admin http://{HOST}:{PORT}/auth/v1/domains/{id} * * Create A Domain * curl -u admin:admin -X POST -H "Content-Type: application/json" --data-binary {@literal @}domain.json http://{HOST}:{PORT}/auth/v1/domains * Example domain.json { * "description": "new domain", * "enabled", "true", * "name", "not sdn" * } * * Update A Domain * curl -u admin:admin -X PUT -H "Content-Type: application/json" --data-binary {@literal @}domain.json http://{HOST}:{PORT}/auth/v1/domains * Example domain.json { * "description": "new domain description", * "enabled", "true", * "name", "not sdn" * } * * @author peter.mellquist@hp.com * @author Ryan Goulding (ryandgoulding@gmail.com) */ @Path("/v1/domains") public class DomainHandler { private static final Logger LOG = LoggerFactory.getLogger(DomainHandler.class); /** * Extracts all domains. * * @return a response with all domains stored in the H2 database */ @GET @Produces("application/json") public Response getDomains() { LOG.info("Get /domains"); Domains domains = null; try { domains = AAAIDMLightModule.getStore().getDomains(); } catch (IDMStoreException se) { LOG.error("StoreException: ", se); IDMError idmerror = new IDMError(); idmerror.setMessage("Internal error getting domains"); idmerror.setDetails(se.getMessage()); return Response.status(500).entity(idmerror).build(); } return Response.ok(domains).build(); } /** * Extracts the domain represented by domainId. * * @param domainId the string domain (i.e., "sdn") * @return a response with the specified domain */ @GET @Path("/{id}") @Produces("application/json") public Response getDomain(@PathParam("id") String domainId) { LOG.info("Get /domains/{}", domainId); Domain domain = null; try { domain = AAAIDMLightModule.getStore().readDomain(domainId); } catch (IDMStoreException se) { LOG.error("StoreException: ", se); IDMError idmerror = new IDMError(); idmerror.setMessage("Internal error getting domain"); idmerror.setDetails(se.getMessage()); return Response.status(500).entity(idmerror).build(); } if (domain == null) { IDMError idmerror = new IDMError(); idmerror.setMessage("Not found! domain id :" + domainId); return Response.status(404).entity(idmerror).build(); } return Response.ok(domain).build(); } /** * Creates a domain. The name attribute is required for domain creation. * Enabled and description fields are optional. Optional fields default * in the following manner: * enabled: false * description: An empty string (""). * * @param info passed from Jersey * @param domain designated by the REST payload * @return A response stating success or failure of domain creation. */ @POST @Consumes("application/json") @Produces("application/json") public Response createDomain(@Context UriInfo info, Domain domain) { LOG.info("Post /domains"); try { if (domain.isEnabled() == null) { domain.setEnabled(false); } if (domain.getName() == null) { domain.setName(""); } if (domain.getDescription() == null) { domain.setDescription(""); } domain = AAAIDMLightModule.getStore().writeDomain(domain); } catch (IDMStoreException se) { LOG.error("StoreException: ", se); IDMError idmerror = new IDMError(); idmerror.setMessage("Internal error creating domain"); idmerror.setDetails(se.getMessage()); return Response.status(500).entity(idmerror).build(); } return Response.status(201).entity(domain).build(); } /** * Updates a domain. * * @param info passed from Jersey * @param domain the REST payload * @param domainId the last part of the path, containing the specified domain id * @return A response stating success or failure of domain update. */ @PUT @Path("/{id}") @Consumes("application/json") @Produces("application/json") public Response putDomain(@Context UriInfo info, Domain domain, @PathParam("id") String domainId) { LOG.info("Put /domains/{}", domainId); try { domain.setDomainid(domainId); domain = AAAIDMLightModule.getStore().updateDomain(domain); if (domain == null) { IDMError idmerror = new IDMError(); idmerror.setMessage("Not found! Domain id :" + domainId); return Response.status(404).entity(idmerror).build(); } IdmLightProxy.clearClaimCache(); return Response.status(200).entity(domain).build(); } catch (IDMStoreException se) { LOG.error("StoreException: ", se); IDMError idmerror = new IDMError(); idmerror.setMessage("Internal error putting domain"); idmerror.setDetails(se.getMessage()); return Response.status(500).entity(idmerror).build(); } } /** * Deletes a domain. * * @param info passed from Jersey * @param domainId the last part of the path, containing the specified domain id * @return A response stating success or failure of domain deletion. */ @DELETE @Path("/{id}") public Response deleteDomain(@Context UriInfo info, @PathParam("id") String domainId) { LOG.info("Delete /domains/{}", domainId); try { Domain domain = AAAIDMLightModule.getStore().deleteDomain(domainId); if (domain == null) { IDMError idmerror = new IDMError(); idmerror.setMessage("Not found! Domain id :" + domainId); return Response.status(404).entity(idmerror).build(); } } catch (IDMStoreException se) { LOG.error("StoreException: ", se); IDMError idmerror = new IDMError(); idmerror.setMessage("Internal error deleting Domain"); idmerror.setDetails(se.getMessage()); return Response.status(500).entity(idmerror).build(); } IdmLightProxy.clearClaimCache(); return Response.status(204).build(); } /** * Creates a grant. A grant defines the role a particular user is given on * a particular domain. For example, by default, AAA installs a grant for * the "admin" user, granting permission to act with "admin" role on the * "sdn" domain. * * @param info passed from Jersey * @param domainId the domain the user is allowed to access * @param userId the user that is allowed to access the domain * @param grant the payload containing role access controls * @return A response stating success or failure of grant creation. */ @POST @Path("/{did}/users/{uid}/roles") @Consumes("application/json") @Produces("application/json") public Response createGrant(@Context UriInfo info, @PathParam("did") String domainId, @PathParam("uid") String userId, Grant grant) { LOG.info("Post /domains/{}/users/{}/roles", domainId, userId); Domain domain = null; User user = null; Role role = null; String roleId = null; // validate domain id try { domain = AAAIDMLightModule.getStore().readDomain(domainId); } catch (IDMStoreException se) { LOG.error("StoreException: ", se); IDMError idmerror = new IDMError(); idmerror.setMessage("Internal error getting domain"); idmerror.setDetails(se.getMessage()); return Response.status(500).entity(idmerror).build(); } if (domain == null) { IDMError idmerror = new IDMError(); idmerror.setMessage("Not found! domain id :" + domainId); return Response.status(404).entity(idmerror).build(); } grant.setDomainid(domainId); try { user = AAAIDMLightModule.getStore().readUser(userId); } catch (IDMStoreException se) { LOG.error("StoreException: ", se); IDMError idmerror = new IDMError(); idmerror.setMessage("Internal error getting user"); idmerror.setDetails(se.getMessage()); return Response.status(500).entity(idmerror).build(); } if (user == null) { IDMError idmerror = new IDMError(); idmerror.setMessage("Not found! User id :" + userId); return Response.status(404).entity(idmerror).build(); } grant.setUserid(userId); // validate role id try { roleId = grant.getRoleid(); LOG.info("roleid = {}", roleId); } catch (NumberFormatException nfe) { IDMError idmerror = new IDMError(); idmerror.setMessage("Invalid Role id :" + grant.getRoleid()); return Response.status(404).entity(idmerror).build(); } try { role = AAAIDMLightModule.getStore().readRole(roleId); } catch (IDMStoreException se) { LOG.error("StoreException: ", se); IDMError idmerror = new IDMError(); idmerror.setMessage("Internal error getting role"); idmerror.setDetails(se.getMessage()); return Response.status(500).entity(idmerror).build(); } if (role == null) { IDMError idmerror = new IDMError(); idmerror.setMessage("Not found! role :" + grant.getRoleid()); return Response.status(404).entity(idmerror).build(); } // see if grant already exists for this try { Grant existingGrant = AAAIDMLightModule.getStore().readGrant(domainId, userId, roleId); if (existingGrant != null) { IDMError idmerror = new IDMError(); idmerror.setMessage("Grant already exists for did:" + domainId + " uid:" + userId + " rid:" + roleId); return Response.status(403).entity(idmerror).build(); } } catch (IDMStoreException se) { LOG.error("StoreException: ", se); IDMError idmerror = new IDMError(); idmerror.setMessage("Internal error creating grant"); idmerror.setDetails(se.getMessage()); return Response.status(500).entity(idmerror).build(); } // create grant try { grant = AAAIDMLightModule.getStore().writeGrant(grant); } catch (IDMStoreException se) { LOG.error("StoreException: ", se); IDMError idmerror = new IDMError(); idmerror.setMessage("Internal error creating grant"); idmerror.setDetails(se.getMessage()); return Response.status(500).entity(idmerror).build(); } IdmLightProxy.clearClaimCache(); return Response.status(201).entity(grant).build(); } /** * Used to validate user access. * * @param info passed from Jersey * @param domainId the domain in question * @param userpwd the password attempt * @return A response stating success or failure of user validation. */ @POST @Path("/{did}/users/roles") @Consumes("application/json") @Produces("application/json") public Response validateUser(@Context UriInfo info, @PathParam("did") String domainId, UserPwd userpwd) { LOG.info("GET /domains/{}/users", domainId); Domain domain = null; Claim claim = new Claim(); List roleList = new ArrayList(); try { domain = AAAIDMLightModule.getStore().readDomain(domainId); } catch (IDMStoreException se) { LOG.error("StoreException: ", se); IDMError idmerror = new IDMError(); idmerror.setMessage("Internal error getting domain"); idmerror.setDetails(se.getMessage()); return Response.status(500).entity(idmerror).build(); } if (domain == null) { IDMError idmerror = new IDMError(); idmerror.setMessage("Not found! Domain id :" + domainId); return Response.status(404).entity(idmerror).build(); } // check request body for username and pwd String username = userpwd.getUsername(); if (username == null) { IDMError idmerror = new IDMError(); idmerror.setMessage("username not specfied in request body"); return Response.status(400).entity(idmerror).build(); } String pwd = userpwd.getUserpwd(); if (pwd == null) { IDMError idmerror = new IDMError(); idmerror.setMessage("userpwd not specfied in request body"); return Response.status(400).entity(idmerror).build(); } // find userid for user try { Users users = AAAIDMLightModule.getStore().getUsers(username, domainId); List userList = users.getUsers(); if (userList.size() == 0) { IDMError idmerror = new IDMError(); idmerror.setMessage("did not find username: " + username); return Response.status(404).entity(idmerror).build(); } User user = userList.get(0); String userPwd = user.getPassword(); String reqPwd = userpwd.getUserpwd(); if (!userPwd.equals(reqPwd)) { IDMError idmerror = new IDMError(); idmerror.setMessage("password does not match for username: " + username); return Response.status(401).entity(idmerror).build(); } claim.setDomainid(domainId); claim.setUsername(username); claim.setUserid(user.getUserid()); try { Grants grants = AAAIDMLightModule.getStore().getGrants(domainId, user.getUserid()); List grantsList = grants.getGrants(); for (int i = 0; i < grantsList.size(); i++) { Grant grant = grantsList.get(i); Role role = AAAIDMLightModule.getStore().readRole(grant.getRoleid()); roleList.add(role); } } catch (IDMStoreException se) { LOG.error("StoreException: ", se); IDMError idmerror = new IDMError(); idmerror.setMessage("Internal error getting Roles"); idmerror.setDetails(se.getMessage()); return Response.status(500).entity(idmerror).build(); } claim.setRoles(roleList); } catch (IDMStoreException se) { LOG.error("StoreException: ", se); IDMError idmerror = new IDMError(); idmerror.setMessage("Internal error getting user"); idmerror.setDetails(se.getMessage()); return Response.status(500).entity(idmerror).build(); } return Response.ok(claim).build(); } /** * Get the grants for a user on a domain. * * @param info passed from Jersey * @param domainId the domain in question * @param userId the user in question * @return A response containing the grants for a user on a domain. */ @GET @Path("/{did}/users/{uid}/roles") @Produces("application/json") public Response getRoles(@Context UriInfo info, @PathParam("did") String domainId, @PathParam("uid") String userId) { LOG.info("GET /domains/{}/users/{}/roles", domainId, userId); Domain domain = null; User user = null; Roles roles = new Roles(); List roleList = new ArrayList(); try { domain = AAAIDMLightModule.getStore().readDomain(domainId); } catch (IDMStoreException se) { LOG.error("StoreException: ", se); IDMError idmerror = new IDMError(); idmerror.setMessage("Internal error getting domain"); idmerror.setDetails(se.getMessage()); return Response.status(500).entity(idmerror).build(); } if (domain == null) { IDMError idmerror = new IDMError(); idmerror.setMessage("Not found! Domain id :" + domainId); return Response.status(404).entity(idmerror).build(); } try { user = AAAIDMLightModule.getStore().readUser(userId); } catch (IDMStoreException se) { LOG.error("StoreException: ", se); IDMError idmerror = new IDMError(); idmerror.setMessage("Internal error getting user"); idmerror.setDetails(se.getMessage()); return Response.status(500).entity(idmerror).build(); } if (user == null) { IDMError idmerror = new IDMError(); idmerror.setMessage("Not found! User id :" + userId); return Response.status(404).entity(idmerror).build(); } try { Grants grants = AAAIDMLightModule.getStore().getGrants(domainId, userId); List grantsList = grants.getGrants(); for (int i = 0; i < grantsList.size(); i++) { Grant grant = grantsList.get(i); Role role = AAAIDMLightModule.getStore().readRole(grant.getRoleid()); roleList.add(role); } } catch (IDMStoreException se) { LOG.error("StoreException: ", se); IDMError idmerror = new IDMError(); idmerror.setMessage("Internal error getting Roles"); idmerror.setDetails(se.getMessage()); return Response.status(500).entity(idmerror).build(); } roles.setRoles(roleList); return Response.ok(roles).build(); } /** * Delete a grant. * * @param info passed from Jersey * @param domainId the domain for the grant * @param userId the user for the grant * @param roleId the role for the grant * @return A response stating success or failure of the grant deletion. */ @DELETE @Path("/{did}/users/{uid}/roles/{rid}") public Response deleteGrant(@Context UriInfo info, @PathParam("did") String domainId, @PathParam("uid") String userId, @PathParam("rid") String roleId) { Domain domain = null; User user = null; Role role = null; try { domain = AAAIDMLightModule.getStore().readDomain(domainId); } catch (IDMStoreException se) { LOG.error("Error deleting Grant : ", se); IDMError idmerror = new IDMError(); idmerror.setMessage("Internal error getting domain"); idmerror.setDetails(se.getMessage()); return Response.status(500).entity(idmerror).build(); } if (domain == null) { IDMError idmerror = new IDMError(); idmerror.setMessage("Not found! Domain id :" + domainId); return Response.status(404).entity(idmerror).build(); } try { user = AAAIDMLightModule.getStore().readUser(userId); } catch (IDMStoreException se) { LOG.error("StoreException : ", se); IDMError idmerror = new IDMError(); idmerror.setMessage("Internal error getting user"); idmerror.setDetails(se.getMessage()); return Response.status(500).entity(idmerror).build(); } if (user == null) { IDMError idmerror = new IDMError(); idmerror.setMessage("Not found! User id :" + userId); return Response.status(404).entity(idmerror).build(); } try { role = AAAIDMLightModule.getStore().readRole(roleId); } catch (IDMStoreException se) { LOG.error("StoreException: ", se); IDMError idmerror = new IDMError(); idmerror.setMessage("Internal error getting Role"); idmerror.setDetails(se.getMessage()); return Response.status(500).entity(idmerror).build(); } if (role == null) { IDMError idmerror = new IDMError(); idmerror.setMessage("Not found! Role id :" + roleId); return Response.status(404).entity(idmerror).build(); } // see if grant already exists try { Grant existingGrant = AAAIDMLightModule.getStore().readGrant(domainId, userId, roleId); if (existingGrant == null) { IDMError idmerror = new IDMError(); idmerror.setMessage("Grant does not exist for did:" + domainId + " uid:" + userId + " rid:" + roleId); return Response.status(404).entity(idmerror).build(); } existingGrant = AAAIDMLightModule.getStore().deleteGrant(existingGrant.getGrantid()); } catch (IDMStoreException se) { LOG.error("StoreException: ", se); IDMError idmerror = new IDMError(); idmerror.setMessage("Internal error creating grant"); idmerror.setDetails(se.getMessage()); return Response.status(500).entity(idmerror).build(); } IdmLightProxy.clearClaimCache(); return Response.status(204).build(); } }