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.util;
016    
017    import com.liferay.portal.json.JSONObjectImpl;
018    import com.liferay.portal.kernel.cluster.ClusterExecutorUtil;
019    import com.liferay.portal.kernel.cluster.ClusterNode;
020    import com.liferay.portal.kernel.cluster.ClusterNodeResponse;
021    import com.liferay.portal.kernel.cluster.ClusterNodeResponses;
022    import com.liferay.portal.kernel.cluster.ClusterRequest;
023    import com.liferay.portal.kernel.cluster.FutureClusterResponses;
024    import com.liferay.portal.kernel.json.JSONObject;
025    import com.liferay.portal.kernel.license.util.LicenseManagerUtil;
026    import com.liferay.portal.kernel.log.Log;
027    import com.liferay.portal.kernel.log.LogFactoryUtil;
028    import com.liferay.portal.kernel.util.ArrayUtil;
029    import com.liferay.portal.kernel.util.Base64;
030    import com.liferay.portal.kernel.util.CharPool;
031    import com.liferay.portal.kernel.util.ClassLoaderUtil;
032    import com.liferay.portal.kernel.util.Constants;
033    import com.liferay.portal.kernel.util.ContentTypes;
034    import com.liferay.portal.kernel.util.FileUtil;
035    import com.liferay.portal.kernel.util.GetterUtil;
036    import com.liferay.portal.kernel.util.Http;
037    import com.liferay.portal.kernel.util.MethodHandler;
038    import com.liferay.portal.kernel.util.MethodKey;
039    import com.liferay.portal.kernel.util.ParamUtil;
040    import com.liferay.portal.kernel.util.PortalUtil;
041    import com.liferay.portal.kernel.util.ReleaseInfo;
042    import com.liferay.portal.kernel.util.StringPool;
043    import com.liferay.portal.kernel.util.StringUtil;
044    import com.liferay.portal.kernel.util.Validator;
045    import com.liferay.util.Encryptor;
046    
047    import java.io.File;
048    import java.io.InputStream;
049    
050    import java.net.Inet4Address;
051    import java.net.InetAddress;
052    import java.net.NetworkInterface;
053    import java.net.URI;
054    import java.net.URL;
055    
056    import java.security.Key;
057    import java.security.KeyFactory;
058    import java.security.PublicKey;
059    import java.security.SecureRandom;
060    import java.security.spec.X509EncodedKeySpec;
061    
062    import java.util.Arrays;
063    import java.util.Collections;
064    import java.util.HashMap;
065    import java.util.HashSet;
066    import java.util.Iterator;
067    import java.util.List;
068    import java.util.Map;
069    import java.util.Set;
070    import java.util.TreeMap;
071    import java.util.concurrent.TimeUnit;
072    
073    import javax.crypto.KeyGenerator;
074    
075    import javax.servlet.http.HttpServletRequest;
076    
077    import org.apache.commons.io.IOUtils;
078    import org.apache.http.HttpEntity;
079    import org.apache.http.HttpHost;
080    import org.apache.http.HttpResponse;
081    import org.apache.http.auth.AuthScope;
082    import org.apache.http.auth.UsernamePasswordCredentials;
083    import org.apache.http.client.CredentialsProvider;
084    import org.apache.http.client.HttpClient;
085    import org.apache.http.client.methods.HttpPost;
086    import org.apache.http.conn.HttpClientConnectionManager;
087    import org.apache.http.entity.ByteArrayEntity;
088    import org.apache.http.impl.client.BasicCredentialsProvider;
089    import org.apache.http.impl.client.HttpClientBuilder;
090    import org.apache.http.impl.conn.BasicHttpClientConnectionManager;
091    
092    /**
093     * @author Amos Fong
094     */
095    public class LicenseUtil {
096    
097            public static final String LICENSE_REPOSITORY_DIR =
098                    PropsValues.LIFERAY_HOME.concat("/data/license");
099    
100            public static final String LICENSE_SERVER_URL = GetterUtil.get(
101                    PropsUtil.get("license.server.url"), "https://www.liferay.com");
102    
103            public static Map<String, String> getClusterServerInfo(String clusterNodeId)
104                    throws Exception {
105    
106                    ClusterNode localClusterNode =
107                            ClusterExecutorUtil.getLocalClusterNode();
108    
109                    String localClusterNodeId = localClusterNode.getClusterNodeId();
110    
111                    if (clusterNodeId.equals(localClusterNodeId)) {
112                            return getServerInfo();
113                    }
114    
115                    List<ClusterNode> clusterNodes = ClusterExecutorUtil.getClusterNodes();
116    
117                    ClusterNode clusterNode = null;
118    
119                    for (ClusterNode curClusterNode : clusterNodes) {
120                            String curClusterNodeId = curClusterNode.getClusterNodeId();
121    
122                            if (curClusterNodeId.equals(clusterNodeId)) {
123                                    clusterNode = curClusterNode;
124    
125                                    break;
126                            }
127                    }
128    
129                    if (clusterNode == null) {
130                            return null;
131                    }
132    
133                    try {
134                            ClusterRequest clusterRequest = ClusterRequest.createUnicastRequest(
135                                    _getServerInfoMethodHandler, clusterNodeId);
136    
137                            FutureClusterResponses futureClusterResponses =
138                                    ClusterExecutorUtil.execute(clusterRequest);
139    
140                            ClusterNodeResponses clusterNodeResponses =
141                                    futureClusterResponses.get(20000, TimeUnit.MILLISECONDS);
142    
143                            ClusterNodeResponse clusterNodeResponse =
144                                    clusterNodeResponses.getClusterResponse(
145                                            clusterNode.getClusterNodeId());
146    
147                            return (Map<String, String>)clusterNodeResponse.getResult();
148                    }
149                    catch (Exception e) {
150                            _log.error(e, e);
151    
152                            throw e;
153                    }
154            }
155    
156            public static Set<String> getIpAddresses() {
157                    return _ipAddresses;
158            }
159    
160            public static Set<String> getMacAddresses() {
161                    return _macAddresses;
162            }
163    
164            public static byte[] getServerIdBytes() throws Exception {
165                    if (_serverIdBytes != null) {
166                            return _serverIdBytes;
167                    }
168    
169                    File serverIdFile = new File(
170                            LICENSE_REPOSITORY_DIR + "/server/serverId");
171    
172                    if (!serverIdFile.exists()) {
173                            return new byte[0];
174                    }
175    
176                    _serverIdBytes = FileUtil.getBytes(serverIdFile);
177    
178                    return _serverIdBytes;
179            }
180    
181            public static Map<String, String> getServerInfo() {
182                    Map<String, String> serverInfo = new HashMap<>();
183    
184                    serverInfo.put("hostName", PortalUtil.getComputerName());
185                    serverInfo.put("ipAddresses", StringUtil.merge(getIpAddresses()));
186                    serverInfo.put("macAddresses", StringUtil.merge(getMacAddresses()));
187    
188                    return serverInfo;
189            }
190    
191            public static void registerOrder(HttpServletRequest request) {
192                    String orderUuid = ParamUtil.getString(request, "orderUuid");
193                    String productEntryName = ParamUtil.getString(
194                            request, "productEntryName");
195                    int maxServers = ParamUtil.getInteger(request, "maxServers");
196    
197                    List<ClusterNode> clusterNodes = ClusterExecutorUtil.getClusterNodes();
198    
199                    if ((clusterNodes.size() <= 1) || Validator.isNull(productEntryName) ||
200                            Validator.isNull(orderUuid)) {
201    
202                            Map<String, Object> attributes = registerOrder(
203                                    orderUuid, productEntryName, maxServers);
204    
205                            for (Map.Entry<String, Object> entry : attributes.entrySet()) {
206                                    request.setAttribute(entry.getKey(), entry.getValue());
207                            }
208                    }
209                    else {
210                            for (ClusterNode clusterNode : clusterNodes) {
211                                    boolean register = ParamUtil.getBoolean(
212                                            request, clusterNode.getClusterNodeId() + "_register");
213    
214                                    if (!register) {
215                                            continue;
216                                    }
217    
218                                    try {
219                                            _registerClusterOrder(
220                                                    request, clusterNode, orderUuid, productEntryName,
221                                                    maxServers);
222                                    }
223                                    catch (Exception e) {
224                                            _log.error(e, e);
225    
226                                            InetAddress inetAddress = clusterNode.getBindInetAddress();
227    
228                                            String message =
229                                                    "Error contacting " + inetAddress.getHostName();
230    
231                                            if (clusterNode.getPortalPort() != -1) {
232                                                    message +=
233                                                            StringPool.COLON + clusterNode.getPortalPort();
234                                            }
235    
236                                            request.setAttribute(
237                                                    clusterNode.getClusterNodeId() + "_ERROR_MESSAGE",
238                                                    message);
239                                    }
240                            }
241                    }
242            }
243    
244            public static Map<String, Object> registerOrder(
245                    String orderUuid, String productEntryName, int maxServers) {
246    
247                    Map<String, Object> attributes = new HashMap<>();
248    
249                    if (Validator.isNull(orderUuid)) {
250                            return attributes;
251                    }
252    
253                    try {
254                            JSONObject jsonObject = _createRequest(
255                                    orderUuid, productEntryName, maxServers);
256    
257                            String response = sendRequest(jsonObject.toString());
258    
259                            JSONObject responseJSONObject = new JSONObjectImpl(response);
260    
261                            attributes.put(
262                                    "ORDER_PRODUCT_ID", responseJSONObject.getString("productId"));
263                            attributes.put(
264                                    "ORDER_PRODUCTS", _getOrderProducts(responseJSONObject));
265    
266                            String errorMessage = responseJSONObject.getString("errorMessage");
267    
268                            if (Validator.isNotNull(errorMessage)) {
269                                    attributes.put("ERROR_MESSAGE", errorMessage);
270    
271                                    return attributes;
272                            }
273    
274                            String licenseXML = responseJSONObject.getString("licenseXML");
275    
276                            if (Validator.isNotNull(licenseXML)) {
277                                    LicenseManagerUtil.registerLicense(responseJSONObject);
278    
279                                    attributes.clear();
280                                    attributes.put(
281                                            "SUCCESS_MESSAGE",
282                                            "Your license has been successfully registered.");
283                            }
284                    }
285                    catch (Exception e) {
286                            _log.error(e, e);
287    
288                            attributes.put(
289                                    "ERROR_MESSAGE",
290                                    "There was an error contacting " + LICENSE_SERVER_URL);
291                    }
292    
293                    return attributes;
294            }
295    
296            public static String sendRequest(String request) throws Exception {
297                    HttpClient httpClient = null;
298    
299                    HttpClientConnectionManager httpClientConnectionManager =
300                            new BasicHttpClientConnectionManager();
301    
302                    try {
303                            HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
304    
305                            httpClientBuilder.setConnectionManager(httpClientConnectionManager);
306    
307                            String serverURL = LICENSE_SERVER_URL;
308    
309                            if (!serverURL.endsWith(StringPool.SLASH)) {
310                                    serverURL += StringPool.SLASH;
311                            }
312    
313                            serverURL += "osb-portlet/license";
314    
315                            URI uri = new URI(serverURL);
316    
317                            HttpPost httpPost = new HttpPost(uri);
318    
319                            CredentialsProvider credentialsProvider =
320                                    new BasicCredentialsProvider();
321    
322                            HttpHost proxyHttpHost = null;
323    
324                            if (Validator.isNotNull(_PROXY_URL)) {
325                                    if (_log.isInfoEnabled()) {
326                                            _log.info(
327                                                    "Using proxy " + _PROXY_URL + StringPool.COLON +
328                                                            _PROXY_PORT);
329                                    }
330    
331                                    proxyHttpHost = new HttpHost(_PROXY_URL, _PROXY_PORT);
332    
333                                    if (Validator.isNotNull(_PROXY_USER_NAME)) {
334                                            credentialsProvider.setCredentials(
335                                                    new AuthScope(_PROXY_URL, _PROXY_PORT),
336                                                    new UsernamePasswordCredentials(
337                                                            _PROXY_USER_NAME, _PROXY_PASSWORD));
338                                    }
339                            }
340    
341                            httpClientBuilder.setDefaultCredentialsProvider(
342                                    credentialsProvider);
343                            httpClientBuilder.setProxy(proxyHttpHost);
344    
345                            httpClient = httpClientBuilder.build();
346    
347                            ByteArrayEntity byteArrayEntity = new ByteArrayEntity(
348                                    _encryptRequest(serverURL, request));
349    
350                            byteArrayEntity.setContentType(ContentTypes.APPLICATION_JSON);
351    
352                            httpPost.setEntity(byteArrayEntity);
353    
354                            HttpResponse httpResponse = httpClient.execute(httpPost);
355    
356                            HttpEntity httpEntity = httpResponse.getEntity();
357    
358                            String response = _decryptResponse(
359                                    serverURL, httpEntity.getContent());
360    
361                            if (_log.isDebugEnabled()) {
362                                    _log.debug("Server response: " + response);
363                            }
364    
365                            if (Validator.isNull(response)) {
366                                    throw new Exception("Server response is null");
367                            }
368    
369                            return response;
370                    }
371                    finally {
372                            if (httpClient != null) {
373                                    httpClientConnectionManager.shutdown();
374                            }
375                    }
376            }
377    
378            public static void writeServerProperties(byte[] serverIdBytes)
379                    throws Exception {
380    
381                    File serverIdFile = new File(
382                            LICENSE_REPOSITORY_DIR + "/server/serverId");
383    
384                    FileUtil.write(serverIdFile, serverIdBytes);
385            }
386    
387            private static JSONObject _createRequest(
388                            String orderUuid, String productEntryName, int maxServers)
389                    throws Exception {
390    
391                    JSONObject jsonObject = new JSONObjectImpl();
392    
393                    jsonObject.put("liferayVersion", ReleaseInfo.getBuildNumber());
394                    jsonObject.put("orderUuid", orderUuid);
395                    jsonObject.put("version", 1);
396    
397                    if (Validator.isNull(productEntryName)) {
398                            jsonObject.put(Constants.CMD, "QUERY");
399                    }
400                    else {
401                            jsonObject.put(Constants.CMD, "REGISTER");
402    
403                            if (productEntryName.startsWith("basic")) {
404                                    jsonObject.put("productEntryName", "basic");
405    
406                                    if (productEntryName.equals("basic-cluster")) {
407                                            jsonObject.put("cluster", true);
408                                            jsonObject.put("maxServers", maxServers);
409                                    }
410                                    else if (productEntryName.startsWith("basic-")) {
411                                            String[] productNameArray = StringUtil.split(
412                                                    productEntryName, StringPool.DASH);
413    
414                                            if (productNameArray.length >= 3) {
415                                                    jsonObject.put("clusterId", productNameArray[2]);
416                                                    jsonObject.put("offeringEntryId", productNameArray[1]);
417                                            }
418                                    }
419                            }
420                            else {
421                                    jsonObject.put("productEntryName", productEntryName);
422                            }
423    
424                            jsonObject.put("hostName", PortalUtil.getComputerName());
425                            jsonObject.put("ipAddresses", StringUtil.merge(getIpAddresses()));
426                            jsonObject.put("macAddresses", StringUtil.merge(getMacAddresses()));
427                            jsonObject.put("serverId", com.liferay.portal.license.LicenseManager.getServerId());
428                    }
429    
430                    return jsonObject;
431            }
432    
433            private static String _decryptResponse(
434                            String serverURL, InputStream inputStream)
435                    throws Exception {
436    
437                    if (serverURL.startsWith(Http.HTTPS)) {
438                            return StringUtil.read(inputStream);
439                    }
440    
441                    byte[] bytes = IOUtils.toByteArray(inputStream);
442    
443                    if ((bytes == null) || (bytes.length <= 0)) {
444                            return null;
445                    }
446    
447                    bytes = Encryptor.decryptUnencodedAsBytes(_symmetricKey, bytes);
448    
449                    return new String(bytes, StringPool.UTF8);
450            }
451    
452            private static byte[] _encryptRequest(String serverURL, String request)
453                    throws Exception {
454    
455                    byte[] bytes = request.getBytes(StringPool.UTF8);
456    
457                    if (serverURL.startsWith(Http.HTTPS)) {
458                            return bytes;
459                    }
460    
461                    JSONObject jsonObject = new JSONObjectImpl();
462    
463                    bytes = Encryptor.encryptUnencoded(_symmetricKey, bytes);
464    
465                    jsonObject.put("content", Base64.objectToString(bytes));
466                    jsonObject.put("key", _encryptedSymmetricKey);
467    
468                    return jsonObject.toString().getBytes(StringPool.UTF8);
469            }
470    
471            private static Set<String> _getIPAddresses() {
472                    Set<String> ipAddresses = new HashSet<>();
473    
474                    try {
475                            List<NetworkInterface> networkInterfaces = Collections.list(
476                                    NetworkInterface.getNetworkInterfaces());
477    
478                            for (NetworkInterface networkInterface : networkInterfaces) {
479                                    List<InetAddress> inetAddresses = Collections.list(
480                                            networkInterface.getInetAddresses());
481    
482                                    for (InetAddress inetAddress : inetAddresses) {
483                                            if (inetAddress.isLinkLocalAddress() ||
484                                                    inetAddress.isLoopbackAddress() ||
485                                                    !(inetAddress instanceof Inet4Address)) {
486    
487                                                    continue;
488                                            }
489    
490                                            ipAddresses.add(inetAddress.getHostAddress());
491                                    }
492                            }
493                    }
494                    catch (Exception e) {
495                            _log.error("Unable to read local server's IP addresses", e);
496                    }
497    
498                    return Collections.unmodifiableSet(ipAddresses);
499            }
500    
501            private static Set<String> _getMACAddresses() {
502                    Set<String> macAddresses = new HashSet<>();
503    
504                    try {
505                            List<NetworkInterface> networkInterfaces = Collections.list(
506                                    NetworkInterface.getNetworkInterfaces());
507    
508                            for (NetworkInterface networkInterface : networkInterfaces) {
509                                    byte[] hardwareAddress = networkInterface.getHardwareAddress();
510    
511                                    if (ArrayUtil.isEmpty(hardwareAddress)) {
512                                            continue;
513                                    }
514    
515                                    StringBuilder sb = new StringBuilder(
516                                            (hardwareAddress.length * 3) - 1);
517    
518                                    String hexString = StringUtil.bytesToHexString(hardwareAddress);
519    
520                                    for (int i = 0; i < hexString.length(); i += 2) {
521                                            if (i != 0) {
522                                                    sb.append(CharPool.COLON);
523                                            }
524    
525                                            sb.append(Character.toLowerCase(hexString.charAt(i)));
526                                            sb.append(Character.toLowerCase(hexString.charAt(i + 1)));
527                                    }
528    
529                                    macAddresses.add(sb.toString());
530                            }
531                    }
532                    catch (Exception e) {
533                            _log.error("Unable to read local server's MAC addresses", e);
534                    }
535    
536                    return Collections.unmodifiableSet(macAddresses);
537            }
538    
539            private static Map<String, String> _getOrderProducts(
540                    JSONObject jsonObject) {
541    
542                    JSONObject productsJSONObject = jsonObject.getJSONObject(
543                            "productsJSONObject");
544    
545                    if (productsJSONObject == null) {
546                            return null;
547                    }
548    
549                    Map<String, String> sortedMap = new TreeMap<>(
550                            String.CASE_INSENSITIVE_ORDER);
551    
552                    Iterator<String> itr = productsJSONObject.keys();
553    
554                    while (itr.hasNext()) {
555                            String key = itr.next();
556    
557                            sortedMap.put(key, productsJSONObject.getString(key));
558                    }
559    
560                    return sortedMap;
561            }
562    
563            private static void _initKeys() {
564                    ClassLoader classLoader = ClassLoaderUtil.getPortalClassLoader();
565    
566                    if ((classLoader == null) || (_encryptedSymmetricKey != null)) {
567                            return;
568                    }
569    
570                    try {
571                            URL url = classLoader.getResource(
572                                    "com/liferay/portal/license/public.key");
573    
574                            byte[] bytes = IOUtils.toByteArray(url.openStream());
575    
576                            X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(
577                                    bytes);
578    
579                            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
580    
581                            PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
582    
583                            KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
584    
585                            keyGenerator.init(128, new SecureRandom());
586    
587                            _symmetricKey = keyGenerator.generateKey();
588    
589                            byte[] encryptedSymmetricKey = Encryptor.encryptUnencoded(
590                                    publicKey, _symmetricKey.getEncoded());
591    
592                            _encryptedSymmetricKey = Base64.objectToString(
593                                    encryptedSymmetricKey);
594                    }
595                    catch (Exception e) {
596                            _log.error(e, e);
597                    }
598            }
599    
600            private static void _registerClusterOrder(
601                            HttpServletRequest request, ClusterNode clusterNode,
602                            String orderUuid, String productEntryName, int maxServers)
603                    throws Exception {
604    
605                    MethodHandler methodHandler = new MethodHandler(
606                            _registerOrderMethodKey, orderUuid, productEntryName, maxServers);
607    
608                    ClusterRequest clusterRequest = ClusterRequest.createUnicastRequest(
609                            methodHandler, clusterNode.getClusterNodeId());
610    
611                    FutureClusterResponses futureClusterResponses =
612                            ClusterExecutorUtil.execute(clusterRequest);
613    
614                    ClusterNodeResponses clusterNodeResponses = futureClusterResponses.get(
615                            20000, TimeUnit.MILLISECONDS);
616    
617                    ClusterNodeResponse clusterNodeResponse =
618                            clusterNodeResponses.getClusterResponse(
619                                    clusterNode.getClusterNodeId());
620    
621                    Map<String, Object> attributes =
622                            (Map<String, Object>)clusterNodeResponse.getResult();
623    
624                    for (Map.Entry<String, Object> entry : attributes.entrySet()) {
625                            request.setAttribute(
626                                    clusterNode.getClusterNodeId() + StringPool.UNDERLINE +
627                                            entry.getKey(),
628                                    entry.getValue());
629                    }
630            }
631    
632            private static final String _PROXY_PASSWORD = GetterUtil.getString(
633                    PropsUtil.get("license.proxy.password"));
634    
635            private static final int _PROXY_PORT = GetterUtil.getInteger(
636                    PropsUtil.get("license.proxy.port"), 80);
637    
638            private static final String _PROXY_URL = PropsUtil.get("license.proxy.url");
639    
640            private static final String _PROXY_USER_NAME = GetterUtil.getString(
641                    PropsUtil.get("license.proxy.username"));
642    
643            private static final Log _log = LogFactoryUtil.getLog(LicenseUtil.class);
644    
645            private static String _encryptedSymmetricKey;
646            private static final MethodHandler _getServerInfoMethodHandler =
647                    new MethodHandler(new MethodKey(LicenseUtil.class, "getServerInfo"));
648            private static final Set<String> _ipAddresses;
649            private static final Set<String> _macAddresses;
650            private static final MethodKey _registerOrderMethodKey = new MethodKey(
651                    LicenseUtil.class, "registerOrder", String.class, String.class,
652                    int.class);
653            private static byte[] _serverIdBytes;
654            private static Key _symmetricKey;
655    
656            static {
657                    _initKeys();
658    
659                    _ipAddresses = _getIPAddresses();
660                    _macAddresses = _getMACAddresses();
661            }
662    
663    }