/* * Copyright 2000-2016 Vaadin Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package com.vaadin.tools; import static com.vaadin.tools.CvalAddonsChecker.VAADIN_ADDON_LICENSE; import static com.vaadin.tools.CvalAddonsChecker.VAADIN_ADDON_NAME; import static com.vaadin.tools.CvalAddonsChecker.VAADIN_ADDON_TITLE; import static com.vaadin.tools.CvalAddonsChecker.VAADIN_ADDON_VERSION; import static com.vaadin.tools.CvalChecker.GRACE_DAYS_MSECS; import static com.vaadin.tools.CvalChecker.cacheLicenseInfo; import static com.vaadin.tools.CvalChecker.deleteCache; import static com.vaadin.tools.CvalChecker.parseJson; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileDescriptor; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintStream; import java.io.PrintWriter; import java.lang.reflect.Method; import java.net.URL; import java.net.URLClassLoader; import java.util.jar.JarOutputStream; import java.util.jar.Manifest; import java.util.prefs.Preferences; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import com.vaadin.tools.CvalChecker.CvalInfo; import com.vaadin.tools.CvalChecker.CvalServer; import com.vaadin.tools.CvalChecker.InvalidCvalException; import com.vaadin.tools.CvalChecker.UnreachableCvalServerException; /** * The CvalChecker test. */ public class CvalCheckerTest { static final String productNameCval = "test.cval"; static final String productTitleCval = "Vaadin Test"; static final String productNameAgpl = "test.agpl"; static final String productTitleAgpl = "Vaadin Test"; static final String productNameApache = "test.apache"; static final String VALID_KEY = "valid"; static final String INVALID_KEY = "invalid"; static final String responseJson = "{'licenseKey':'" + VALID_KEY + "'," + "'licensee':'Test User','type':'normal'," + "'expiredEpoch':1893511225000," + "'product':{'name':'" + productNameCval + "', 'version': 2}}"; static final String responseJsonWithNullVersion = "{'licenseKey':'" + VALID_KEY + "'," + "'licensee':'Test User','type':'normal'," + "'expiredEpoch':1893511225000," + "'product':{'name':'" + productNameCval + "', 'version': null}}"; private static ByteArrayOutputStream outContent; // A provider returning a valid license if productKey is valid or null if // invalid static final CvalServer validLicenseProvider = new CvalServer() { @Override String askServer(String productName, String productKey, int timeout) { return VALID_KEY.equals(productKey) ? responseJson : null; } }; // A provider returning a valid evaluation license static final CvalServer validEvaluationLicenseProvider = new CvalServer() { @Override String askServer(String productName, String productKey, int timeout) { return responseJson.replace("normal", "evaluation"); } }; // A provider returning an expired license with a server message static final CvalServer expiredLicenseProviderWithMessage = new CvalServer() { @Override String askServer(String productName, String productKey, int timeout) { return responseJson.replace("'expired", "'message':'Custom\\\\nServer\\\\nMessage','expired':true,'expired"); } }; // A provider returning an expired license with a server message static final CvalServer expiredLicenseProvider = new CvalServer() { @Override String askServer(String productName, String productKey, int timeout) { return responseJson.replace("'expired", "'expired':true,'expired"); } }; // A provider returning an expired epoch license static final CvalServer expiredEpochLicenseProvider = new CvalServer() { @Override String askServer(String productName, String productKey, int timeout) { long ts = System.currentTimeMillis() - GRACE_DAYS_MSECS - 1000; return responseJson.replace("1893511225000", "" + ts); } }; // A provider returning an unlimited license static final CvalServer unlimitedLicenseProvider = new CvalServer() { @Override String askServer(String productName, String productKey, int timeout) { return responseJson.replaceFirst("1893511225000", "null"); } }; // An unreachable provider static final CvalServer unreachableLicenseProvider = new CvalServer() { @Override String askServer(String productName, String productKey, int timeout) throws IOException { // Normally there is no route for this ip in public routers, so we // should get a timeout. licenseUrl = "http://localhost:9999/"; return super.askServer(productName, productKey, 1000); } }; // A provider with 'null' in the version field static final CvalServer nullVersionLicenseProvider = new CvalServer() { @Override String askServer(String productName, String productKey, int timeout) throws IOException { return responseJsonWithNullVersion; } }; private CvalChecker licenseChecker; private String licenseName; @Before public void setup() { licenseChecker = new CvalChecker() .setLicenseProvider(validLicenseProvider); licenseName = CvalChecker.computeLicenseName(productNameCval); System.getProperties().remove(licenseName); deleteCache(productNameCval); } @Test public void testValidateProduct() throws Exception { deleteCache(productNameCval); // If the license key in our environment is null, throw an exception try { licenseChecker.validateProduct(productNameCval, "2.1", productTitleCval); Assert.fail(); } catch (InvalidCvalException expected) { assertEquals(productNameCval, expected.name); } Assert.assertFalse(cacheExists(productNameCval)); // If the license key is empty, throw an exception System.setProperty(licenseName, ""); try { licenseChecker.validateProduct(productNameCval, "2.1", productTitleCval); Assert.fail(); } catch (InvalidCvalException expected) { assertEquals(productNameCval, expected.name); } Assert.assertFalse(cacheExists(productNameCval)); // If license key is invalid, throw an exception System.setProperty(licenseName, "invalid"); try { licenseChecker.validateProduct(productNameCval, "2.1", productTitleCval); Assert.fail(); } catch (InvalidCvalException expected) { assertEquals(productNameCval, expected.name); } Assert.assertFalse(cacheExists(productNameCval)); // Fail if version is bigger System.setProperty(licenseName, VALID_KEY); try { licenseChecker.validateProduct(productNameCval, "3.0", productTitleCval); Assert.fail(); } catch (InvalidCvalException expected) { assertEquals(productNameCval, expected.name); assertTrue(expected.getMessage().contains("is not valid")); } Assert.assertFalse(cacheExists(productNameCval)); // Success if license key and version are valid System.setProperty(licenseName, VALID_KEY); licenseChecker.validateProduct(productNameCval, "2.1", productTitleCval); Assert.assertTrue(cacheExists(productNameCval)); // Success if license and cache file are valid, although the license // server is offline licenseChecker.setLicenseProvider(unreachableLicenseProvider); licenseChecker.validateProduct(productNameCval, "2.1", productTitleCval); Assert.assertTrue(cacheExists(productNameCval)); // Fail if license key changes although cache file were validated // previously and it is ok, we are offline try { System.setProperty(licenseName, INVALID_KEY); licenseChecker.validateProduct(productNameCval, "2.1", productTitleCval); Assert.fail(); } catch (InvalidCvalException expected) { Assert.fail(); } catch (UnreachableCvalServerException expected) { assertEquals(productNameCval, expected.name); } Assert.assertFalse(cacheExists(productNameCval)); // Fail with unreachable exception if license has never verified and // server is offline try { System.setProperty(licenseName, VALID_KEY); licenseChecker.validateProduct(productNameCval, "2.1", productTitleCval); Assert.fail(); } catch (InvalidCvalException expected) { Assert.fail(); } catch (UnreachableCvalServerException expected) { assertEquals(productNameCval, expected.name); } Assert.assertFalse(cacheExists(productNameCval)); // Fail when expired flag comes in the server response, although the // expired is valid. deleteCache(productNameCval); licenseChecker.setLicenseProvider(expiredLicenseProviderWithMessage); try { licenseChecker.validateProduct(productNameCval, "2.1", productTitleCval); Assert.fail(); } catch (InvalidCvalException expected) { assertEquals(productNameCval, expected.name); // Check that we use server customized message if it comes Assert.assertTrue(expected.getMessage().contains("Custom")); } Assert.assertTrue(cacheExists(productNameCval)); // Check an unlimited license deleteCache(productNameCval); licenseChecker.setLicenseProvider(unlimitedLicenseProvider); licenseChecker.validateProduct(productNameCval, "2.1", productTitleCval); Assert.assertTrue(cacheExists(productNameCval)); // Fail if expired flag does not come, but expired epoch is in the past System.setProperty(licenseName, VALID_KEY); deleteCache(productNameCval); licenseChecker.setLicenseProvider(expiredEpochLicenseProvider); try { licenseChecker.validateProduct(productNameCval, "2.1", productTitleCval); Assert.fail(); } catch (InvalidCvalException expected) { assertEquals(productNameCval, expected.name); } Assert.assertTrue(cacheExists(productNameCval)); deleteCache(productNameCval); licenseChecker.setLicenseProvider(nullVersionLicenseProvider); licenseChecker.validateProduct(productNameCval, "2.1", productTitleCval); Assert.assertTrue(cacheExists(productNameCval)); } /* * Creates a new .jar file with a MANIFEST.MF with all vaadin license info * attributes set, and add the .jar to the classpath */ static void addLicensedJarToClasspath(String productName, String licenseType) throws Exception { // Create a manifest with Vaadin CVAL license Manifest testManifest = new Manifest(); testManifest.getMainAttributes().putValue("Manifest-Version", "1.0"); testManifest.getMainAttributes().putValue(VAADIN_ADDON_LICENSE, licenseType); testManifest.getMainAttributes().putValue(VAADIN_ADDON_NAME, productName); testManifest.getMainAttributes().putValue(VAADIN_ADDON_TITLE, "Test " + productName); testManifest.getMainAttributes().putValue(VAADIN_ADDON_VERSION, "2"); // Create a temporary Jar File testJarFile = File.createTempFile("vaadin." + productName, ".jar"); testJarFile.deleteOnExit(); JarOutputStream target = new JarOutputStream( new FileOutputStream(testJarFile), testManifest); target.close(); // Add the new jar to our classpath (use reflection) URL url = testJarFile.toURI().toURL(); final Method addURL = URLClassLoader.class.getDeclaredMethod("addURL", new Class[] { URL.class }); addURL.setAccessible(true); final URLClassLoader urlClassLoader = (URLClassLoader) Thread .currentThread().getContextClassLoader(); addURL.invoke(urlClassLoader, new Object[] { url }); } static boolean cacheExists(String productName) { return cachedPreferences(productName) != null; } static String cachedPreferences(String productName) { // ~/Library/Preferences/com.apple.java.util.prefs.plist // .java/.userPrefs/com/google/gwt/dev/shell/prefs.xml // HKEY_CURRENT_USER\SOFTWARE\JavaSoft\Prefs Preferences p = Preferences.userNodeForPackage(CvalInfo.class); return p.get(productName, null); } static void saveCache(String productName, String key, Boolean expired, Long expireTs, String type) { String json = responseJson.replace(productNameCval, productName); if (expired != null && expired) { expireTs = System.currentTimeMillis() - GRACE_DAYS_MSECS - 1000; } if (expireTs != null) { json = json.replace("1893511225000", "" + expireTs); } if (key != null) { json = json.replace(VALID_KEY, key); } if (type != null) { json = json.replace("normal", type); } cacheLicenseInfo(parseJson(json)); } static void captureSystemOut() { outContent = new ByteArrayOutputStream(); System.setOut(new PrintStream(outContent)); } static String readSystemOut() { restoreSystemOut(); return outContent.toString(); } static void restoreSystemOut() { System.setOut( new PrintStream(new FileOutputStream(FileDescriptor.out))); } @Test(expected = FileNotFoundException.class) public void testReadKeyFromFile_NonexistingLicenseFile() throws Exception { licenseChecker.readKeyFromFile(new URL("file:///foobar.baz"), 4); } @Test public void testReadKeyFromFile_LicenseFileEmpty() throws Exception { File tmpLicenseFile = File.createTempFile("license", "lic"); assertNull(licenseChecker .readKeyFromFile(tmpLicenseFile.toURI().toURL(), 4)); tmpLicenseFile.delete(); } @Test public void testReadKeyFromFile_LicenseFileHasSingleUnidentifiedKey() throws Exception { File tmpLicenseFile = File.createTempFile("license", "lic"); PrintWriter out = new PrintWriter(tmpLicenseFile); out.println("this-is-a-license"); out.close(); assertEquals("this-is-a-license", licenseChecker .readKeyFromFile(tmpLicenseFile.toURI().toURL(), 4)); tmpLicenseFile.delete(); } @Test public void testReadKeyFromFile_LicenseFileHasSingleIdentifiedKey() throws Exception { File tmpLicenseFile = File.createTempFile("license", "lic"); PrintWriter out = new PrintWriter(tmpLicenseFile); out.println("4=this-is-a-license"); out.close(); assertEquals("this-is-a-license", licenseChecker .readKeyFromFile(tmpLicenseFile.toURI().toURL(), 4)); tmpLicenseFile.delete(); } @Test public void testReadKeyFromFile_LicenseFileHasMultipleKeys() throws Exception { File tmpLicenseFile = File.createTempFile("license", "lic"); PrintWriter out = new PrintWriter(tmpLicenseFile); out.println("4=this-is-a-license"); out.println("5=this-is-another-license"); out.close(); assertEquals("this-is-a-license", licenseChecker .readKeyFromFile(tmpLicenseFile.toURI().toURL(), 4)); assertEquals("this-is-another-license", licenseChecker .readKeyFromFile(tmpLicenseFile.toURI().toURL(), 5)); tmpLicenseFile.delete(); } @Test public void testReadKeyFromFile_LicenseFileHasMultipleKeysWithWhitespace() throws Exception { File tmpLicenseFile = File.createTempFile("license", "lic"); PrintWriter out = new PrintWriter(tmpLicenseFile); out.println("4 = this-is-a-license"); out.println("5 = this-is-another-license"); out.close(); assertEquals("this-is-a-license", licenseChecker .readKeyFromFile(tmpLicenseFile.toURI().toURL(), 4)); assertEquals("this-is-another-license", licenseChecker .readKeyFromFile(tmpLicenseFile.toURI().toURL(), 5)); tmpLicenseFile.delete(); } @Test public void testReadKeyFromFile_RequestedVersionMissing() throws Exception { File tmpLicenseFile = File.createTempFile("license", "lic"); PrintWriter out = new PrintWriter(tmpLicenseFile); out.println("4 = this-is-a-license"); out.println("5 = this-is-another-license"); out.close(); assertNull(licenseChecker .readKeyFromFile(tmpLicenseFile.toURI().toURL(), 3)); tmpLicenseFile.delete(); } @Test public void testReadKeyFromFile_FallbackToDefaultKey() throws Exception { File tmpLicenseFile = File.createTempFile("license", "lic"); PrintWriter out = new PrintWriter(tmpLicenseFile); out.println("this-is-a-license"); out.println("5 = this-is-another-license"); out.close(); assertEquals("this-is-a-license", licenseChecker .readKeyFromFile(tmpLicenseFile.toURI().toURL(), 3)); assertEquals("this-is-a-license", licenseChecker .readKeyFromFile(tmpLicenseFile.toURI().toURL(), 4)); assertEquals("this-is-another-license", licenseChecker .readKeyFromFile(tmpLicenseFile.toURI().toURL(), 5)); tmpLicenseFile.delete(); } @Test public void testReadKeyFromFile_FallbackToDefaultKeyReversed() throws Exception { File tmpLicenseFile = File.createTempFile("license", "lic"); PrintWriter out = new PrintWriter(tmpLicenseFile); out.println("5 = this-is-another-license"); out.println("this-is-a-license"); out.close(); assertEquals("this-is-a-license", licenseChecker .readKeyFromFile(tmpLicenseFile.toURI().toURL(), 3)); assertEquals("this-is-a-license", licenseChecker .readKeyFromFile(tmpLicenseFile.toURI().toURL(), 4)); assertEquals("this-is-another-license", licenseChecker .readKeyFromFile(tmpLicenseFile.toURI().toURL(), 5)); tmpLicenseFile.delete(); } }