Remove API to validate user access
[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 package org.opendaylight.aaa.shiro.idm;
9
10 import static java.util.Objects.requireNonNull;
11 import static javax.ws.rs.core.Response.Status.BAD_REQUEST;
12 import static javax.ws.rs.core.Response.Status.CREATED;
13 import static javax.ws.rs.core.Response.Status.FORBIDDEN;
14 import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR;
15 import static javax.ws.rs.core.Response.Status.NOT_FOUND;
16
17 import java.util.ArrayList;
18 import javax.ws.rs.Consumes;
19 import javax.ws.rs.DELETE;
20 import javax.ws.rs.GET;
21 import javax.ws.rs.POST;
22 import javax.ws.rs.PUT;
23 import javax.ws.rs.Path;
24 import javax.ws.rs.PathParam;
25 import javax.ws.rs.Produces;
26 import javax.ws.rs.core.Context;
27 import javax.ws.rs.core.MediaType;
28 import javax.ws.rs.core.Response;
29 import javax.ws.rs.core.UriInfo;
30 import org.opendaylight.aaa.api.ClaimCache;
31 import org.opendaylight.aaa.api.IDMStoreException;
32 import org.opendaylight.aaa.api.IIDMStore;
33 import org.opendaylight.aaa.api.model.Domain;
34 import org.opendaylight.aaa.api.model.Domains;
35 import org.opendaylight.aaa.api.model.Grant;
36 import org.opendaylight.aaa.api.model.IDMError;
37 import org.opendaylight.aaa.api.model.Role;
38 import org.opendaylight.aaa.api.model.Roles;
39 import org.opendaylight.aaa.api.model.User;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
42
43 /**
44  * REST application used to manipulate the H2 database domains table. The REST
45  * endpoint is <code>/auth/v1/domains</code>.
46  *
47  * <p>
48  * A wrapper script called <code>idmtool</code> is provided to manipulate AAA
49  * data.
50  *
51  * @author peter.mellquist@hp.com
52  */
53 @Path("/v1/domains")
54 public class DomainHandler {
55     private static final Logger LOG = LoggerFactory.getLogger(DomainHandler.class);
56
57     private final IIDMStore iidMStore;
58     private final ClaimCache claimCache;
59
60     public DomainHandler(final IIDMStore iidMStore, final ClaimCache claimCache) {
61         this.iidMStore = requireNonNull(iidMStore);
62         this.claimCache = requireNonNull(claimCache);
63     }
64
65     /**
66      * Extracts all domains.
67      *
68      * @return a response with all domains stored in the H2 database
69      */
70     @GET
71     @Produces(MediaType.APPLICATION_JSON)
72     public Response getDomains() {
73         LOG.info("Get /domains");
74         final Domains domains;
75         try {
76             domains = iidMStore.getDomains();
77         } catch (IDMStoreException e) {
78             LOG.error("StoreException", e);
79             IDMError idmerror = new IDMError();
80             idmerror.setMessage("Internal error getting domains");
81             idmerror.setDetails(e.getMessage());
82             return Response.status(INTERNAL_SERVER_ERROR).entity(idmerror).build();
83         }
84         return Response.ok(domains).build();
85     }
86
87     /**
88      * Extracts the domain represented by <code>domainId</code>.
89      *
90      * @param domainId
91      *            the string domain (i.e., "sdn")
92      * @return a response with the specified domain
93      */
94     @GET
95     @Path("/{id}")
96     @Produces(MediaType.APPLICATION_JSON)
97     public Response getDomain(@PathParam("id") final String domainId) {
98         LOG.info("Get /domains/{}", domainId);
99         final Domain domain;
100         try {
101             domain = iidMStore.readDomain(domainId);
102         } catch (IDMStoreException e) {
103             LOG.error("StoreException", e);
104             IDMError idmerror = new IDMError();
105             idmerror.setMessage("Internal error getting domain");
106             idmerror.setDetails(e.getMessage());
107             return Response.status(INTERNAL_SERVER_ERROR).entity(idmerror).build();
108         }
109
110         if (domain == null) {
111             IDMError idmerror = new IDMError();
112             idmerror.setMessage("Not found! domain id :" + domainId);
113             return Response.status(NOT_FOUND).entity(idmerror).build();
114         }
115         return Response.ok(domain).build();
116     }
117
118     /**
119      * Creates a domain. The name attribute is required for domain creation.
120      * Enabled and description fields are optional. Optional fields default in
121      * the following manner: <code>enabled</code>: <code>false</code>
122      * <code>description</code>: An empty string (<code>""</code>).
123      *
124      * @param info
125      *            passed from Jersey
126      * @param domain
127      *            designated by the REST payload
128      * @return A response stating success or failure of domain creation.
129      */
130     @POST
131     @Consumes(MediaType.APPLICATION_JSON)
132     @Produces(MediaType.APPLICATION_JSON)
133     public Response createDomain(@Context final UriInfo info, final Domain domain) {
134         LOG.info("Post /domains");
135
136         // Bug 8382: domain id is an implementation detail and isn't specifiable
137         if (domain.getDomainid() != null) {
138             final String errorMessage = "do not specify domainId, it will be assigned automatically for you";
139             LOG.debug(errorMessage);
140             final IDMError idmError = new IDMError();
141             idmError.setMessage(errorMessage);
142             return Response.status(BAD_REQUEST).entity(idmError).build();
143         }
144         if (domain.isEnabled() == null) {
145             domain.setEnabled(false);
146         }
147         if (domain.getName() == null) {
148             domain.setName("");
149         }
150         if (domain.getDescription() == null) {
151             domain.setDescription("");
152         }
153
154         final Domain newDomain;
155         try {
156             newDomain = iidMStore.writeDomain(domain);
157         } catch (IDMStoreException e) {
158             LOG.error("StoreException", e);
159             IDMError idmerror = new IDMError();
160             idmerror.setMessage("Internal error creating domain");
161             idmerror.setDetails(e.getMessage());
162             return Response.status(INTERNAL_SERVER_ERROR).entity(idmerror).build();
163         }
164         return Response.status(CREATED).entity(newDomain).build();
165     }
166
167     /**
168      * Updates a domain.
169      *
170      * @param info
171      *            passed from Jersey
172      * @param domain
173      *            the REST payload
174      * @param domainId
175      *            the last part of the path, containing the specified domain id
176      * @return A response stating success or failure of domain update.
177      */
178     @PUT
179     @Path("/{id}")
180     @Consumes(MediaType.APPLICATION_JSON)
181     @Produces(MediaType.APPLICATION_JSON)
182     public Response putDomain(@Context final UriInfo info, final Domain domain,
183             @PathParam("id") final String domainId) {
184         LOG.info("Put /domains/{}", domainId);
185
186         domain.setDomainid(domainId);
187         final Domain newDomain;
188         try {
189             newDomain = iidMStore.updateDomain(domain);
190         } catch (IDMStoreException e) {
191             LOG.error("StoreException", e);
192             IDMError idmerror = new IDMError();
193             idmerror.setMessage("Internal error putting domain");
194             idmerror.setDetails(e.getMessage());
195             return Response.status(INTERNAL_SERVER_ERROR).entity(idmerror).build();
196         }
197
198         if (newDomain == null) {
199             IDMError idmerror = new IDMError();
200             idmerror.setMessage("Not found! Domain id:" + domainId);
201             return Response.status(NOT_FOUND).entity(idmerror).build();
202         }
203         claimCache.clear();
204         return Response.ok(newDomain).build();
205     }
206
207     /**
208      * Deletes a domain.
209      *
210      * @param info
211      *            passed from Jersey
212      * @param domainId
213      *            the last part of the path, containing the specified domain id
214      * @return A response stating success or failure of domain deletion.
215      */
216     @DELETE
217     @Path("/{id}")
218     public Response deleteDomain(@Context final UriInfo info, @PathParam("id") final String domainId) {
219         LOG.info("Delete /domains/{}", domainId);
220
221         final Domain domain;
222         try {
223             domain = iidMStore.deleteDomain(domainId);
224         } catch (IDMStoreException e) {
225             LOG.error("StoreException", e);
226             IDMError idmerror = new IDMError();
227             idmerror.setMessage("Internal error deleting Domain");
228             idmerror.setDetails(e.getMessage());
229             return Response.status(INTERNAL_SERVER_ERROR).entity(idmerror).build();
230         }
231
232         if (domain == null) {
233             IDMError idmerror = new IDMError();
234             idmerror.setMessage("Not found! Domain id:" + domainId);
235             return Response.status(NOT_FOUND).entity(idmerror).build();
236         }
237         claimCache.clear();
238         return Response.noContent().build();
239     }
240
241     /**
242      * Creates a grant. A grant defines the role a particular user is given on a
243      * particular domain. For example, by default, AAA installs a grant for the
244      * "admin" user, granting permission to act with "admin" role on the "sdn"
245      * domain.
246      *
247      * @param info
248      *            passed from Jersey
249      * @param domainId
250      *            the domain the user is allowed to access
251      * @param userId
252      *            the user that is allowed to access the domain
253      * @param grant
254      *            the payload containing role access controls
255      * @return A response stating success or failure of grant creation.
256      */
257     @POST
258     @Path("/{did}/users/{uid}/roles")
259     @Consumes(MediaType.APPLICATION_JSON)
260     @Produces(MediaType.APPLICATION_JSON)
261     public Response createGrant(@Context final UriInfo info, @PathParam("did") final String domainId,
262             @PathParam("uid") final String userId, final Grant grant) {
263         LOG.info("Post /domains/{}/users/{}/roles", domainId, userId);
264
265         // Bug 8382: grant id is an implementation detail and isn't specifiable
266         if (grant.getGrantid() != null) {
267             final String errorMessage = "do not specify grantId, it will be assigned automatically for you";
268             LOG.debug(errorMessage);
269             final IDMError idmError = new IDMError();
270             idmError.setMessage(errorMessage);
271             return Response.status(BAD_REQUEST).entity(idmError).build();
272         }
273
274         // validate domain id
275         final Domain domain;
276         try {
277             domain = iidMStore.readDomain(domainId);
278         } catch (IDMStoreException e) {
279             LOG.error("StoreException", e);
280             IDMError idmerror = new IDMError();
281             idmerror.setMessage("Internal error getting domain");
282             idmerror.setDetails(e.getMessage());
283             return Response.status(INTERNAL_SERVER_ERROR).entity(idmerror).build();
284         }
285         if (domain == null) {
286             IDMError idmerror = new IDMError();
287             idmerror.setMessage("Not found! domain id :" + domainId);
288             return Response.status(NOT_FOUND).entity(idmerror).build();
289         }
290         grant.setDomainid(domainId);
291
292         final User user;
293         try {
294             user = iidMStore.readUser(userId);
295         } catch (IDMStoreException e) {
296             LOG.error("StoreException", e);
297             IDMError idmerror = new IDMError();
298             idmerror.setMessage("Internal error getting user");
299             idmerror.setDetails(e.getMessage());
300             return Response.status(INTERNAL_SERVER_ERROR).entity(idmerror).build();
301         }
302         if (user == null) {
303             IDMError idmerror = new IDMError();
304             idmerror.setMessage("Not found! User id:" + userId);
305             return Response.status(NOT_FOUND).entity(idmerror).build();
306         }
307         grant.setUserid(userId);
308
309         // validate role id
310         final String roleId;
311         try {
312             roleId = grant.getRoleid();
313         } catch (NumberFormatException nfe) {
314             IDMError idmerror = new IDMError();
315             idmerror.setMessage("Invalid Role id:" + grant.getRoleid());
316             return Response.status(NOT_FOUND).entity(idmerror).build();
317         }
318         LOG.info("roleid = {}", roleId);
319
320         final Role role;
321         try {
322             role = iidMStore.readRole(roleId);
323         } catch (IDMStoreException e) {
324             LOG.error("StoreException", e);
325             IDMError idmerror = new IDMError();
326             idmerror.setMessage("Internal error getting role");
327             idmerror.setDetails(e.getMessage());
328             return Response.status(INTERNAL_SERVER_ERROR).entity(idmerror).build();
329         }
330         if (role == null) {
331             IDMError idmerror = new IDMError();
332             idmerror.setMessage("Not found! role:" + grant.getRoleid());
333             return Response.status(NOT_FOUND).entity(idmerror).build();
334         }
335
336         // see if grant already exists for this
337         final Grant existingGrant;
338         try {
339             existingGrant = iidMStore.readGrant(domainId, userId, roleId);
340         } catch (IDMStoreException e) {
341             LOG.error("StoreException", e);
342             IDMError idmerror = new IDMError();
343             idmerror.setMessage("Internal error creating grant");
344             idmerror.setDetails(e.getMessage());
345             return Response.status(INTERNAL_SERVER_ERROR).entity(idmerror).build();
346         }
347         if (existingGrant != null) {
348             IDMError idmerror = new IDMError();
349             idmerror.setMessage("Grant already exists for did:" + domainId + " uid:" + userId + " rid:" + roleId);
350             return Response.status(FORBIDDEN).entity(idmerror).build();
351         }
352
353         // create grant
354         final Grant newGrant;
355         try {
356             newGrant = iidMStore.writeGrant(grant);
357         } catch (IDMStoreException e) {
358             LOG.error("StoreException: ", e);
359             IDMError idmerror = new IDMError();
360             idmerror.setMessage("Internal error creating grant");
361             idmerror.setDetails(e.getMessage());
362             return Response.status(INTERNAL_SERVER_ERROR).entity(idmerror).build();
363         }
364
365         claimCache.clear();
366         return Response.status(CREATED).entity(newGrant).build();
367     }
368
369     /**
370      * Get the grants for a user on a domain.
371      *
372      * @param info
373      *            passed from Jersey
374      * @param domainId
375      *            the domain in question
376      * @param userId
377      *            the user in question
378      * @return A response containing the grants for a user on a domain.
379      */
380     @GET
381     @Path("/{did}/users/{uid}/roles")
382     @Produces(MediaType.APPLICATION_JSON)
383     public Response getRoles(@Context final UriInfo info, @PathParam("did") final String domainId,
384             @PathParam("uid") final String userId) {
385         LOG.info("GET /domains/{}/users/{}/roles", domainId, userId);
386
387         final Domain domain;
388         try {
389             domain = iidMStore.readDomain(domainId);
390         } catch (IDMStoreException e) {
391             LOG.error("StoreException", e);
392             IDMError idmerror = new IDMError();
393             idmerror.setMessage("Internal error getting domain");
394             idmerror.setDetails(e.getMessage());
395             return Response.status(INTERNAL_SERVER_ERROR).entity(idmerror).build();
396         }
397         if (domain == null) {
398             IDMError idmerror = new IDMError();
399             idmerror.setMessage("Not found! Domain id:" + domainId);
400             return Response.status(NOT_FOUND).entity(idmerror).build();
401         }
402
403         final User user;
404         try {
405             user = iidMStore.readUser(userId);
406         } catch (IDMStoreException e) {
407             LOG.error("StoreException", e);
408             IDMError idmerror = new IDMError();
409             idmerror.setMessage("Internal error getting user");
410             idmerror.setDetails(e.getMessage());
411             return Response.status(INTERNAL_SERVER_ERROR).entity(idmerror).build();
412         }
413         if (user == null) {
414             IDMError idmerror = new IDMError();
415             idmerror.setMessage("Not found! User id:" + userId);
416             return Response.status(NOT_FOUND).entity(idmerror).build();
417         }
418
419         final var roleList = new ArrayList<Role>();
420         try {
421             for (Grant grant : iidMStore.getGrants(domainId, userId).getGrants()) {
422                 roleList.add(iidMStore.readRole(grant.getRoleid()));
423             }
424         } catch (IDMStoreException e) {
425             LOG.error("StoreException", e);
426             IDMError idmerror = new IDMError();
427             idmerror.setMessage("Internal error getting Roles");
428             idmerror.setDetails(e.getMessage());
429             return Response.status(INTERNAL_SERVER_ERROR).entity(idmerror).build();
430         }
431         Roles roles = new Roles();
432         roles.setRoles(roleList);
433         return Response.ok(roles).build();
434     }
435
436     /**
437      * Delete a grant.
438      *
439      * @param info
440      *            passed from Jersey
441      * @param domainId
442      *            the domain for the grant
443      * @param userId
444      *            the user for the grant
445      * @param roleId
446      *            the role for the grant
447      * @return A response stating success or failure of the grant deletion.
448      */
449     @DELETE
450     @Path("/{did}/users/{uid}/roles/{rid}")
451     public Response deleteGrant(@Context final UriInfo info, @PathParam("did") final String domainId,
452             @PathParam("uid") final String userId, @PathParam("rid") final String roleId) {
453         final Domain domain;
454         try {
455             domain = iidMStore.readDomain(domainId);
456         } catch (IDMStoreException e) {
457             LOG.error("Error deleting Grant", e);
458             IDMError idmerror = new IDMError();
459             idmerror.setMessage("Internal error getting domain");
460             idmerror.setDetails(e.getMessage());
461             return Response.status(INTERNAL_SERVER_ERROR).entity(idmerror).build();
462         }
463         if (domain == null) {
464             IDMError idmerror = new IDMError();
465             idmerror.setMessage("Not found! Domain id:" + domainId);
466             return Response.status(NOT_FOUND).entity(idmerror).build();
467         }
468
469         final User user;
470         try {
471             user = iidMStore.readUser(userId);
472         } catch (IDMStoreException e) {
473             LOG.error("StoreException", e);
474             IDMError idmerror = new IDMError();
475             idmerror.setMessage("Internal error getting user");
476             idmerror.setDetails(e.getMessage());
477             return Response.status(INTERNAL_SERVER_ERROR).entity(idmerror).build();
478         }
479         if (user == null) {
480             IDMError idmerror = new IDMError();
481             idmerror.setMessage("Not found! User id:" + userId);
482             return Response.status(NOT_FOUND).entity(idmerror).build();
483         }
484
485         final Role role;
486         try {
487             role = iidMStore.readRole(roleId);
488         } catch (IDMStoreException e) {
489             LOG.error("StoreException", e);
490             IDMError idmerror = new IDMError();
491             idmerror.setMessage("Internal error getting Role");
492             idmerror.setDetails(e.getMessage());
493             return Response.status(INTERNAL_SERVER_ERROR).entity(idmerror).build();
494         }
495         if (role == null) {
496             IDMError idmerror = new IDMError();
497             idmerror.setMessage("Not found! Role id:" + roleId);
498             return Response.status(NOT_FOUND).entity(idmerror).build();
499         }
500
501         // see if grant already exists
502         try {
503             Grant existingGrant = iidMStore.readGrant(domainId, userId, roleId);
504             if (existingGrant == null) {
505                 IDMError idmerror = new IDMError();
506                 idmerror.setMessage("Grant does not exist for did:" + domainId + " uid:" + userId + " rid:" + roleId);
507                 return Response.status(NOT_FOUND).entity(idmerror).build();
508             }
509             iidMStore.deleteGrant(existingGrant.getGrantid());
510         } catch (IDMStoreException e) {
511             LOG.error("StoreException", e);
512             IDMError idmerror = new IDMError();
513             idmerror.setMessage("Internal error creating grant");
514             idmerror.setDetails(e.getMessage());
515             return Response.status(INTERNAL_SERVER_ERROR).entity(idmerror).build();
516         }
517         claimCache.clear();
518         return Response.noContent().build();
519     }
520 }