001
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
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:
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 }