/** * Copyright (c) 2009 - 2012 Red Hat, Inc. * * This software is licensed to you under the GNU General Public License, * version 2 (GPLv2). There is NO WARRANTY for this software, express or * implied, including the implied warranties of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 * along with this software; if not, see * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * Red Hat trademarks are not licensed under GPLv2. No permission is * granted to use or replicate Red Hat trademarks that are incorporated * in this software or its documentation. */ package org.candlepin.auth; import org.candlepin.common.config.Configuration; import org.candlepin.common.exceptions.BadRequestException; import org.candlepin.common.exceptions.CandlepinException; import org.candlepin.common.exceptions.IseException; import org.candlepin.common.exceptions.NotAuthorizedException; import org.candlepin.common.resteasy.auth.AuthUtil; import org.candlepin.common.resteasy.auth.RestEasyOAuthMessage; import com.google.inject.Inject; import org.jboss.resteasy.spi.HttpRequest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.xnap.commons.i18n.I18n; import net.oauth.OAuthAccessor; import net.oauth.OAuthConsumer; import net.oauth.OAuthException; import net.oauth.OAuthMessage; import net.oauth.OAuthProblemException; import net.oauth.OAuthValidator; import net.oauth.SimpleOAuthValidator; import net.oauth.signature.OAuthSignatureMethod; import java.io.IOException; import java.net.URISyntaxException; import java.util.HashMap; import java.util.Map; import javax.inject.Provider; import javax.ws.rs.core.Response; /** * Uses two legged OAuth. If it succeeds, then it pulls the username off of a * headers and creates a principal based only on the username */ public class OAuth implements AuthProvider { protected static final String HEADER = "cp-user"; protected static final OAuthValidator VALIDATOR = new SimpleOAuthValidator(); protected static final String SIGNATURE_TYPE = "HMAC-SHA1"; private static Logger log = LoggerFactory.getLogger(OAuth.class);; private Configuration config; private TrustedUserAuth userAuth; private TrustedConsumerAuth consumerAuth; private TrustedExternalSystemAuth systemAuth; private Map<String, OAuthAccessor> accessors = new HashMap<String, OAuthAccessor>(); protected Provider<I18n> i18nProvider; @Inject OAuth(TrustedConsumerAuth consumerAuth, TrustedUserAuth userAuth, TrustedExternalSystemAuth systemAuth, Provider<I18n> i18nProvider, Configuration config) { this.config = config; this.userAuth = userAuth; this.consumerAuth = consumerAuth; this.systemAuth = systemAuth; this.i18nProvider = i18nProvider; this.setupAccessors(); this.setupSigners(); } /** * Attempt to pull a principal off of an oauth signed message. * * @return the principal if it can be created, null otherwise */ public Principal getPrincipal(HttpRequest httpRequest) { Principal principal = null; I18n i18n = i18nProvider.get(); try { if (AuthUtil.getHeader(httpRequest, "Authorization").contains("oauth")) { OAuthMessage requestMessage = new RestEasyOAuthMessage(httpRequest); OAuthAccessor accessor = this.getAccessor(requestMessage); // TODO: This is known to be memory intensive. VALIDATOR.validateMessage(requestMessage, accessor); // If we got here, it is a valid oauth message. // Figure out which kind of principal we should create, based on header log.debug("Using OAuth"); if (!AuthUtil.getHeader(httpRequest, TrustedUserAuth.USER_HEADER).equals("")) { principal = userAuth.getPrincipal(httpRequest); } else if (!AuthUtil.getHeader(httpRequest, TrustedConsumerAuth.CONSUMER_HEADER).equals("")) { principal = consumerAuth.getPrincipal(httpRequest); } else { // The external system is acting on behalf of itself principal = systemAuth.getPrincipal(httpRequest); } } } catch (OAuthProblemException e) { log.debug("OAuth Problem", e); // XXX: for some reason invalid signature (like bad password) has a // status code of 200. make it 401 unauthorized instead. if (e.getProblem().equals("signature_invalid")) { throw new NotAuthorizedException( i18n.tr("Invalid OAuth unit or secret")); } Response.Status returnCode = Response.Status.fromStatusCode(e .getHttpStatusCode()); String message = i18n.tr("OAuth problem encountered. Internal message is: {0}", e.getMessage()); throw new CandlepinException(returnCode, message); } catch (OAuthException e) { log.debug("OAuth Error", e); String message = i18n.tr("OAuth error encountered. Internal message is: {0}", e.getMessage()); throw new BadRequestException(message); } catch (URISyntaxException e) { throw new IseException(e.getMessage(), e); } catch (IOException e) { throw new IseException(e.getMessage(), e); } return principal; } /** * Get an oauth accessor for a given message. An exception is thrown if no * accessor is found. * * @param msg * @return the OAuth accessor for the given message. */ protected OAuthAccessor getAccessor(OAuthMessage msg) { I18n i18n = i18nProvider.get(); try { OAuthAccessor accessor = accessors.get(msg.getConsumerKey()); if (accessor == null) { throw new NotAuthorizedException(i18n.tr("Invalid OAuth unit or secret")); } return accessor; } catch (IOException e) { throw new IseException(i18n.tr("Error getting OAuth unit key", e)); } } /** * Look for settings which are in the form of * candlepin.auth.oauth.consumer.CONSUMERNAME.secret = CONSUMERSECRET and * create consumers for them. */ protected void setupAccessors() { String prefix = "candlepin.auth.oauth.consumer."; Configuration oauthConfig = config.strippedSubset(prefix); for (String key : oauthConfig.getKeys()) { String[] parts = key.split("\\."); if ((parts.length == 2) && (parts[1].equals("secret"))) { String consumerName = parts[0]; String secret = oauthConfig.getString(key); log.debug(String.format("Creating consumer '%s'", consumerName)); OAuthConsumer consumer = new OAuthConsumer("", consumerName, secret, null); OAuthAccessor accessor = new OAuthAccessor(consumer); accessors.put(consumerName, accessor); } } } /** * Override the built in signer for HMAC-SHA1 so that we can see better * output. */ protected void setupSigners() { log.debug("Add custom signers"); OAuthSignatureMethod.registerMethodClass(SIGNATURE_TYPE + OAuthSignatureMethod._ACCESSOR, net.oauth.signature.CustomSigner.class); OAuthSignatureMethod.registerMethodClass(SIGNATURE_TYPE, net.oauth.signature.CustomSigner.class); } }