2 * Copyright (c) 2014, 2017 Hewlett-Packard Development Company, L.P. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
9 package org.opendaylight.aaa.shiro.idm;
11 import java.util.ArrayList;
12 import java.util.List;
13 import javax.ws.rs.Consumes;
14 import javax.ws.rs.DELETE;
15 import javax.ws.rs.GET;
16 import javax.ws.rs.POST;
17 import javax.ws.rs.PUT;
18 import javax.ws.rs.Path;
19 import javax.ws.rs.PathParam;
20 import javax.ws.rs.Produces;
21 import javax.ws.rs.core.Context;
22 import javax.ws.rs.core.Response;
23 import javax.ws.rs.core.UriInfo;
24 import org.opendaylight.aaa.api.IDMStoreException;
25 import org.opendaylight.aaa.api.model.Claim;
26 import org.opendaylight.aaa.api.model.Domain;
27 import org.opendaylight.aaa.api.model.Domains;
28 import org.opendaylight.aaa.api.model.Grant;
29 import org.opendaylight.aaa.api.model.Grants;
30 import org.opendaylight.aaa.api.model.IDMError;
31 import org.opendaylight.aaa.api.model.Role;
32 import org.opendaylight.aaa.api.model.Roles;
33 import org.opendaylight.aaa.api.model.User;
34 import org.opendaylight.aaa.api.model.UserPwd;
35 import org.opendaylight.aaa.api.model.Users;
36 import org.opendaylight.aaa.shiro.AAAShiroProvider;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
41 * REST application used to manipulate the H2 database domains table. The REST
42 * endpoint is <code>/auth/v1/domains</code>.
45 * A wrapper script called <code>idmtool</code> is provided to manipulate AAA
48 * @author peter.mellquist@hp.com
51 public class DomainHandler {
53 private static final Logger LOG = LoggerFactory.getLogger(DomainHandler.class);
56 * Extracts all domains.
58 * @return a response with all domains stored in the H2 database
61 @Produces("application/json")
62 public Response getDomains() {
63 LOG.info("Get /domains");
64 Domains domains = null;
66 domains = AAAShiroProvider.getInstance().getIdmStore().getDomains();
67 } catch (IDMStoreException e) {
68 LOG.error("StoreException", e);
69 IDMError idmerror = new IDMError();
70 idmerror.setMessage("Internal error getting domains");
71 idmerror.setDetails(e.getMessage());
72 return Response.status(500).entity(idmerror).build();
74 return Response.ok(domains).build();
78 * Extracts the domain represented by <code>domainId</code>.
81 * the string domain (i.e., "sdn")
82 * @return a response with the specified domain
86 @Produces("application/json")
87 public Response getDomain(@PathParam("id") String domainId) {
88 LOG.info("Get /domains/{}", domainId);
91 domain = AAAShiroProvider.getInstance().getIdmStore().readDomain(domainId);
92 } catch (IDMStoreException e) {
93 LOG.error("StoreException", e);
94 IDMError idmerror = new IDMError();
95 idmerror.setMessage("Internal error getting domain");
96 idmerror.setDetails(e.getMessage());
97 return Response.status(500).entity(idmerror).build();
100 if (domain == null) {
101 IDMError idmerror = new IDMError();
102 idmerror.setMessage("Not found! domain id :" + domainId);
103 return Response.status(404).entity(idmerror).build();
105 return Response.ok(domain).build();
109 * Creates a domain. The name attribute is required for domain creation.
110 * Enabled and description fields are optional. Optional fields default in
111 * the following manner: <code>enabled</code>: <code>false</code>
112 * <code>description</code>: An empty string (<code>""</code>).
117 * designated by the REST payload
118 * @return A response stating success or failure of domain creation.
121 @Consumes("application/json")
122 @Produces("application/json")
123 public Response createDomain(@Context UriInfo info, Domain domain) {
124 LOG.info("Post /domains");
126 // Bug 8382: domain id is an implementation detail and isn't
128 if (domain.getDomainid() != null) {
129 final String errorMessage = "do not specify domainId, it will be assigned automatically for you";
130 LOG.debug(errorMessage);
131 final IDMError idmError = new IDMError();
132 idmError.setMessage(errorMessage);
133 return Response.status(400).entity(idmError).build();
135 if (domain.isEnabled() == null) {
136 domain.setEnabled(false);
138 if (domain.getName() == null) {
141 if (domain.getDescription() == null) {
142 domain.setDescription("");
144 domain = AAAShiroProvider.getInstance().getIdmStore().writeDomain(domain);
145 } catch (IDMStoreException e) {
146 LOG.error("StoreException", e);
147 IDMError idmerror = new IDMError();
148 idmerror.setMessage("Internal error creating domain");
149 idmerror.setDetails(e.getMessage());
150 return Response.status(500).entity(idmerror).build();
152 return Response.status(201).entity(domain).build();
163 * the last part of the path, containing the specified domain id
164 * @return A response stating success or failure of domain update.
168 @Consumes("application/json")
169 @Produces("application/json")
170 public Response putDomain(@Context UriInfo info, Domain domain, @PathParam("id") String domainId) {
171 LOG.info("Put /domains/{}", domainId);
173 domain.setDomainid(domainId);
174 domain = AAAShiroProvider.getInstance().getIdmStore().updateDomain(domain);
175 if (domain == null) {
176 IDMError idmerror = new IDMError();
177 idmerror.setMessage("Not found! Domain id:" + domainId);
178 return Response.status(404).entity(idmerror).build();
180 IdmLightProxy.clearClaimCache();
181 return Response.status(200).entity(domain).build();
182 } catch (IDMStoreException e) {
183 LOG.error("StoreException", e);
184 IDMError idmerror = new IDMError();
185 idmerror.setMessage("Internal error putting domain");
186 idmerror.setDetails(e.getMessage());
187 return Response.status(500).entity(idmerror).build();
197 * the last part of the path, containing the specified domain id
198 * @return A response stating success or failure of domain deletion.
202 public Response deleteDomain(@Context UriInfo info, @PathParam("id") String domainId) {
203 LOG.info("Delete /domains/{}", domainId);
206 Domain domain = AAAShiroProvider.getInstance().getIdmStore().deleteDomain(domainId);
207 if (domain == null) {
208 IDMError idmerror = new IDMError();
209 idmerror.setMessage("Not found! Domain id:" + domainId);
210 return Response.status(404).entity(idmerror).build();
212 } catch (IDMStoreException e) {
213 LOG.error("StoreException", e);
214 IDMError idmerror = new IDMError();
215 idmerror.setMessage("Internal error deleting Domain");
216 idmerror.setDetails(e.getMessage());
217 return Response.status(500).entity(idmerror).build();
219 IdmLightProxy.clearClaimCache();
220 return Response.status(204).build();
224 * Creates a grant. A grant defines the role a particular user is given on a
225 * particular domain. For example, by default, AAA installs a grant for the
226 * "admin" user, granting permission to act with "admin" role on the "sdn"
232 * the domain the user is allowed to access
234 * the user that is allowed to access the domain
236 * the payload containing role access controls
237 * @return A response stating success or failure of grant creation.
240 @Path("/{did}/users/{uid}/roles")
241 @Consumes("application/json")
242 @Produces("application/json")
243 public Response createGrant(@Context UriInfo info, @PathParam("did") String domainId,
244 @PathParam("uid") String userId, Grant grant) {
245 LOG.info("Post /domains/{}/users/{}/roles", domainId, userId);
247 // Bug 8382: grant id is an implementation detail and isn't specifiable
248 if (grant.getGrantid() != null) {
249 final String errorMessage = "do not specify grantId, it will be assigned automatically for you";
250 LOG.debug(errorMessage);
251 final IDMError idmError = new IDMError();
252 idmError.setMessage(errorMessage);
253 return Response.status(400).entity(idmError).build();
256 Domain domain = null;
261 // validate domain id
263 domain = AAAShiroProvider.getInstance().getIdmStore().readDomain(domainId);
264 } catch (IDMStoreException e) {
265 LOG.error("StoreException", e);
266 IDMError idmerror = new IDMError();
267 idmerror.setMessage("Internal error getting domain");
268 idmerror.setDetails(e.getMessage());
269 return Response.status(500).entity(idmerror).build();
271 if (domain == null) {
272 IDMError idmerror = new IDMError();
273 idmerror.setMessage("Not found! domain id :" + domainId);
274 return Response.status(404).entity(idmerror).build();
276 grant.setDomainid(domainId);
279 user = AAAShiroProvider.getInstance().getIdmStore().readUser(userId);
280 } catch (IDMStoreException e) {
281 LOG.error("StoreException", e);
282 IDMError idmerror = new IDMError();
283 idmerror.setMessage("Internal error getting user");
284 idmerror.setDetails(e.getMessage());
285 return Response.status(500).entity(idmerror).build();
288 IDMError idmerror = new IDMError();
289 idmerror.setMessage("Not found! User id:" + userId);
290 return Response.status(404).entity(idmerror).build();
292 grant.setUserid(userId);
296 roleId = grant.getRoleid();
297 LOG.info("roleid = {}", roleId);
298 } catch (NumberFormatException nfe) {
299 IDMError idmerror = new IDMError();
300 idmerror.setMessage("Invalid Role id:" + grant.getRoleid());
301 return Response.status(404).entity(idmerror).build();
304 role = AAAShiroProvider.getInstance().getIdmStore().readRole(roleId);
305 } catch (IDMStoreException e) {
306 LOG.error("StoreException", e);
307 IDMError idmerror = new IDMError();
308 idmerror.setMessage("Internal error getting role");
309 idmerror.setDetails(e.getMessage());
310 return Response.status(500).entity(idmerror).build();
313 IDMError idmerror = new IDMError();
314 idmerror.setMessage("Not found! role:" + grant.getRoleid());
315 return Response.status(404).entity(idmerror).build();
318 // see if grant already exists for this
320 Grant existingGrant = AAAShiroProvider.getInstance().getIdmStore().readGrant(domainId, userId, roleId);
321 if (existingGrant != null) {
322 IDMError idmerror = new IDMError();
323 idmerror.setMessage("Grant already exists for did:" + domainId + " uid:" + userId + " rid:" + roleId);
324 return Response.status(403).entity(idmerror).build();
326 } catch (IDMStoreException e) {
327 LOG.error("StoreException", e);
328 IDMError idmerror = new IDMError();
329 idmerror.setMessage("Internal error creating grant");
330 idmerror.setDetails(e.getMessage());
331 return Response.status(500).entity(idmerror).build();
336 grant = AAAShiroProvider.getInstance().getIdmStore().writeGrant(grant);
337 } catch (IDMStoreException e) {
338 LOG.error("StoreException: ", e);
339 IDMError idmerror = new IDMError();
340 idmerror.setMessage("Internal error creating grant");
341 idmerror.setDetails(e.getMessage());
342 return Response.status(500).entity(idmerror).build();
345 IdmLightProxy.clearClaimCache();
346 return Response.status(201).entity(grant).build();
350 * Used to validate user access.
355 * the domain in question
357 * the password attempt
358 * @return A response stating success or failure of user validation.
361 @Path("/{did}/users/roles")
362 @Consumes("application/json")
363 @Produces("application/json")
364 public Response validateUser(@Context UriInfo info, @PathParam("did") String domainId, UserPwd userpwd) {
365 LOG.info("GET /domains/{}/users", domainId);
366 Domain domain = null;
367 Claim claim = new Claim();
368 List<Role> roleList = new ArrayList<>();
371 domain = AAAShiroProvider.getInstance().getIdmStore().readDomain(domainId);
372 } catch (IDMStoreException se) {
373 LOG.error("StoreException: ", se);
374 IDMError idmerror = new IDMError();
375 idmerror.setMessage("Internal error getting domain");
376 idmerror.setDetails(se.getMessage());
377 return Response.status(500).entity(idmerror).build();
379 if (domain == null) {
380 IDMError idmerror = new IDMError();
381 idmerror.setMessage("Not found! Domain id:" + domainId);
382 return Response.status(404).entity(idmerror).build();
385 // check request body for username and pwd
386 String username = userpwd.getUsername();
387 if (username == null) {
388 IDMError idmerror = new IDMError();
389 idmerror.setMessage("username not specfied in request body");
390 return Response.status(400).entity(idmerror).build();
392 String pwd = userpwd.getUserpwd();
394 IDMError idmerror = new IDMError();
395 idmerror.setMessage("userpwd not specfied in request body");
396 return Response.status(400).entity(idmerror).build();
399 // find userid for user
401 Users users = AAAShiroProvider.getInstance().getIdmStore().getUsers(username, domainId);
402 List<User> userList = users.getUsers();
403 if (userList.size() == 0) {
404 IDMError idmerror = new IDMError();
405 idmerror.setMessage("did not find username: " + username);
406 return Response.status(404).entity(idmerror).build();
408 User user = userList.get(0);
409 String userPwd = user.getPassword();
410 String reqPwd = userpwd.getUserpwd();
411 if (!userPwd.equals(reqPwd)) {
412 IDMError idmerror = new IDMError();
413 idmerror.setMessage("password does not match for username: " + username);
414 return Response.status(401).entity(idmerror).build();
416 claim.setDomainid(domainId);
417 claim.setUsername(username);
418 claim.setUserid(user.getUserid());
420 Grants grants = AAAShiroProvider.getInstance().getIdmStore().getGrants(domainId, user.getUserid());
421 List<Grant> grantsList = grants.getGrants();
422 for (int i = 0; i < grantsList.size(); i++) {
423 Grant grant = grantsList.get(i);
424 Role role = AAAShiroProvider.getInstance().getIdmStore().readRole(grant.getRoleid());
427 } catch (IDMStoreException e) {
428 LOG.error("StoreException", e);
429 IDMError idmerror = new IDMError();
430 idmerror.setMessage("Internal error getting Roles");
431 idmerror.setDetails(e.getMessage());
432 return Response.status(500).entity(idmerror).build();
434 claim.setRoles(roleList);
435 } catch (IDMStoreException e) {
436 LOG.error("StoreException", e);
437 IDMError idmerror = new IDMError();
438 idmerror.setMessage("Internal error getting user");
439 idmerror.setDetails(e.getMessage());
440 return Response.status(500).entity(idmerror).build();
443 return Response.ok(claim).build();
447 * Get the grants for a user on a domain.
452 * the domain in question
454 * the user in question
455 * @return A response containing the grants for a user on a domain.
458 @Path("/{did}/users/{uid}/roles")
459 @Produces("application/json")
460 public Response getRoles(@Context UriInfo info, @PathParam("did") String domainId,
461 @PathParam("uid") String userId) {
462 LOG.info("GET /domains/{}/users/{}/roles", domainId, userId);
463 Domain domain = null;
465 List<Role> roleList = new ArrayList<>();
468 domain = AAAShiroProvider.getInstance().getIdmStore().readDomain(domainId);
469 } catch (IDMStoreException e) {
470 LOG.error("StoreException", e);
471 IDMError idmerror = new IDMError();
472 idmerror.setMessage("Internal error getting domain");
473 idmerror.setDetails(e.getMessage());
474 return Response.status(500).entity(idmerror).build();
476 if (domain == null) {
477 IDMError idmerror = new IDMError();
478 idmerror.setMessage("Not found! Domain id:" + domainId);
479 return Response.status(404).entity(idmerror).build();
483 user = AAAShiroProvider.getInstance().getIdmStore().readUser(userId);
484 } catch (IDMStoreException e) {
485 LOG.error("StoreException", e);
486 IDMError idmerror = new IDMError();
487 idmerror.setMessage("Internal error getting user");
488 idmerror.setDetails(e.getMessage());
489 return Response.status(500).entity(idmerror).build();
492 IDMError idmerror = new IDMError();
493 idmerror.setMessage("Not found! User id:" + userId);
494 return Response.status(404).entity(idmerror).build();
498 Grants grants = AAAShiroProvider.getInstance().getIdmStore().getGrants(domainId, userId);
499 List<Grant> grantsList = grants.getGrants();
500 for (int i = 0; i < grantsList.size(); i++) {
501 Grant grant = grantsList.get(i);
502 Role role = AAAShiroProvider.getInstance().getIdmStore().readRole(grant.getRoleid());
505 } catch (IDMStoreException e) {
506 LOG.error("StoreException", e);
507 IDMError idmerror = new IDMError();
508 idmerror.setMessage("Internal error getting Roles");
509 idmerror.setDetails(e.getMessage());
510 return Response.status(500).entity(idmerror).build();
512 Roles roles = new Roles();
513 roles.setRoles(roleList);
514 return Response.ok(roles).build();
523 * the domain for the grant
525 * the user for the grant
527 * the role for the grant
528 * @return A response stating success or failure of the grant deletion.
531 @Path("/{did}/users/{uid}/roles/{rid}")
532 public Response deleteGrant(@Context UriInfo info, @PathParam("did") String domainId,
533 @PathParam("uid") String userId, @PathParam("rid") String roleId) {
534 Domain domain = null;
539 domain = AAAShiroProvider.getInstance().getIdmStore().readDomain(domainId);
540 } catch (IDMStoreException e) {
541 LOG.error("Error deleting Grant", e);
542 IDMError idmerror = new IDMError();
543 idmerror.setMessage("Internal error getting domain");
544 idmerror.setDetails(e.getMessage());
545 return Response.status(500).entity(idmerror).build();
547 if (domain == null) {
548 IDMError idmerror = new IDMError();
549 idmerror.setMessage("Not found! Domain id:" + domainId);
550 return Response.status(404).entity(idmerror).build();
554 user = AAAShiroProvider.getInstance().getIdmStore().readUser(userId);
555 } catch (IDMStoreException e) {
556 LOG.error("StoreException", e);
557 IDMError idmerror = new IDMError();
558 idmerror.setMessage("Internal error getting user");
559 idmerror.setDetails(e.getMessage());
560 return Response.status(500).entity(idmerror).build();
563 IDMError idmerror = new IDMError();
564 idmerror.setMessage("Not found! User id:" + userId);
565 return Response.status(404).entity(idmerror).build();
569 role = AAAShiroProvider.getInstance().getIdmStore().readRole(roleId);
570 } catch (IDMStoreException e) {
571 LOG.error("StoreException", e);
572 IDMError idmerror = new IDMError();
573 idmerror.setMessage("Internal error getting Role");
574 idmerror.setDetails(e.getMessage());
575 return Response.status(500).entity(idmerror).build();
578 IDMError idmerror = new IDMError();
579 idmerror.setMessage("Not found! Role id:" + roleId);
580 return Response.status(404).entity(idmerror).build();
583 // see if grant already exists
585 Grant existingGrant = AAAShiroProvider.getInstance().getIdmStore().readGrant(domainId, userId, roleId);
586 if (existingGrant == null) {
587 IDMError idmerror = new IDMError();
588 idmerror.setMessage("Grant does not exist for did:" + domainId + " uid:" + userId + " rid:" + roleId);
589 return Response.status(404).entity(idmerror).build();
591 existingGrant = AAAShiroProvider.getInstance().getIdmStore().deleteGrant(existingGrant.getGrantid());
592 } catch (IDMStoreException e) {
593 LOG.error("StoreException", e);
594 IDMError idmerror = new IDMError();
595 idmerror.setMessage("Internal error creating grant");
596 idmerror.setDetails(e.getMessage());
597 return Response.status(500).entity(idmerror).build();
599 IdmLightProxy.clearClaimCache();
600 return Response.status(204).build();