001/* 002 * (C) Copyright 2014 Nuxeo SA (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 * Arnaud Kervern 018 */ 019package org.nuxeo.ecm.platform.oauth2.request; 020 021import static org.apache.commons.lang.StringUtils.isBlank; 022import static org.nuxeo.ecm.platform.ui.web.auth.oauth2.NuxeoOAuth2Filter.ERRORS.invalid_request; 023import static org.nuxeo.ecm.platform.ui.web.auth.oauth2.NuxeoOAuth2Filter.ERRORS.server_error; 024import static org.nuxeo.ecm.platform.ui.web.auth.oauth2.NuxeoOAuth2Filter.ERRORS.unauthorized_client; 025import static org.nuxeo.ecm.platform.ui.web.auth.oauth2.NuxeoOAuth2Filter.ERRORS.unsupported_response_type; 026 027import java.io.UnsupportedEncodingException; 028import java.util.Date; 029import java.util.Iterator; 030import java.util.Map; 031import java.util.concurrent.ConcurrentHashMap; 032 033import javax.servlet.http.HttpServletRequest; 034 035import org.apache.commons.lang.RandomStringUtils; 036import org.apache.commons.logging.Log; 037import org.apache.commons.logging.LogFactory; 038import org.nuxeo.ecm.directory.DirectoryException; 039import org.nuxeo.ecm.platform.oauth2.clients.ClientRegistry; 040import org.nuxeo.runtime.api.Framework; 041 042/** 043 * @author <a href="mailto:ak@nuxeo.com">Arnaud Kervern</a> 044 * @since 5.9.2 045 */ 046public class AuthorizationRequest extends Oauth2Request { 047 private static final Log log = LogFactory.getLog(AuthorizationRequest.class); 048 049 protected static Map<String, AuthorizationRequest> requests = new ConcurrentHashMap<>(); 050 051 protected String responseType; 052 053 protected String scope; 054 055 protected String state; 056 057 protected String sessionId; 058 059 protected Date creationDate; 060 061 protected String authorizationCode; 062 063 protected String authorizationKey; 064 065 protected String username; 066 067 public static final String RESPONSE_TYPE = "response_type"; 068 069 public static final String SCOPE = "scope"; 070 071 public static final String STATE = "state"; 072 073 public AuthorizationRequest() { 074 } 075 076 public AuthorizationRequest(HttpServletRequest request) { 077 super(request); 078 responseType = request.getParameter(RESPONSE_TYPE); 079 080 scope = request.getParameter(SCOPE); 081 state = request.getParameter(STATE); 082 sessionId = request.getSession(true).getId(); 083 084 creationDate = new Date(); 085 authorizationKey = RandomStringUtils.random(6, true, false); 086 } 087 088 public String checkError() { 089 // Check mandatory fields 090 if (isBlank(responseType) || isBlank(clientId) || isBlank(redirectUri)) { 091 return invalid_request.toString(); 092 } 093 094 // Check if client exists 095 try { 096 ClientRegistry registry = Framework.getLocalService(ClientRegistry.class); 097 if (!registry.hasClient(clientId)) { 098 return unauthorized_client.toString(); 099 } 100 } catch (DirectoryException e) { 101 log.warn(e, e); 102 return server_error.toString(); 103 } 104 105 // Check request type 106 if (!"code".equals(responseType)) { 107 return unsupported_response_type.toString(); 108 } 109 return null; 110 } 111 112 public boolean isExpired() { 113 // RFC 4.1.2, Authorization code lifetime is 10 114 return new Date().getTime() - creationDate.getTime() > 10 * 60 * 1000; 115 } 116 117 public boolean isValidState(HttpServletRequest request) { 118 return isBlank(getState()) || request.getParameter(STATE).equals(getState()); 119 } 120 121 public String getUsername() { 122 return username; 123 } 124 125 public String getResponseType() { 126 return responseType; 127 } 128 129 public String getScope() { 130 return scope; 131 } 132 133 public String getState() { 134 return state; 135 } 136 137 public String getAuthorizationCode() { 138 if (isBlank(authorizationCode)) { 139 authorizationCode = RandomStringUtils.random(10, true, true); 140 } 141 return authorizationCode; 142 } 143 144 public String getAuthorizationKey() { 145 return authorizationKey; 146 } 147 148 private static void deleteExpiredRequests() { 149 Iterator<AuthorizationRequest> iterator = requests.values().iterator(); 150 AuthorizationRequest req; 151 while (iterator.hasNext() && (req = iterator.next()) != null) { 152 if (req.isExpired()) { 153 requests.remove(req.sessionId); 154 } 155 } 156 } 157 158 public static AuthorizationRequest from(HttpServletRequest request) throws UnsupportedEncodingException { 159 deleteExpiredRequests(); 160 161 String sessionId = request.getSession(true).getId(); 162 if (requests.containsKey(sessionId)) { 163 AuthorizationRequest authRequest = requests.get(sessionId); 164 if (!authRequest.isExpired() && authRequest.isValidState(request)) { 165 return authRequest; 166 } 167 } 168 169 AuthorizationRequest authRequest = new AuthorizationRequest(request); 170 requests.put(sessionId, authRequest); 171 return authRequest; 172 } 173 174 public static AuthorizationRequest fromCode(String authorizationCode) { 175 for (AuthorizationRequest auth : requests.values()) { 176 if (auth.authorizationCode != null && auth.authorizationCode.equals(authorizationCode)) { 177 if (auth.sessionId != null) { 178 requests.remove(auth.sessionId); 179 } 180 return auth.isExpired() ? null : auth; 181 } 182 } 183 return null; 184 } 185 186 public void setUsername(String username) { 187 this.username = username; 188 } 189}