/** * Copyright (C) 2011 Talend Inc. - www.talend.com */ package oauth.thirdparty; import java.net.URI; import java.util.HashMap; import java.util.Map; import java.util.logging.Logger; import javax.ws.rs.FormParam; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.Context; import javax.ws.rs.core.Form; import javax.ws.rs.core.Response; import javax.ws.rs.core.SecurityContext; import javax.ws.rs.core.UriBuilder; import javax.ws.rs.core.UriInfo; import oauth.common.Calendar; import oauth.common.CalendarEntry; import oauth.common.ReservationConfirmation; import oauth.common.ReservationFailure; import org.apache.cxf.jaxrs.client.WebClient; import org.apache.cxf.rs.security.oauth.client.OAuthClientUtils.Token; @Path("reserve") public class RestaurantReservationService { private static final Logger LOG = Logger.getLogger(RestaurantReservationService.class.getName()); private static final String NO_VERIFIER = "noverifier"; private static final String NO_REQUEST = "norequest"; private static final String NO_REQUEST_FOR_TOKEN = "norequesttoken"; private static final String NO_RESERVATION = "noreserve"; private static final String NO_OAUTH_REQUEST_TOKEN = "nooauthrequest"; private static final String NO_OAUTH_ACCESS_TOKEN = "nooauthaccess"; private static final String CALENDAR_ACCESS_PROBLEM = "calendar_forbidden"; private static final String CALENDAR_BUSY = "calendar_busy"; private static final Map<String, String> ERROR_DESCRIPTIONS; static { ERROR_DESCRIPTIONS = new HashMap<String, String>(); ERROR_DESCRIPTIONS.put(NO_VERIFIER, "The reservation can not be completed due to you denying the access to your Calendar"); ERROR_DESCRIPTIONS.put(NO_REQUEST, "No pending requests have been found, please try again"); ERROR_DESCRIPTIONS.put(NO_REQUEST_FOR_TOKEN, "The information about the request can not be located, please try again"); ERROR_DESCRIPTIONS.put(NO_RESERVATION, "All restaurants are currently booked, please try again"); ERROR_DESCRIPTIONS.put(NO_OAUTH_REQUEST_TOKEN, "Problem initiating the OAuth flow in order to access your Calendar" + ", please report to Social.com admin"); ERROR_DESCRIPTIONS.put(NO_OAUTH_ACCESS_TOKEN, "Problem replacing your authorization key for OAuth access token" + ", please report to Social.com admin"); ERROR_DESCRIPTIONS.put(CALENDAR_ACCESS_PROBLEM, "Social.com refused to return a calendar, please try again"); ERROR_DESCRIPTIONS.put(CALENDAR_BUSY, "You are busy at the requested hour, please check your calendar"); } @Context private SecurityContext sc; @Context private UriInfo ui; private Map<String, Map<String, ReservationRequest>> requests = new HashMap<String, Map<String, ReservationRequest>>(); private WebClient socialService; private WebClient restaurantService; private OAuthClientManager manager; public void setOAuthClientManager(OAuthClientManager manager) { this.manager = manager; } public void setSocialService(WebClient socialService) { this.socialService = socialService; } public void setRestaurantService(WebClient restaurantService) { this.restaurantService = restaurantService; } @GET @Path("failure") @Produces({"text/html", "application/xml;q=0.9" }) public ReservationFailure handleReservationFailure(@QueryParam("code") String errorCode) { LOG.info("Handling the reservation failure"); String message = ERROR_DESCRIPTIONS.get(errorCode); return new ReservationFailure(message); } @GET @Path("complete") @Produces({"text/html", "application/xml;q=0.9" }) public Response completeReservation(@QueryParam("oauth_token") String token, @QueryParam("oauth_verifier") String verifier) { String userName = sc.getUserPrincipal().getName(); Map<String, ReservationRequest> userRequests = requests.get(userName); if (userRequests == null) { return redirectToFailureHandler(NO_REQUEST); } ReservationRequest request = userRequests.remove(token); if (request == null) { return redirectToFailureHandler(NO_REQUEST_FOR_TOKEN); } if (verifier == null) { return redirectToFailureHandler(NO_VERIFIER); } LOG.info("Requesting OAuth server to replace an authorized request token with an access token"); Token accessToken = manager.getAccessToken(request.getRequestToken(), verifier); if (accessToken == null) { return redirectToFailureHandler(NO_OAUTH_ACCESS_TOKEN); } LOG.info("Completing the reservation request for a user: " + request.getReserveName()); Calendar c = null; try { String authHeader = manager.createAuthorizationHeader(accessToken, "GET", socialService.getCurrentURI().toString()); socialService.replaceHeader("Authorization", authHeader); c = socialService.get(Calendar.class); } catch (RuntimeException ex) { return redirectToFailureHandler(CALENDAR_ACCESS_PROBLEM); } CalendarEntry entry = c.getEntry(request.getHour()); if (entry.getEventDescription() == null || entry.getEventDescription().trim().isEmpty()) { String address = restaurantService.post(new Form().param("name", request.getReserveName()) .param("phone", request.getContactPhone()) .param("hour", Integer.toString(request.getHour())), String.class); if (address == null) { return redirectToFailureHandler(NO_RESERVATION); } // update the user's calendar String authHeader = manager.createAuthorizationHeader(accessToken, "POST", socialService.getCurrentURI().toString()); socialService.replaceHeader("Authorization", authHeader); Response response = socialService.form(new Form().param("hour", Integer.toString(request.getHour())) .param("description", "Table reserved at " + address)); boolean calendarUpdated = response.getStatus() == 200 || response.getStatus() == 204; return Response.ok(new ReservationConfirmation(address, request.getHour(), calendarUpdated)) .build(); } else { return redirectToFailureHandler(CALENDAR_BUSY); } } private Response redirectToFailureHandler(String code) { URI handlerUri = getBaseUriBuilder().path("failure").queryParam("code", code).build(); return Response.seeOther(handlerUri).build(); } @POST @Path("table") public Response reserveTable(@FormParam("name") String name, @FormParam("phone") String phone, @FormParam("hour") int hour) { LOG.info("Reservation request from a user " + name + " has been received"); LOG.info("Requesting a temporarily token from the OAuth server"); URI callback = getBaseUriBuilder().path("complete").build(); ReservationRequest request = new ReservationRequest(); request.setReserveName(name); request.setContactPhone(phone); request.setHour(hour); Token requestToken = manager.getRequestToken(callback, request); if (requestToken == null) { return redirectToFailureHandler(NO_OAUTH_REQUEST_TOKEN); } request.setRequestToken(requestToken); String userName = sc.getUserPrincipal().getName(); synchronized (requests) { Map<String, ReservationRequest> userRequests = requests.get(userName); if (userRequests == null) { userRequests = new HashMap<String, ReservationRequest>(); requests.put(userName, userRequests); } userRequests.put(requestToken.getToken(), request); } LOG.info("Persisting the reservation details and redirecting" + " the current user to OAuth Authorization endpoint"); // Create a request token request and redirect return Response.seeOther(manager.getAuthorizationServiceURI(requestToken.getToken())).build(); } private UriBuilder getBaseUriBuilder() { return ui.getBaseUriBuilder().path("reserve"); } }