/** * Copyright 2005-2014 Restlet * * The contents of this file are subject to the terms of one of the following * open source licenses: Apache 2.0 or or EPL 1.0 (the "Licenses"). You can * select the license that you prefer but you may not use this file except in * compliance with one of these Licenses. * * You can obtain a copy of the Apache 2.0 license at * http://www.opensource.org/licenses/apache-2.0 * * You can obtain a copy of the EPL 1.0 license at * http://www.opensource.org/licenses/eclipse-1.0 * * See the Licenses for the specific language governing permissions and * limitations under the Licenses. * * Alternatively, you can obtain a royalty free commercial license with less * limitations, transferable or non-transferable, directly at * http://restlet.com/products/restlet-framework * * Restlet is a registered trademark of Restlet S.A.S. */ package org.restlet.ext.oauth; import java.util.HashMap; import java.util.concurrent.ConcurrentMap; import org.restlet.data.CookieSetting; import org.restlet.data.MediaType; import org.restlet.data.Reference; import org.restlet.ext.freemarker.ContextTemplateLoader; import org.restlet.ext.freemarker.TemplateRepresentation; import org.restlet.ext.oauth.internal.AuthSession; import org.restlet.ext.oauth.internal.AuthSessionTimeoutException; import org.restlet.ext.oauth.internal.RedirectionURI; import org.restlet.representation.Representation; import freemarker.template.Configuration; /** * Base Restlet resource class for Authorization service resource. Handle errors * according to OAuth2.0 specification, and manage AuthSession. Authorization * Endndpoint, Authorization pages, and Login pages should extends this class. * * @author Shotaro Uchida <fantom@xmaker.mx> */ public class AuthorizationBaseServerResource extends OAuthServerResource { private static final String ClientCookieID = "_cid"; @Override protected void doCatch(Throwable t) { final OAuthException oex = OAuthException.toOAuthException(t); AuthSession session = null; try { session = getAuthSession(); } catch (OAuthException ignore) { // FIXME } if (session == null || session.getAuthFlow() == null) { // Session was revoked or not established. Representation resp = getErrorPage( HttpOAuthHelper.getErrorPageTemplate(getContext()), oex); getResponse().setEntity(resp); } else { /* * If the resource owner denies the access request or if the request * fails for reasons other than a missing or invalid redirection * URI, the authorization server informs the client by adding the * following parameters to the query component of the redirection * URI using the "application/x-www-form-urlencoded" format, per * Appendix B: (4.2.2.1. Error Response) */ // Use fragment component for Implicit Grant (4.2.2.1. Error // Response) boolean fragment = session.getAuthFlow().equals(ResponseType.token); sendError(session.getRedirectionURI().getURI(), oex, session.getState(), fragment); } } /** * Returns the current authorization session. * * @return The current {@link AuthSession} instance. */ protected AuthSession getAuthSession() throws OAuthException { // Get some basic information String sessionId = getCookies().getFirstValue(ClientCookieID); getLogger().fine("sessionId = " + sessionId); AuthSession session = (sessionId == null) ? null : (AuthSession) getContext().getAttributes().get(sessionId); if (session == null) { return null; } try { session.updateActivity(); } catch (AuthSessionTimeoutException ex) { // Remove timeout session getContext().getAttributes().remove(sessionId); throw new OAuthException(OAuthError.server_error, "Session timeout", null); } return session; } /** * Helper method to format error responses according to OAuth2 spec. (Non * Redirect) * * @param errPage * errorPage template name * @param ex * Any OAuthException with error */ protected Representation getErrorPage(String errPage, OAuthException ex) { Configuration config = new Configuration(); config.setTemplateLoader(new ContextTemplateLoader(getContext(), "clap:///")); getLogger().fine("loading: " + errPage); TemplateRepresentation response = new TemplateRepresentation(errPage, config, MediaType.TEXT_HTML); // Build the model HashMap<String, Object> data = new HashMap<String, Object>(); data.put(ERROR, ex.getError().name()); data.put(ERROR_DESC, ex.getErrorDescription()); data.put(ERROR_URI, ex.getErrorURI()); response.setDataModel(data); return response; } /** * Helper method to format error responses according to OAuth2 spec. * (Redirect) * * @param redirectURI * redirection URI to send error * @param ex * Any OAuthException with error * @param state * state parameter as presented in the initial authorize request * @param fragment * true if use URL Fragment. */ protected void sendError(String redirectURI, OAuthException ex, String state, boolean fragment) { Reference cb = new Reference(redirectURI); cb.addQueryParameter(ERROR, ex.getError().name()); if (state != null && state.length() > 0) { cb.addQueryParameter(STATE, state); } String description = ex.getErrorDescription(); if (description != null && description.length() > 0) { cb.addQueryParameter(ERROR_DESC, description); } String errorUri = ex.getErrorURI(); if (errorUri != null && errorUri.length() > 0) { cb.addQueryParameter(ERROR_URI, errorUri); } if (fragment) { cb.setFragment(cb.getQuery()); cb.setQuery(""); } redirectTemporary(cb); } /** * Sets up a new authorization session. * * @param redirectUri * The redirection URI. */ protected AuthSession setupAuthSession(RedirectionURI redirectUri) { getLogger().fine("Base ref = " + getReference().getParentRef()); AuthSession session = AuthSession.newAuthSession(); session.setRedirectionURI(redirectUri); CookieSetting cs = new CookieSetting(ClientCookieID, session.getId()); // TODO create a secure mode setting, update all cookies // cs.setAccessRestricted(true); // cs.setSecure(true); getCookieSettings().add(cs); getLogger().fine("Setting cookie in SetupSession - " + session.getId()); getContext().getAttributes().put(session.getId(), session); return session; } /** * Unget current authorization session. */ protected void ungetAuthSession() { String sessionId = getCookies().getFirstValue(ClientCookieID); // cleanup cookie. if (sessionId != null && sessionId.length() > 0) { ConcurrentMap<String, Object> attribs = getContext() .getAttributes(); attribs.remove(sessionId); } } }