001    /**
002     * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.security.auth.tunnel;
016    
017    import com.liferay.portal.kernel.log.Log;
018    import com.liferay.portal.kernel.log.LogFactoryUtil;
019    import com.liferay.portal.kernel.security.auth.http.HttpAuthManagerUtil;
020    import com.liferay.portal.kernel.security.auth.http.HttpAuthorizationHeader;
021    import com.liferay.portal.kernel.security.auth.tunnel.TunnelAuthenticationManager;
022    import com.liferay.portal.kernel.servlet.HttpHeaders;
023    import com.liferay.portal.kernel.util.GetterUtil;
024    import com.liferay.portal.kernel.util.PropsKeys;
025    import com.liferay.portal.kernel.util.StringUtil;
026    import com.liferay.portal.kernel.util.Validator;
027    import com.liferay.portal.model.User;
028    import com.liferay.portal.security.auth.AuthException;
029    import com.liferay.portal.security.auth.RemoteAuthException;
030    import com.liferay.portal.service.UserLocalServiceUtil;
031    import com.liferay.portal.util.PortalInstances;
032    import com.liferay.portal.util.PropsValues;
033    import com.liferay.util.Encryptor;
034    import com.liferay.util.EncryptorException;
035    
036    import java.net.HttpURLConnection;
037    
038    import java.security.Key;
039    
040    import javax.crypto.spec.SecretKeySpec;
041    
042    import javax.servlet.http.HttpServletRequest;
043    
044    import org.apache.commons.codec.DecoderException;
045    import org.apache.commons.codec.binary.Hex;
046    
047    /**
048     * @author Tomas Polesovsky
049     */
050    public class TunnelAuthenticationManagerImpl
051            implements TunnelAuthenticationManager {
052    
053            @Override
054            public long getUserId(HttpServletRequest httpServletRequest)
055                    throws AuthException {
056    
057                    HttpAuthorizationHeader httpAuthorizationHeader =
058                            HttpAuthManagerUtil.parse(httpServletRequest);
059    
060                    if (httpAuthorizationHeader == null) {
061                            return 0;
062                    }
063    
064                    String scheme = httpAuthorizationHeader.getScheme();
065    
066                    if (!StringUtil.equalsIgnoreCase(
067                                    scheme, HttpAuthorizationHeader.SCHEME_BASIC)) {
068    
069                            AuthException authException = new RemoteAuthException(
070                                    "Invalid scheme " + scheme);
071    
072                            authException.setType(AuthException.INTERNAL_SERVER_ERROR);
073    
074                            throw authException;
075                    }
076    
077                    String expectedPassword = null;
078    
079                    String login = httpAuthorizationHeader.getAuthParameter(
080                            HttpAuthorizationHeader.AUTH_PARAMETER_NAME_USERNAME);
081    
082                    try {
083                            expectedPassword = Encryptor.encrypt(getSharedSecretKey(), login);
084                    }
085                    catch (EncryptorException ee) {
086                            AuthException authException = new RemoteAuthException(ee);
087    
088                            authException.setType(AuthException.INTERNAL_SERVER_ERROR);
089    
090                            throw authException;
091                    }
092                    catch (AuthException ae) {
093                            AuthException authException = new RemoteAuthException(ae);
094    
095                            authException.setType(ae.getType());
096    
097                            throw authException;
098                    }
099    
100                    String password = httpAuthorizationHeader.getAuthParameter(
101                            HttpAuthorizationHeader.AUTH_PARAMETER_NAME_PASSWORD);
102    
103                    if (!Validator.equals(expectedPassword, password)) {
104                            AuthException authException = new RemoteAuthException();
105    
106                            authException.setType(RemoteAuthException.WRONG_SHARED_SECRET);
107    
108                            throw authException;
109                    }
110    
111                    User user = UserLocalServiceUtil.fetchUser(GetterUtil.getLong(login));
112    
113                    if (user == null) {
114                            long companyId = PortalInstances.getCompanyId(httpServletRequest);
115    
116                            user = UserLocalServiceUtil.fetchUserByEmailAddress(
117                                    companyId, login);
118    
119                            if (user == null) {
120                                    user = UserLocalServiceUtil.fetchUserByScreenName(
121                                            companyId, login);
122                            }
123                    }
124    
125                    if (user == null) {
126                            AuthException authException = new RemoteAuthException(
127                                    "Unable to find user " + login);
128    
129                            authException.setType(AuthException.INTERNAL_SERVER_ERROR);
130    
131                            throw authException;
132                    }
133    
134                    return user.getUserId();
135            }
136    
137            @Override
138            public void setCredentials(
139                            String login, HttpURLConnection httpURLConnection)
140                    throws Exception {
141    
142                    if (Validator.isBlank(login)) {
143                            throw new IllegalArgumentException("Login is null");
144                    }
145    
146                    HttpAuthorizationHeader httpAuthorizationHeader =
147                            new HttpAuthorizationHeader(HttpAuthorizationHeader.SCHEME_BASIC);
148    
149                    String password = Encryptor.encrypt(getSharedSecretKey(), login);
150    
151                    httpAuthorizationHeader.setAuthParameter(
152                            HttpAuthorizationHeader.AUTH_PARAMETER_NAME_PASSWORD, password);
153    
154                    httpAuthorizationHeader.setAuthParameter(
155                            HttpAuthorizationHeader.AUTH_PARAMETER_NAME_USERNAME, login);
156                    httpURLConnection.setRequestProperty(
157                            HttpHeaders.AUTHORIZATION, httpAuthorizationHeader.toString());
158            }
159    
160            protected Key getSharedSecretKey() throws AuthException {
161                    String sharedSecret = PropsValues.TUNNELING_SERVLET_SHARED_SECRET;
162                    boolean sharedSecretHex =
163                            PropsValues.TUNNELING_SERVLET_SHARED_SECRET_HEX;
164    
165                    if (Validator.isNull(sharedSecret)) {
166                            String message =
167                                    "Please configure " + PropsKeys.TUNNELING_SERVLET_SHARED_SECRET;
168    
169                            if (_log.isWarnEnabled()) {
170                                    _log.warn(message);
171                            }
172    
173                            AuthException authException = new AuthException(message);
174    
175                            authException.setType(AuthException.NO_SHARED_SECRET);
176    
177                            throw authException;
178                    }
179    
180                    byte[] key = null;
181    
182                    if (sharedSecretHex) {
183                            try {
184                                    key = Hex.decodeHex(sharedSecret.toCharArray());
185                            }
186                            catch (DecoderException e) {
187                                    if (_log.isWarnEnabled()) {
188                                            _log.warn(e, e);
189                                    }
190    
191                                    AuthException authException = new AuthException();
192    
193                                    authException.setType(AuthException.INVALID_SHARED_SECRET);
194    
195                                    throw authException;
196                            }
197                    }
198                    else {
199                            key = sharedSecret.getBytes();
200                    }
201    
202                    if (key.length < 8) {
203                            String message =
204                                    PropsKeys.TUNNELING_SERVLET_SHARED_SECRET + " is too short";
205    
206                            if (_log.isWarnEnabled()) {
207                                    _log.warn(message);
208                            }
209    
210                            AuthException authException = new AuthException(message);
211    
212                            authException.setType(AuthException.INVALID_SHARED_SECRET);
213    
214                            throw authException;
215                    }
216    
217                    if (StringUtil.equalsIgnoreCase(
218                                    PropsValues.TUNNELING_SERVLET_ENCRYPTION_ALGORITHM, "AES") &&
219                            (key.length != 16) && (key.length != 32)) {
220    
221                            String message =
222                                    PropsKeys.TUNNELING_SERVLET_SHARED_SECRET +
223                                            " must have 16 or 32 bytes when used with AES";
224    
225                            if (_log.isWarnEnabled()) {
226                                    _log.warn(message);
227                            }
228    
229                            AuthException authException = new AuthException(message);
230    
231                            authException.setType(AuthException.INVALID_SHARED_SECRET);
232    
233                            throw authException;
234                    }
235    
236                    return new SecretKeySpec(
237                            key, PropsValues.TUNNELING_SERVLET_ENCRYPTION_ALGORITHM);
238            }
239    
240            private static final Log _log = LogFactoryUtil.getLog(
241                    TunnelAuthenticationManagerImpl.class);
242    
243    }