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
8 package org.opendaylight.aaa.shiro.idm;
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;
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;
44 * REST application used to manipulate the H2 database domains table. The REST
45 * endpoint is <code>/auth/v1/domains</code>.
48 * A wrapper script called <code>idmtool</code> is provided to manipulate AAA
51 * @author peter.mellquist@hp.com
54 public class DomainHandler {
55 private static final Logger LOG = LoggerFactory.getLogger(DomainHandler.class);
57 private final IIDMStore iidMStore;
58 private final ClaimCache claimCache;
60 public DomainHandler(final IIDMStore iidMStore, final ClaimCache claimCache) {
61 this.iidMStore = requireNonNull(iidMStore);
62 this.claimCache = requireNonNull(claimCache);
66 * Extracts all domains.
68 * @return a response with all domains stored in the H2 database
71 @Produces(MediaType.APPLICATION_JSON)
72 public Response getDomains() {
73 LOG.info("Get /domains");
74 final Domains domains;
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();
84 return Response.ok(domains).build();
88 * Extracts the domain represented by <code>domainId</code>.
91 * the string domain (i.e., "sdn")
92 * @return a response with the specified domain
96 @Produces(MediaType.APPLICATION_JSON)
97 public Response getDomain(@PathParam("id") final String domainId) {
98 LOG.info("Get /domains/{}", domainId);
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();
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();
115 return Response.ok(domain).build();
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>).
127 * designated by the REST payload
128 * @return A response stating success or failure of domain creation.
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");
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();
144 if (domain.isEnabled() == null) {
145 domain.setEnabled(false);
147 if (domain.getName() == null) {
150 if (domain.getDescription() == null) {
151 domain.setDescription("");
154 final Domain newDomain;
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();
164 return Response.status(CREATED).entity(newDomain).build();
175 * the last part of the path, containing the specified domain id
176 * @return A response stating success or failure of domain update.
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);
186 domain.setDomainid(domainId);
187 final Domain newDomain;
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();
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();
204 return Response.ok(newDomain).build();
213 * the last part of the path, containing the specified domain id
214 * @return A response stating success or failure of domain deletion.
218 public Response deleteDomain(@Context final UriInfo info, @PathParam("id") final String domainId) {
219 LOG.info("Delete /domains/{}", domainId);
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();
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();
238 return Response.noContent().build();
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"
250 * the domain the user is allowed to access
252 * the user that is allowed to access the domain
254 * the payload containing role access controls
255 * @return A response stating success or failure of grant creation.
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);
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();
274 // validate domain id
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();
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();
290 grant.setDomainid(domainId);
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();
303 IDMError idmerror = new IDMError();
304 idmerror.setMessage("Not found! User id:" + userId);
305 return Response.status(NOT_FOUND).entity(idmerror).build();
307 grant.setUserid(userId);
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();
318 LOG.info("roleid = {}", roleId);
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();
331 IDMError idmerror = new IDMError();
332 idmerror.setMessage("Not found! role:" + grant.getRoleid());
333 return Response.status(NOT_FOUND).entity(idmerror).build();
336 // see if grant already exists for this
337 final Grant existingGrant;
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();
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();
354 final Grant newGrant;
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();
366 return Response.status(CREATED).entity(newGrant).build();
370 * Get the grants for a user on a domain.
375 * the domain in question
377 * the user in question
378 * @return A response containing the grants for a user on a domain.
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);
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();
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();
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();
414 IDMError idmerror = new IDMError();
415 idmerror.setMessage("Not found! User id:" + userId);
416 return Response.status(NOT_FOUND).entity(idmerror).build();
419 final var roleList = new ArrayList<Role>();
421 for (Grant grant : iidMStore.getGrants(domainId, userId).getGrants()) {
422 roleList.add(iidMStore.readRole(grant.getRoleid()));
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();
431 Roles roles = new Roles();
432 roles.setRoles(roleList);
433 return Response.ok(roles).build();
442 * the domain for the grant
444 * the user for the grant
446 * the role for the grant
447 * @return A response stating success or failure of the grant deletion.
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) {
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();
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();
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();
480 IDMError idmerror = new IDMError();
481 idmerror.setMessage("Not found! User id:" + userId);
482 return Response.status(NOT_FOUND).entity(idmerror).build();
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();
496 IDMError idmerror = new IDMError();
497 idmerror.setMessage("Not found! Role id:" + roleId);
498 return Response.status(NOT_FOUND).entity(idmerror).build();
501 // see if grant already exists
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();
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();
518 return Response.noContent().build();