7ddc0748698260f06f28b2709c8d5cb5afc7eeaf
[moon.git] /
1 /*
2  * Copyright (c) 2014, 2015 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.idm.rest;
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.idm.IdmLightProxy;
37 import org.opendaylight.yang.gen.v1.config.aaa.authn.idmlight.rev151204.AAAIDMLightModule;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
40
41 /**
42  * REST application used to manipulate the H2 database domains table. The REST
43  * endpoint is <code>/auth/v1/domains</code>.
44  *
45  * The following provides examples of curl commands and payloads to utilize the
46  * domains REST endpoint:
47  *
48  * <b>Get All Domains</b>
49  * <code>curl -u admin:admin http://{HOST}:{PORT}/auth/v1/domains</code>
50  *
51  * <b>Get A Specific Domain</b>
52  * <code>curl -u admin:admin http://{HOST}:{PORT}/auth/v1/domains/{id}</code>
53  *
54  * <b>Create A Domain</b>
55  * <code>curl -u admin:admin -X POST -H "Content-Type: application/json" --data-binary {@literal @}domain.json http://{HOST}:{PORT}/auth/v1/domains</code>
56  * Example domain.json <code>{
57  *   "description": "new domain",
58  *   "enabled", "true",
59  *   "name", "not sdn"
60  * }</code>
61  *
62  * <b>Update A Domain</b>
63  * <code>curl -u admin:admin -X PUT -H "Content-Type: application/json" --data-binary {@literal @}domain.json http://{HOST}:{PORT}/auth/v1/domains</code>
64  * Example domain.json <code>{
65  *   "description": "new domain description",
66  *   "enabled", "true",
67  *   "name", "not sdn"
68  * }</code>
69  *
70  * @author peter.mellquist@hp.com
71  * @author Ryan Goulding (ryandgoulding@gmail.com)
72  */
73 @Path("/v1/domains")
74 public class DomainHandler {
75
76     private static final Logger LOG = LoggerFactory.getLogger(DomainHandler.class);
77
78     /**
79      * Extracts all domains.
80      *
81      * @return a response with all domains stored in the H2 database
82      */
83     @GET
84     @Produces("application/json")
85     public Response getDomains() {
86         LOG.info("Get /domains");
87         Domains domains = null;
88         try {
89             domains = AAAIDMLightModule.getStore().getDomains();
90         } catch (IDMStoreException se) {
91             LOG.error("StoreException: ", se);
92             IDMError idmerror = new IDMError();
93             idmerror.setMessage("Internal error getting domains");
94             idmerror.setDetails(se.getMessage());
95             return Response.status(500).entity(idmerror).build();
96         }
97         return Response.ok(domains).build();
98     }
99
100     /**
101      * Extracts the domain represented by <code>domainId</code>.
102      *
103      * @param domainId the string domain (i.e., "sdn")
104      * @return a response with the specified domain
105      */
106     @GET
107     @Path("/{id}")
108     @Produces("application/json")
109     public Response getDomain(@PathParam("id") String domainId) {
110         LOG.info("Get /domains/{}", domainId);
111         Domain domain = null;
112         try {
113             domain = AAAIDMLightModule.getStore().readDomain(domainId);
114         } catch (IDMStoreException se) {
115             LOG.error("StoreException: ", se);
116             IDMError idmerror = new IDMError();
117             idmerror.setMessage("Internal error getting domain");
118             idmerror.setDetails(se.getMessage());
119             return Response.status(500).entity(idmerror).build();
120         }
121
122         if (domain == null) {
123             IDMError idmerror = new IDMError();
124             idmerror.setMessage("Not found! domain id :" + domainId);
125             return Response.status(404).entity(idmerror).build();
126         }
127         return Response.ok(domain).build();
128     }
129
130     /**
131      * Creates a domain.  The name attribute is required for domain creation.
132      * Enabled and description fields are optional.  Optional fields default
133      * in the following manner:
134      * <code>enabled</code>: <code>false</code>
135      * <code>description</code>: An empty string (<code>""</code>).
136      *
137      * @param info passed from Jersey
138      * @param domain designated by the REST payload
139      * @return A response stating success or failure of domain creation.
140      */
141     @POST
142     @Consumes("application/json")
143     @Produces("application/json")
144     public Response createDomain(@Context UriInfo info, Domain domain) {
145         LOG.info("Post /domains");
146         try {
147             if (domain.isEnabled() == null) {
148                 domain.setEnabled(false);
149             }
150             if (domain.getName() == null) {
151                 domain.setName("");
152             }
153             if (domain.getDescription() == null) {
154                 domain.setDescription("");
155             }
156             domain = AAAIDMLightModule.getStore().writeDomain(domain);
157         } catch (IDMStoreException se) {
158             LOG.error("StoreException: ", se);
159             IDMError idmerror = new IDMError();
160             idmerror.setMessage("Internal error creating domain");
161             idmerror.setDetails(se.getMessage());
162             return Response.status(500).entity(idmerror).build();
163         }
164         return Response.status(201).entity(domain).build();
165     }
166
167     /**
168      * Updates a domain.
169      *
170      * @param info passed from Jersey
171      * @param domain the REST payload
172      * @param domainId the last part of the path, containing the specified domain id
173      * @return A response stating success or failure of domain update.
174      */
175     @PUT
176     @Path("/{id}")
177     @Consumes("application/json")
178     @Produces("application/json")
179     public Response putDomain(@Context UriInfo info, Domain domain, @PathParam("id") String domainId) {
180         LOG.info("Put /domains/{}", domainId);
181         try {
182             domain.setDomainid(domainId);
183             domain = AAAIDMLightModule.getStore().updateDomain(domain);
184             if (domain == null) {
185                 IDMError idmerror = new IDMError();
186                 idmerror.setMessage("Not found! Domain id :" + domainId);
187                 return Response.status(404).entity(idmerror).build();
188             }
189             IdmLightProxy.clearClaimCache();
190             return Response.status(200).entity(domain).build();
191         } catch (IDMStoreException se) {
192             LOG.error("StoreException: ", se);
193             IDMError idmerror = new IDMError();
194             idmerror.setMessage("Internal error putting domain");
195             idmerror.setDetails(se.getMessage());
196             return Response.status(500).entity(idmerror).build();
197         }
198     }
199
200     /**
201      * Deletes a domain.
202      *
203      * @param info passed from Jersey
204      * @param domainId the last part of the path, containing the specified domain id
205      * @return A response stating success or failure of domain deletion.
206      */
207     @DELETE
208     @Path("/{id}")
209     public Response deleteDomain(@Context UriInfo info, @PathParam("id") String domainId) {
210         LOG.info("Delete /domains/{}", domainId);
211
212         try {
213             Domain domain = AAAIDMLightModule.getStore().deleteDomain(domainId);
214             if (domain == null) {
215                 IDMError idmerror = new IDMError();
216                 idmerror.setMessage("Not found! Domain id :" + domainId);
217                 return Response.status(404).entity(idmerror).build();
218             }
219         } catch (IDMStoreException se) {
220             LOG.error("StoreException: ", se);
221             IDMError idmerror = new IDMError();
222             idmerror.setMessage("Internal error deleting Domain");
223             idmerror.setDetails(se.getMessage());
224             return Response.status(500).entity(idmerror).build();
225         }
226         IdmLightProxy.clearClaimCache();
227         return Response.status(204).build();
228     }
229
230     /**
231      * Creates a grant.  A grant defines the role a particular user is given on
232      * a particular domain.  For example, by default, AAA installs a grant for
233      * the "admin" user, granting permission to act with "admin" role on the
234      * "sdn" domain.
235      *
236      * @param info passed from Jersey
237      * @param domainId the domain the user is allowed to access
238      * @param userId the user that is allowed to access the domain
239      * @param grant the payload containing role access controls
240      * @return A response stating success or failure of grant creation.
241      */
242     @POST
243     @Path("/{did}/users/{uid}/roles")
244     @Consumes("application/json")
245     @Produces("application/json")
246     public Response createGrant(@Context UriInfo info, @PathParam("did") String domainId,
247             @PathParam("uid") String userId, Grant grant) {
248         LOG.info("Post /domains/{}/users/{}/roles", domainId, userId);
249         Domain domain = null;
250         User user = null;
251         Role role = null;
252         String roleId = null;
253
254         // validate domain id
255         try {
256             domain = AAAIDMLightModule.getStore().readDomain(domainId);
257         } catch (IDMStoreException se) {
258             LOG.error("StoreException: ", se);
259             IDMError idmerror = new IDMError();
260             idmerror.setMessage("Internal error getting domain");
261             idmerror.setDetails(se.getMessage());
262             return Response.status(500).entity(idmerror).build();
263         }
264         if (domain == null) {
265             IDMError idmerror = new IDMError();
266             idmerror.setMessage("Not found! domain id :" + domainId);
267             return Response.status(404).entity(idmerror).build();
268         }
269         grant.setDomainid(domainId);
270
271         try {
272             user = AAAIDMLightModule.getStore().readUser(userId);
273         } catch (IDMStoreException se) {
274             LOG.error("StoreException: ", se);
275             IDMError idmerror = new IDMError();
276             idmerror.setMessage("Internal error getting user");
277             idmerror.setDetails(se.getMessage());
278             return Response.status(500).entity(idmerror).build();
279         }
280         if (user == null) {
281             IDMError idmerror = new IDMError();
282             idmerror.setMessage("Not found! User id :" + userId);
283             return Response.status(404).entity(idmerror).build();
284         }
285         grant.setUserid(userId);
286
287         // validate role id
288         try {
289             roleId = grant.getRoleid();
290             LOG.info("roleid = {}", roleId);
291         } catch (NumberFormatException nfe) {
292             IDMError idmerror = new IDMError();
293             idmerror.setMessage("Invalid Role id :" + grant.getRoleid());
294             return Response.status(404).entity(idmerror).build();
295         }
296         try {
297             role = AAAIDMLightModule.getStore().readRole(roleId);
298         } catch (IDMStoreException se) {
299             LOG.error("StoreException: ", se);
300             IDMError idmerror = new IDMError();
301             idmerror.setMessage("Internal error getting role");
302             idmerror.setDetails(se.getMessage());
303             return Response.status(500).entity(idmerror).build();
304         }
305         if (role == null) {
306             IDMError idmerror = new IDMError();
307             idmerror.setMessage("Not found! role :" + grant.getRoleid());
308             return Response.status(404).entity(idmerror).build();
309         }
310
311         // see if grant already exists for this
312         try {
313             Grant existingGrant = AAAIDMLightModule.getStore().readGrant(domainId, userId, roleId);
314             if (existingGrant != null) {
315                 IDMError idmerror = new IDMError();
316                 idmerror.setMessage("Grant already exists for did:" + domainId + " uid:" + userId
317                         + " rid:" + roleId);
318                 return Response.status(403).entity(idmerror).build();
319             }
320         } catch (IDMStoreException se) {
321             LOG.error("StoreException: ", se);
322             IDMError idmerror = new IDMError();
323             idmerror.setMessage("Internal error creating grant");
324             idmerror.setDetails(se.getMessage());
325             return Response.status(500).entity(idmerror).build();
326         }
327
328         // create grant
329         try {
330             grant = AAAIDMLightModule.getStore().writeGrant(grant);
331         } catch (IDMStoreException se) {
332             LOG.error("StoreException: ", se);
333             IDMError idmerror = new IDMError();
334             idmerror.setMessage("Internal error creating grant");
335             idmerror.setDetails(se.getMessage());
336             return Response.status(500).entity(idmerror).build();
337         }
338
339         IdmLightProxy.clearClaimCache();
340         return Response.status(201).entity(grant).build();
341     }
342
343     /**
344      * Used to validate user access.
345      *
346      * @param info passed from Jersey
347      * @param domainId the domain in question
348      * @param userpwd the password attempt
349      * @return A response stating success or failure of user validation.
350      */
351     @POST
352     @Path("/{did}/users/roles")
353     @Consumes("application/json")
354     @Produces("application/json")
355     public Response validateUser(@Context UriInfo info, @PathParam("did") String domainId,
356             UserPwd userpwd) {
357
358         LOG.info("GET /domains/{}/users", domainId);
359         Domain domain = null;
360         Claim claim = new Claim();
361         List<Role> roleList = new ArrayList<Role>();
362
363         try {
364             domain = AAAIDMLightModule.getStore().readDomain(domainId);
365         } catch (IDMStoreException se) {
366             LOG.error("StoreException: ", se);
367             IDMError idmerror = new IDMError();
368             idmerror.setMessage("Internal error getting domain");
369             idmerror.setDetails(se.getMessage());
370             return Response.status(500).entity(idmerror).build();
371         }
372         if (domain == null) {
373             IDMError idmerror = new IDMError();
374             idmerror.setMessage("Not found! Domain id :" + domainId);
375             return Response.status(404).entity(idmerror).build();
376         }
377
378         // check request body for username and pwd
379         String username = userpwd.getUsername();
380         if (username == null) {
381             IDMError idmerror = new IDMError();
382             idmerror.setMessage("username not specfied in request body");
383             return Response.status(400).entity(idmerror).build();
384         }
385         String pwd = userpwd.getUserpwd();
386         if (pwd == null) {
387             IDMError idmerror = new IDMError();
388             idmerror.setMessage("userpwd not specfied in request body");
389             return Response.status(400).entity(idmerror).build();
390         }
391
392         // find userid for user
393         try {
394             Users users = AAAIDMLightModule.getStore().getUsers(username, domainId);
395             List<User> userList = users.getUsers();
396             if (userList.size() == 0) {
397                 IDMError idmerror = new IDMError();
398                 idmerror.setMessage("did not find username: " + username);
399                 return Response.status(404).entity(idmerror).build();
400             }
401             User user = userList.get(0);
402             String userPwd = user.getPassword();
403             String reqPwd = userpwd.getUserpwd();
404             if (!userPwd.equals(reqPwd)) {
405                 IDMError idmerror = new IDMError();
406                 idmerror.setMessage("password does not match for username: " + username);
407                 return Response.status(401).entity(idmerror).build();
408             }
409             claim.setDomainid(domainId);
410             claim.setUsername(username);
411             claim.setUserid(user.getUserid());
412             try {
413                 Grants grants = AAAIDMLightModule.getStore().getGrants(domainId, user.getUserid());
414                 List<Grant> grantsList = grants.getGrants();
415                 for (int i = 0; i < grantsList.size(); i++) {
416                     Grant grant = grantsList.get(i);
417                     Role role = AAAIDMLightModule.getStore().readRole(grant.getRoleid());
418                     roleList.add(role);
419                 }
420             } catch (IDMStoreException se) {
421                 LOG.error("StoreException: ", se);
422                 IDMError idmerror = new IDMError();
423                 idmerror.setMessage("Internal error getting Roles");
424                 idmerror.setDetails(se.getMessage());
425                 return Response.status(500).entity(idmerror).build();
426             }
427             claim.setRoles(roleList);
428         } catch (IDMStoreException se) {
429             LOG.error("StoreException: ", se);
430             IDMError idmerror = new IDMError();
431             idmerror.setMessage("Internal error getting user");
432             idmerror.setDetails(se.getMessage());
433             return Response.status(500).entity(idmerror).build();
434         }
435
436         return Response.ok(claim).build();
437     }
438
439     /**
440      * Get the grants for a user on a domain.
441      *
442      * @param info passed from Jersey
443      * @param domainId the domain in question
444      * @param userId the user in question
445      * @return A response containing the grants for a user on a domain.
446      */
447     @GET
448     @Path("/{did}/users/{uid}/roles")
449     @Produces("application/json")
450     public Response getRoles(@Context UriInfo info, @PathParam("did") String domainId,
451             @PathParam("uid") String userId) {
452         LOG.info("GET /domains/{}/users/{}/roles", domainId, userId);
453         Domain domain = null;
454         User user = null;
455         Roles roles = new Roles();
456         List<Role> roleList = new ArrayList<Role>();
457
458         try {
459             domain = AAAIDMLightModule.getStore().readDomain(domainId);
460         } catch (IDMStoreException se) {
461             LOG.error("StoreException: ", se);
462             IDMError idmerror = new IDMError();
463             idmerror.setMessage("Internal error getting domain");
464             idmerror.setDetails(se.getMessage());
465             return Response.status(500).entity(idmerror).build();
466         }
467         if (domain == null) {
468             IDMError idmerror = new IDMError();
469             idmerror.setMessage("Not found! Domain id :" + domainId);
470             return Response.status(404).entity(idmerror).build();
471         }
472
473         try {
474             user = AAAIDMLightModule.getStore().readUser(userId);
475         } catch (IDMStoreException se) {
476             LOG.error("StoreException: ", se);
477             IDMError idmerror = new IDMError();
478             idmerror.setMessage("Internal error getting user");
479             idmerror.setDetails(se.getMessage());
480             return Response.status(500).entity(idmerror).build();
481         }
482         if (user == null) {
483             IDMError idmerror = new IDMError();
484             idmerror.setMessage("Not found! User id :" + userId);
485             return Response.status(404).entity(idmerror).build();
486         }
487
488         try {
489             Grants grants = AAAIDMLightModule.getStore().getGrants(domainId, userId);
490             List<Grant> grantsList = grants.getGrants();
491             for (int i = 0; i < grantsList.size(); i++) {
492                 Grant grant = grantsList.get(i);
493                 Role role = AAAIDMLightModule.getStore().readRole(grant.getRoleid());
494                 roleList.add(role);
495             }
496         } catch (IDMStoreException se) {
497             LOG.error("StoreException: ", se);
498             IDMError idmerror = new IDMError();
499             idmerror.setMessage("Internal error getting Roles");
500             idmerror.setDetails(se.getMessage());
501             return Response.status(500).entity(idmerror).build();
502         }
503
504         roles.setRoles(roleList);
505         return Response.ok(roles).build();
506     }
507
508     /**
509      * Delete a grant.
510      *
511      * @param info passed from Jersey
512      * @param domainId the domain for the grant
513      * @param userId the user for the grant
514      * @param roleId the role for the grant
515      * @return A response stating success or failure of the grant deletion.
516      */
517     @DELETE
518     @Path("/{did}/users/{uid}/roles/{rid}")
519     public Response deleteGrant(@Context UriInfo info, @PathParam("did") String domainId,
520             @PathParam("uid") String userId, @PathParam("rid") String roleId) {
521         Domain domain = null;
522         User user = null;
523         Role role = null;
524
525         try {
526             domain = AAAIDMLightModule.getStore().readDomain(domainId);
527         } catch (IDMStoreException se) {
528             LOG.error("Error deleting Grant  : ", se);
529             IDMError idmerror = new IDMError();
530             idmerror.setMessage("Internal error getting domain");
531             idmerror.setDetails(se.getMessage());
532             return Response.status(500).entity(idmerror).build();
533         }
534         if (domain == null) {
535             IDMError idmerror = new IDMError();
536             idmerror.setMessage("Not found! Domain id :" + domainId);
537             return Response.status(404).entity(idmerror).build();
538         }
539
540         try {
541             user = AAAIDMLightModule.getStore().readUser(userId);
542         } catch (IDMStoreException se) {
543             LOG.error("StoreException : ", se);
544             IDMError idmerror = new IDMError();
545             idmerror.setMessage("Internal error getting user");
546             idmerror.setDetails(se.getMessage());
547             return Response.status(500).entity(idmerror).build();
548         }
549         if (user == null) {
550             IDMError idmerror = new IDMError();
551             idmerror.setMessage("Not found! User id :" + userId);
552             return Response.status(404).entity(idmerror).build();
553         }
554
555         try {
556             role = AAAIDMLightModule.getStore().readRole(roleId);
557         } catch (IDMStoreException se) {
558             LOG.error("StoreException: ", se);
559             IDMError idmerror = new IDMError();
560             idmerror.setMessage("Internal error getting Role");
561             idmerror.setDetails(se.getMessage());
562             return Response.status(500).entity(idmerror).build();
563         }
564         if (role == null) {
565             IDMError idmerror = new IDMError();
566             idmerror.setMessage("Not found! Role id :" + roleId);
567             return Response.status(404).entity(idmerror).build();
568         }
569
570         // see if grant already exists
571         try {
572             Grant existingGrant = AAAIDMLightModule.getStore().readGrant(domainId, userId, roleId);
573             if (existingGrant == null) {
574                 IDMError idmerror = new IDMError();
575                 idmerror.setMessage("Grant does not exist for did:" + domainId + " uid:" + userId
576                         + " rid:" + roleId);
577                 return Response.status(404).entity(idmerror).build();
578             }
579             existingGrant = AAAIDMLightModule.getStore().deleteGrant(existingGrant.getGrantid());
580         } catch (IDMStoreException se) {
581             LOG.error("StoreException: ", se);
582             IDMError idmerror = new IDMError();
583             idmerror.setMessage("Internal error creating grant");
584             idmerror.setDetails(se.getMessage());
585             return Response.status(500).entity(idmerror).build();
586         }
587         IdmLightProxy.clearClaimCache();
588         return Response.status(204).build();
589     }
590
591 }