View Javadoc
1   package info.ejava.examples.secureping.rs;
2   
3   import javax.ejb.EJB;
4   import javax.ejb.EJBAccessException;
5   import javax.ws.rs.GET;
6   import javax.ws.rs.Path;
7   import javax.ws.rs.PathParam;
8   import javax.ws.rs.Produces;
9   import javax.ws.rs.core.Context;
10  import javax.ws.rs.core.MediaType;
11  import javax.ws.rs.core.Response;
12  import javax.ws.rs.core.Response.ResponseBuilder;
13  import javax.ws.rs.core.Response.Status;
14  import javax.ws.rs.core.SecurityContext;
15  import javax.ws.rs.core.UriInfo;
16  
17  import org.slf4j.Logger;
18  import org.slf4j.LoggerFactory;
19  
20  import info.ejava.examples.secureping.dto.PingResult;
21  import info.ejava.examples.secureping.ejb.SecurePing;
22  import info.ejava.examples.secureping.ejb.SecurePingLocal;
23  
24  /**
25   * This JAX-RS resource class acts as a HTTP facade for calls to the EJB tier. Declarative encryption 
26   * and authentication requirements are specified by the web.xml. This class primarily implements the structural
27   * URIs and proxies the user calls/responses to/from the EJBs. Note we could have made this a stateless session
28   * EJB and added @RolesAllowed to be enfored here. A pure JAX-RS resource class does not honor those declarative
29   * annotations.    
30   */
31  @Path("ping")
32  public class SecurePingResource {
33      @SuppressWarnings("unused")
34      private static final Logger logger = LoggerFactory.getLogger(SecurePingResource.class);
35      
36      //this injection requires CDI, which requires a WEB-INF/beans.xml file be in place to activate
37      //@EJB(lookup="ejb:securePingEAR/securePingEJB/SecurePingEJB!info.ejava.examples.secureping.ejb.SecurePingRemote")
38      @EJB(beanName="SecurePingEJB", beanInterface=SecurePingLocal.class)
39      private SecurePing secureService;
40      
41      @Context
42      private SecurityContext ctx;
43      @Context
44      private UriInfo uriInfo;
45      
46      @Path("whoAmI")
47      @GET
48      @Produces(MediaType.TEXT_PLAIN)
49      public Response whoAmI() {
50          ResponseBuilder rb = null;
51          try {
52              if (secureService!=null) {
53                  rb = Response.ok(secureService.whoAmI());
54              } else {
55                  rb = Response.serverError().entity("no ejb injected!!!"); 
56              }
57          } catch (Exception ex) {
58              rb=makeExceptionResponse(ex);
59          }
60          
61          return rb.build();
62      }
63      
64      @Path("roles/{role}")
65      @GET
66      @Produces(MediaType.TEXT_PLAIN)
67      public Response isCallerInRole(@PathParam("role") String role) {
68          ResponseBuilder rb = null;
69          try {
70              if (secureService!=null) {
71                  rb = Response.ok(secureService.isCallerInRole(role));
72              } else {
73                  rb = Response.serverError().entity("no ejb injected!!!"); 
74              }
75          } catch (Exception ex) {
76              rb=makeExceptionResponse(ex);
77          }
78          
79          return rb.build();        
80      }
81      
82      /**
83       * This method will return a sub-resource that is the same as the "secure" path except this path
84       * is configured to not require any identity or encryption.
85       * @return
86       */
87      @Path("unsecured")
88      public Pinger anonymous() {
89          return new Pinger();
90      }
91  
92      /**
93       * This method returns a sub-resource and will require that sub-resource to be envoked with 
94       * HTTPS and an authenticated user in either the "admin" or "user" role base on the web.xml.
95       * @return
96       */
97      @Path("secured")
98      public Pinger authenticated() {
99          return new Pinger();
100     }
101     
102     public class Pinger {
103         @Path("pingAdmin")
104         @GET
105         @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
106         public Response pingAdmin() {
107             ResponseBuilder rb = null;
108             try {
109                 PingResult result = makeResourcePayload(secureService!=null ?
110                         secureService.pingAdmin() : "no ejb injected!!!");
111                 rb = secureService!=null ? 
112                         Response.ok(result) :
113                         Response.serverError().entity(result);
114             } catch (EJBAccessException ex) {
115                 PingResult entity = makeResourcePayload(ex.toString());
116                 rb = Response.status(Status.FORBIDDEN).entity(entity);                
117             } catch (Exception ex) {
118                 rb=makeExceptionResponse(ex);
119             }
120             
121             return rb.build();
122         }
123 
124         @Path("pingUser")
125         @GET
126         @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
127         public Response pingUser() {
128             ResponseBuilder rb = null;
129             try {
130                 PingResult result = makeResourcePayload(secureService!=null ?
131                         secureService.pingUser() : "no ejb injected!!!");
132                 rb = secureService!=null ? 
133                         Response.ok(result) :
134                         Response.serverError().entity(result);
135             } catch (EJBAccessException ex) {
136                 PingResult entity = makeResourcePayload(ex.toString());
137                 rb = Response.status(Status.FORBIDDEN).entity(entity);                
138             } catch (Exception ex) {
139                 rb=makeExceptionResponse(ex);
140             }
141             
142             return rb.build();
143         }
144 
145         @Path("pingAll")
146         @GET
147         @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
148         public Response pingAll() {
149             ResponseBuilder rb = null;
150             try {
151                 PingResult result = makeResourcePayload(secureService!=null ?
152                         secureService.pingAll() : "no ejb injected!!!");
153                 rb = secureService!=null ? 
154                         Response.ok(result) :
155                         Response.serverError().entity(result);
156             } catch (Exception ex) {
157                 //everyone should be able to call this -- why are we failing?
158                 rb=makeExceptionResponse(ex);
159             }
160             
161             return rb.build();
162         }
163     }
164     
165     /**
166      * This method is used to report an unexpected error condition in the endpoints.
167      * @param ex
168      * @return error message
169      */
170     private ResponseBuilder makeExceptionResponse(Exception ex) {
171         String user = ctx.getUserPrincipal()==null ? null : ctx.getUserPrincipal().getName();
172         return Response.serverError()
173                 .entity(String.format("unexpected error for user[%s] calling secureService: %s",  user, ex.toString()));
174     }
175     
176     /**
177      * This helper method builds a DTO to be marshaled back to the caller.
178      * @param ejbResponse
179      * @return dto filled in
180      */
181     private PingResult makeResourcePayload(String ejbResponse) {
182         String context = uriInfo.getAbsolutePath().toString();
183         String userName = ctx.getUserPrincipal()==null ? null : ctx.getUserPrincipal().getName();
184         boolean isAdmin = ctx.isUserInRole("admin");
185         boolean isUser = ctx.isUserInRole("user");
186         
187         PingResult result = new PingResult(context, userName, isAdmin, isUser);
188         result.setServiceResult(ejbResponse);
189         return result;
190     }
191 
192 }