Drop explicit jetty-servlets dependency
[aaa.git] / aaa-shiro / impl / src / main / java / org / opendaylight / aaa / shiro / idm / DomainHandler.java
1 /*
2  * Copyright (c) 2014, 2017 Hewlett-Packard Development Company, L.P. and others.  All rights reserved.
3  *
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
7  */
8
9 package org.opendaylight.aaa.shiro.idm;
10
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;
39
40 /**
41  * REST application used to manipulate the H2 database domains table. The REST
42  * endpoint is <code>/auth/v1/domains</code>.
43  *
44  * <p>
45  * A wrapper script called <code>idmtool</code> is provided to manipulate AAA
46  * data.
47  *
48  * @author peter.mellquist@hp.com
49  */
50 @Path("/v1/domains")
51 public class DomainHandler {
52
53     private static final Logger LOG = LoggerFactory.getLogger(DomainHandler.class);
54
55     /**
56      * Extracts all domains.
57      *
58      * @return a response with all domains stored in the H2 database
59      */
60     @GET
61     @Produces("application/json")
62     public Response getDomains() {
63         LOG.info("Get /domains");
64         Domains domains = null;
65         try {
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();
73         }
74         return Response.ok(domains).build();
75     }
76
77     /**
78      * Extracts the domain represented by <code>domainId</code>.
79      *
80      * @param domainId
81      *            the string domain (i.e., "sdn")
82      * @return a response with the specified domain
83      */
84     @GET
85     @Path("/{id}")
86     @Produces("application/json")
87     public Response getDomain(@PathParam("id") String domainId) {
88         LOG.info("Get /domains/{}", domainId);
89         Domain domain = null;
90         try {
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();
98         }
99
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();
104         }
105         return Response.ok(domain).build();
106     }
107
108     /**
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>).
113      *
114      * @param info
115      *            passed from Jersey
116      * @param domain
117      *            designated by the REST payload
118      * @return A response stating success or failure of domain creation.
119      */
120     @POST
121     @Consumes("application/json")
122     @Produces("application/json")
123     public Response createDomain(@Context UriInfo info, Domain domain) {
124         LOG.info("Post /domains");
125         try {
126             // Bug 8382: domain id is an implementation detail and isn't
127             // specifiable
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();
134             }
135             if (domain.isEnabled() == null) {
136                 domain.setEnabled(false);
137             }
138             if (domain.getName() == null) {
139                 domain.setName("");
140             }
141             if (domain.getDescription() == null) {
142                 domain.setDescription("");
143             }
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();
151         }
152         return Response.status(201).entity(domain).build();
153     }
154
155     /**
156      * Updates a domain.
157      *
158      * @param info
159      *            passed from Jersey
160      * @param domain
161      *            the REST payload
162      * @param domainId
163      *            the last part of the path, containing the specified domain id
164      * @return A response stating success or failure of domain update.
165      */
166     @PUT
167     @Path("/{id}")
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);
172         try {
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();
179             }
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();
188         }
189     }
190
191     /**
192      * Deletes a domain.
193      *
194      * @param info
195      *            passed from Jersey
196      * @param domainId
197      *            the last part of the path, containing the specified domain id
198      * @return A response stating success or failure of domain deletion.
199      */
200     @DELETE
201     @Path("/{id}")
202     public Response deleteDomain(@Context UriInfo info, @PathParam("id") String domainId) {
203         LOG.info("Delete /domains/{}", domainId);
204
205         try {
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();
211             }
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();
218         }
219         IdmLightProxy.clearClaimCache();
220         return Response.status(204).build();
221     }
222
223     /**
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"
227      * domain.
228      *
229      * @param info
230      *            passed from Jersey
231      * @param domainId
232      *            the domain the user is allowed to access
233      * @param userId
234      *            the user that is allowed to access the domain
235      * @param grant
236      *            the payload containing role access controls
237      * @return A response stating success or failure of grant creation.
238      */
239     @POST
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);
246
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();
254         }
255
256         Domain domain = null;
257         User user;
258         Role role;
259         String roleId;
260
261         // validate domain id
262         try {
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();
270         }
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();
275         }
276         grant.setDomainid(domainId);
277
278         try {
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();
286         }
287         if (user == null) {
288             IDMError idmerror = new IDMError();
289             idmerror.setMessage("Not found! User id:" + userId);
290             return Response.status(404).entity(idmerror).build();
291         }
292         grant.setUserid(userId);
293
294         // validate role id
295         try {
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();
302         }
303         try {
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();
311         }
312         if (role == null) {
313             IDMError idmerror = new IDMError();
314             idmerror.setMessage("Not found! role:" + grant.getRoleid());
315             return Response.status(404).entity(idmerror).build();
316         }
317
318         // see if grant already exists for this
319         try {
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();
325             }
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();
332         }
333
334         // create grant
335         try {
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();
343         }
344
345         IdmLightProxy.clearClaimCache();
346         return Response.status(201).entity(grant).build();
347     }
348
349     /**
350      * Used to validate user access.
351      *
352      * @param info
353      *            passed from Jersey
354      * @param domainId
355      *            the domain in question
356      * @param userpwd
357      *            the password attempt
358      * @return A response stating success or failure of user validation.
359      */
360     @POST
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<>();
369
370         try {
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();
378         }
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();
383         }
384
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();
391         }
392         String pwd = userpwd.getUserpwd();
393         if (pwd == null) {
394             IDMError idmerror = new IDMError();
395             idmerror.setMessage("userpwd not specfied in request body");
396             return Response.status(400).entity(idmerror).build();
397         }
398
399         // find userid for user
400         try {
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();
407             }
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();
415             }
416             claim.setDomainid(domainId);
417             claim.setUsername(username);
418             claim.setUserid(user.getUserid());
419             try {
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());
425                     roleList.add(role);
426                 }
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();
433             }
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();
441         }
442
443         return Response.ok(claim).build();
444     }
445
446     /**
447      * Get the grants for a user on a domain.
448      *
449      * @param info
450      *            passed from Jersey
451      * @param domainId
452      *            the domain in question
453      * @param userId
454      *            the user in question
455      * @return A response containing the grants for a user on a domain.
456      */
457     @GET
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;
464         User user;
465         List<Role> roleList = new ArrayList<>();
466
467         try {
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();
475         }
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();
480         }
481
482         try {
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();
490         }
491         if (user == null) {
492             IDMError idmerror = new IDMError();
493             idmerror.setMessage("Not found! User id:" + userId);
494             return Response.status(404).entity(idmerror).build();
495         }
496
497         try {
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());
503                 roleList.add(role);
504             }
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();
511         }
512         Roles roles = new Roles();
513         roles.setRoles(roleList);
514         return Response.ok(roles).build();
515     }
516
517     /**
518      * Delete a grant.
519      *
520      * @param info
521      *            passed from Jersey
522      * @param domainId
523      *            the domain for the grant
524      * @param userId
525      *            the user for the grant
526      * @param roleId
527      *            the role for the grant
528      * @return A response stating success or failure of the grant deletion.
529      */
530     @DELETE
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;
535         User user;
536         Role role;
537
538         try {
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();
546         }
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();
551         }
552
553         try {
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();
561         }
562         if (user == null) {
563             IDMError idmerror = new IDMError();
564             idmerror.setMessage("Not found! User id:" + userId);
565             return Response.status(404).entity(idmerror).build();
566         }
567
568         try {
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();
576         }
577         if (role == null) {
578             IDMError idmerror = new IDMError();
579             idmerror.setMessage("Not found! Role id:" + roleId);
580             return Response.status(404).entity(idmerror).build();
581         }
582
583         // see if grant already exists
584         try {
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();
590             }
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();
598         }
599         IdmLightProxy.clearClaimCache();
600         return Response.status(204).build();
601     }
602 }