001/* 002 * (C) Copyright 2018 Nuxeo (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * Florent Guillaume 018 */ 019package org.nuxeo.ecm.platform.ui.web.auth.oauth; 020 021import static java.lang.Boolean.FALSE; 022import static javax.ws.rs.core.HttpHeaders.AUTHORIZATION; 023import static net.oauth.OAuth.OAUTH_SIGNATURE; 024 025import java.io.IOException; 026import java.net.URISyntaxException; 027import java.util.List; 028import java.util.Map; 029 030import javax.servlet.http.HttpServletRequest; 031import javax.servlet.http.HttpServletResponse; 032 033import org.apache.logging.log4j.LogManager; 034import org.apache.logging.log4j.Logger; 035import org.nuxeo.ecm.platform.api.login.UserIdentificationInfo; 036import org.nuxeo.ecm.platform.oauth.consumers.NuxeoOAuthConsumer; 037import org.nuxeo.ecm.platform.oauth.consumers.OAuthConsumerRegistry; 038import org.nuxeo.ecm.platform.oauth.keys.OAuthServerKeyManager; 039import org.nuxeo.ecm.platform.oauth.tokens.OAuthToken; 040import org.nuxeo.ecm.platform.oauth.tokens.OAuthTokenStore; 041import org.nuxeo.ecm.platform.ui.web.auth.interfaces.NuxeoAuthenticationPlugin; 042import org.nuxeo.runtime.api.Framework; 043import org.nuxeo.runtime.transaction.TransactionHelper; 044 045import net.oauth.OAuthAccessor; 046import net.oauth.OAuthException; 047import net.oauth.OAuthMessage; 048import net.oauth.OAuthValidator; 049import net.oauth.SimpleOAuthValidator; 050import net.oauth.server.OAuthServlet; 051 052/** 053 * OAuth 1 Authentication Plugin. 054 * 055 * @since 10.3 056 */ 057public class NuxeoOAuth1Authenticator implements NuxeoAuthenticationPlugin { 058 059 private static final Logger log = LogManager.getLogger(NuxeoOAuth1Authenticator.class); 060 061 @Override 062 public void initPlugin(Map<String, String> parameters) { 063 // nothing to init 064 } 065 066 @Override 067 public List<String> getUnAuthenticatedURLPrefix() { 068 return null; // NOSONAR 069 } 070 071 @Override 072 public Boolean needLoginPrompt(HttpServletRequest httpRequest) { 073 return FALSE; 074 } 075 076 @Override 077 public Boolean handleLoginPrompt(HttpServletRequest httpRequest, HttpServletResponse httpResponse, String baseURL) { 078 return FALSE; 079 } 080 081 @Override 082 public UserIdentificationInfo handleRetrieveIdentity(HttpServletRequest request, HttpServletResponse response) { 083 if (!isOAuth1SignedRequest(request)) { 084 log.trace("Not an OAuth 1 signed request"); 085 return null; 086 } 087 String username = getIdentity(request); 088 if (username == null) { 089 log.trace("OAuth 1 auth failed"); 090 return null; 091 } 092 log.trace("OAuth 1 auth for user: {}", username); 093 return new UserIdentificationInfo(username, username); 094 } 095 096 protected boolean isOAuth1SignedRequest(HttpServletRequest request) { 097 String auth = request.getHeader(AUTHORIZATION); 098 if (auth != null && auth.contains("OAuth")) { 099 return true; 100 } 101 if (request.getParameter(OAUTH_SIGNATURE) != null) { 102 return true; 103 } 104 return false; 105 } 106 107 protected String getIdentity(HttpServletRequest request) { 108 return TransactionHelper.runInTransaction(() -> { 109 try { 110 return getOAuth1Identity(request); 111 } catch (IOException e) { 112 log.debug(e, e); 113 return null; 114 } 115 }); 116 } 117 118 /** 119 * Verifies OAuth information and returns identity. 120 */ 121 protected String getOAuth1Identity(HttpServletRequest request) throws IOException { 122 String url = getRequestURL(request); 123 OAuthMessage message = OAuthServlet.getMessage(request, url); 124 125 String consumerKey = message.getConsumerKey(); 126 String signatureMethod = message.getSignatureMethod(); 127 OAuthConsumerRegistry consumerRegistry = Framework.getService(OAuthConsumerRegistry.class); 128 NuxeoOAuthConsumer consumer = consumerRegistry.getConsumer(consumerKey, signatureMethod); 129 130 if (consumer == null && consumerKey != null) { 131 OAuthServerKeyManager okm = Framework.getService(OAuthServerKeyManager.class); 132 if (consumerKey.equals(okm.getInternalKey())) { 133 consumer = okm.getInternalConsumer(); 134 } 135 } 136 if (consumer == null) { 137 return null; 138 } 139 140 OAuthAccessor accessor = new OAuthAccessor(consumer); 141 OAuthValidator validator = new SimpleOAuthValidator(); 142 OAuthTokenStore tokenStore = Framework.getService(OAuthTokenStore.class); 143 OAuthToken aToken = tokenStore.getAccessToken(message.getToken()); 144 String username; 145 if (aToken != null) { 146 // three-legged auth 147 accessor.accessToken = aToken.getToken(); 148 accessor.tokenSecret = aToken.getTokenSecret(); 149 username = aToken.getNuxeoLogin(); 150 } else { 151 // two-legged auth 152 if (!consumer.allowSignedFetch()) { 153 return null; 154 } 155 username = consumer.getSignedFetchUser(); 156 if (NuxeoOAuthConsumer.SIGNEDFETCH_OPENSOCIAL_VIEWER.equals(username)) { 157 username = message.getParameter("opensocial_viewer_id"); 158 } else if (NuxeoOAuthConsumer.SIGNEDFETCH_OPENSOCIAL_OWNER.equals(username)) { 159 username = message.getParameter("opensocial_owner_id"); 160 } 161 } 162 try { 163 validator.validateMessage(message, accessor); 164 return username; 165 } catch (OAuthException | URISyntaxException e) { 166 log.debug("Invalid OAuth signature", e); 167 return null; 168 } 169 } 170 171 /** 172 * Gets the URL used for this request by checking the X-Forwarded-Proto header used in the request. 173 */ 174 // public for tests 175 public static String getRequestURL(HttpServletRequest request) { 176 String url = request.getRequestURL().toString(); 177 String forwardedProto = request.getHeader("X-Forwarded-Proto"); 178 if (forwardedProto != null && !url.startsWith(forwardedProto)) { 179 url = forwardedProto + url.substring(url.indexOf("://")); 180 } 181 return url; 182 } 183 184}