diff --git a/.gitignore b/.gitignore index aee0706..a9ed789 100644 --- a/.gitignore +++ b/.gitignore @@ -99,4 +99,5 @@ fabric.properties .idea/httpRequests # Android studio 3.1+ serialized cache file -.idea/caches/build_file_checksums.ser \ No newline at end of file +.idea/caches/build_file_checksums.ser +apikeys.json diff --git a/src/main/java/io/cryptolens/internal/HelperMethods.java b/src/main/java/io/cryptolens/internal/HelperMethods.java index c3625be..25dc22f 100644 --- a/src/main/java/io/cryptolens/internal/HelperMethods.java +++ b/src/main/java/io/cryptolens/internal/HelperMethods.java @@ -5,16 +5,17 @@ import io.cryptolens.legacy.RequestHandler; import java.lang.reflect.Field; -import java.util.HashMap; -import java.util.Map; +import java.util.*; public class HelperMethods { public static T SendRequestToWebAPI(String method, Object model, Map extraParams, Class clazz) { Map params = new HashMap<>(); + List allFields = new ArrayList<>(); + getAllFields(allFields, model.getClass()); - for(Field field : model.getClass().getDeclaredFields()) { + for(Field field : allFields) { field.setAccessible(true); try { Object value = field.get(model); @@ -47,4 +48,15 @@ public static T SendRequestToWebAPI(String method, Objec return null; } + + // from: https://stackoverflow.com/a/1042827/1275924 + private static List getAllFields(List fields, Class type) { + fields.addAll(Arrays.asList(type.getDeclaredFields())); + + if (type.getSuperclass() != null) { + getAllFields(fields, type.getSuperclass()); + } + + return fields; + } } diff --git a/src/main/java/io/cryptolens/methods/Data.java b/src/main/java/io/cryptolens/methods/Data.java new file mode 100644 index 0000000..c0ad111 --- /dev/null +++ b/src/main/java/io/cryptolens/methods/Data.java @@ -0,0 +1,240 @@ +package io.cryptolens.methods; + +import com.google.gson.Gson; +import io.cryptolens.internal.*; +import io.cryptolens.models.*; + +import java.util.HashMap; +import java.util.Map; + +/** + *

The following methods allow you to work with data objects (aka. metadata or custom variables) associated with a + * license key. Data objects can be used to store specific properties that (eg. username, OS version). More importantly + * though, they are used if you plan to implement a usage-based licensing model. + *

+ * + *

Access token remarks: When you create an access token for any of the methods below, we recommend to specify the product + * and to set the keylock to -1.

+ */ +public class Data { + /** + * Adds a new data object to a license key. + * @param token The access token with 'AddDataObject' permission and KeyLock set to '-1'. + * @param license The license key object (it's used to get the product id and key string). + * @param name The name of the data object. Max 10 characters. + * @param intValue An int value (int32) to store. + * @param stringValue A string value (text) to store. Max 10000 characters. + * @return + */ + public static BasicResult AddDataObject(String token, LicenseKey license, String name, int intValue, String stringValue) { + return AddDataObject(token, new AddDataObjectToKeyModel(license.ProductId, license.Key, name, intValue, stringValue)); + } + + public static BasicResult AddDataObject(String token, AddDataObjectToKeyModel model) { + + Map extraParams = new HashMap<>(); + + extraParams.put("token", token); + + return HelperMethods.SendRequestToWebAPI("data/AddDataObjectToKey", model, extraParams, BasicResult.class); + } + + /** + * List data objects of a certain license. + * @param token The access token with 'ListDataObjects' permission and KeyLock set to '-1'. + * @param license The license key object (it's used to get the product id and key string). + * @return + */ + public static ListOfDataObjectsResult ListDataObjects(String token, LicenseKey license) { + return ListDataObjects(token, new ListDataObjectsToKeyModel(license.ProductId, license.Key, "")); + } + + /** + * List data objects of a certain license. + * @param token The access token with 'ListDataObjects' permission and KeyLock set to '-1'. + * @param license The license key object (it's used to get the product id and key string). + * @param contains Shows only Data Objects where the name contains the following string. + * @return + */ + public static ListOfDataObjectsResult ListDataObjects(String token, LicenseKey license, String contains) { + return ListDataObjects(token, new ListDataObjectsToKeyModel(license.ProductId, license.Key, contains)); + } + + public static ListOfDataObjectsResult ListDataObjects(String token, ListDataObjectsToKeyModel model) { + + Map extraParams = new HashMap<>(); + + extraParams.put("token", token); + + return HelperMethods.SendRequestToWebAPI("data/listdataobjectstokey", model, extraParams, ListOfDataObjectsResult.class); + } + + /** + * This method will assign a new integer value to a Data Object. + * @param token The access token with 'SetIntValue' permission and KeyLock set to '-1'. + * @param license The license key object (it's used to get the product id and key string). + * @param id The unique object id for the data object. + * @param intValue The new int value that should be assigned to the data object. + * @return + */ + public static BasicResult SetIntValue(String token, LicenseKey license, long id, int intValue) { + return SetIntValue(token, new SetIntValueToKeyModel(license.ProductId, license.Key, id, intValue)); + } + + public static BasicResult SetIntValue(String token, SetIntValueToKeyModel model) { + + Map extraParams = new HashMap<>(); + + extraParams.put("token", token); + + return HelperMethods.SendRequestToWebAPI("data/setintvaluetokey", model, extraParams, BasicResult.class); + } + + /** + * This method will increment the integer value in a Data Object by a certain constant (non-negative). + * You can always decrement it. Note, this method does not allow integer overflows, i.e. if you increment + * by a constant that would result in an overflow, an error will be thrown. Note also that you can use the Feature lock + * in the Access Token to specify the upper bound of the increment constant. So, if you only want to allow incrementing + * by 1, please set Feature lock field to 1 also. Please see Remarks for more details (including access token set up). + * @param token The access token with 'IncrementIntValue' permission and KeyLock set to '-1'. + * @param license The license key object (it's used to get the product id and key string). + * @param id The unique object id for the data object. + * @param intValue The constant int (non-negative) value that should be added to the current + * IntValue of the data object. For example, if this value is set to 5 and the + * old IntValue is 1, then the new IntValue will be the old one plus 5, i.e. 6. + * Note, if you would set this value to -5 instead, the same result would be achieved. + * @return + */ + public static BasicResult IncrementIntValue(String token, LicenseKey license, long id, int intValue) { + return IncrementIntValue(token, new IncrementIntValueToKeyModel(license.ProductId, license.Key, id, intValue, false, 0)); + } + + /** + * This method will increment the integer value in a Data Object by a certain constant (non-negative). + * You can always decrement it. Note, this method does not allow integer overflows, i.e. if you increment + * by a constant that would result in an overflow, an error will be thrown. Note also that you can use the Feature lock + * in the Access Token to specify the upper bound of the increment constant. So, if you only want to allow incrementing + * by 1, please set Feature lock field to 1 also. Please see Remarks for more details (including access token set up). + * @param token The access token with 'IncrementIntValue' permission and KeyLock set to '-1'. + * @param license The license key object (it's used to get the product id and key string). + * @param id The unique object id for the data object. + * @param intValue The constant int (non-negative) value that should be added to the current + * IntValue of the data object. For example, if this value is set to 5 and the + * old IntValue is 1, then the new IntValue will be the old one plus 5, i.e. 6. + * Note, if you would set this value to -5 instead, the same result would be achieved. + * @param enableBound If set to true, it will be possible to specify an upper bound. For example, + * if you set the Bound parameter (below) to 10, you will be able to increment + * the int value until you reach ten (inclusive). Once the upper bound is reached, + * an error will be thrown. + * @param bound This is the upper bound that will be enforced on the increment operation. + * It will only be enforced if EnableBound is set to true. Please read the description about enableBound. + * @return + */ + public static BasicResult IncrementIntValue(String token, LicenseKey license, long id, int intValue, boolean enableBound, int bound) { + return IncrementIntValue(token, new IncrementIntValueToKeyModel(license.ProductId, license.Key, id, intValue, enableBound, bound)); + } + + public static BasicResult IncrementIntValue(String token, IncrementIntValueToKeyModel model) { + + Map extraParams = new HashMap<>(); + + extraParams.put("token", token); + + return HelperMethods.SendRequestToWebAPI("data/incrementintvaluetokey", model, extraParams, BasicResult.class); + } + + /** + * This method will decrement the integer value in a Data Object by a certain constant (non-negative). + * You can always increment it. Note, this method does not allow integer overflows, i.e. if you decrement + * by a constant that would result in an overflow, an error will be thrown. Note also that you can use the + * Feature lock in the Access Token to specify the upper bound of the decrement constant. So, if you only + * want to allow decrementing by 1, please set Feature lock field to 1 also. Please see Remarks for more + * details (including access token setup). + * @param token The access token with 'SetIntValue' permission and KeyLock set to '-1'. + * @param license The license key object (it's used to get the product id and key string). + * @param id The unique object id for the data object. + * @param intValue The constant int value that should be subtracted to the current IntValue of the data object. + * For example, if this value is set to 5 and the old IntValue is 11, then the new IntValue + * will be the old one minus 5, i.e. 6. Note, if you would set this value to -5 instead, the + * same result would be achieved. + * @return + */ + public static BasicResult DecrementIntValue(String token, LicenseKey license, long id, int intValue) { + return DecrementIntValue(token, new DecrementIntValueToKeyModel(license.ProductId, license.Key, id, intValue, false, 0)); + } + + /** + * This method will decrement the integer value in a Data Object by a certain constant (non-negative). + * You can always increment it. Note, this method does not allow integer overflows, i.e. if you decrement + * by a constant that would result in an overflow, an error will be thrown. Note also that you can use the + * Feature lock in the Access Token to specify the upper bound of the decrement constant. So, if you only + * want to allow decrementing by 1, please set Feature lock field to 1 also. Please see Remarks for more + * details (including access token setup). + * @param token The access token with 'SetIntValue' permission and KeyLock set to '-1'. + * @param license The license key object (it's used to get the product id and key string). + * @param id The unique object id for the data object. + * @param intValue The constant int value that should be subtracted to the current IntValue of the data object. + * For example, if this value is set to 5 and the old IntValue is 11, then the new IntValue + * will be the old one minus 5, i.e. 6. Note, if you would set this value to -5 instead, the + * same result would be achieved. + * @param enableBound If set to true, it will be possible to specify a lower bound. For example, if you set the Bound + * parameter (below) to 0, you will be able to decrement the int value until you reach zero (inclusive). + * Once the lower bound is reached, an error will be thrown. + * @param bound This is the lower bound that will be enforced on the decrement operation. It will only be enforced if + * EnableBound is set to true. Please read the description above. + * @return + */ + public static BasicResult DecrementIntValue(String token, LicenseKey license, long id, int intValue, boolean enableBound, int bound) { + return DecrementIntValue(token, new DecrementIntValueToKeyModel(license.ProductId, license.Key, id, intValue, enableBound, bound)); + } + + public static BasicResult DecrementIntValue(String token, DecrementIntValueToKeyModel model) { + + Map extraParams = new HashMap<>(); + + extraParams.put("token", token); + + return HelperMethods.SendRequestToWebAPI("data/decrementintvaluetokey", model, extraParams, BasicResult.class); + } + + /** + * This method will assign a new string value to a Data Object. + * @param token The access token with 'SetIntValue' permission and KeyLock set to '-1'. + * @param license The license key object (it's used to get the product id and key string). + * @param id The unique object id for the data object. + * @param stringValue A string value (text) to store. Max 10000 characters. + * @return + */ + public static BasicResult SetStringValue(String token, LicenseKey license, long id, String stringValue) { + return SetStringValue(token, new SetStringValueToKeyModel(license.ProductId, license.Key, id, stringValue)); + } + + public static BasicResult SetStringValue(String token, SetStringValueToKeyModel model) { + + Map extraParams = new HashMap<>(); + + extraParams.put("token", token); + + return HelperMethods.SendRequestToWebAPI("data/setstringvaluetokey", model, extraParams, BasicResult.class); + } + + /** + * This method will remove an existing Data Object. + * @param token The access token with 'SetIntValue' permission and KeyLock set to '-1'. + * @param license The license key object (it's used to get the product id and key string). + * @param id A string value (text) to store. Max 10000 characters. + * @return + */ + public static BasicResult RemoveDataObject(String token, LicenseKey license, long id) { + return RemoveDataObject(token, new RemoveDataObjectToKeyModel(license.ProductId, license.Key, id)); + } + + public static BasicResult RemoveDataObject(String token, RemoveDataObjectToKeyModel model) { + + Map extraParams = new HashMap<>(); + + extraParams.put("token", token); + + return HelperMethods.SendRequestToWebAPI("data/removedataobjecttokey", model, extraParams, BasicResult.class); + } +} diff --git a/src/main/java/io/cryptolens/methods/Helpers.java b/src/main/java/io/cryptolens/methods/Helpers.java index 2a8c2ae..ee1f666 100644 --- a/src/main/java/io/cryptolens/methods/Helpers.java +++ b/src/main/java/io/cryptolens/methods/Helpers.java @@ -1,5 +1,6 @@ package io.cryptolens.methods; +import io.cryptolens.internal.BasicResult; import io.cryptolens.models.ActivatedMachine; import io.cryptolens.models.LicenseKey; import oshi.SystemInfo; @@ -221,6 +222,17 @@ private static String SHA256(String rawData) { } } + /** + * Checks if a response from Cryptolens is successful. + * @param result The response from an API call. All responses inherit from BasicResult. + * @return True if the response is successful and false otherwise. + */ + public static boolean IsSuccessful(BasicResult result) { + if(result == null || result.result == 1) + return false; + return true; + } + private static String getRawDeviceID() { //thanks to https://stackoverflow.com/a/37705082. may require root. diff --git a/src/main/java/io/cryptolens/models/AddDataObjectToKeyModel.java b/src/main/java/io/cryptolens/models/AddDataObjectToKeyModel.java new file mode 100644 index 0000000..de245e7 --- /dev/null +++ b/src/main/java/io/cryptolens/models/AddDataObjectToKeyModel.java @@ -0,0 +1,24 @@ +package io.cryptolens.models; + +public class AddDataObjectToKeyModel extends ProductAndKeyModel { + /** + * The name of the data object. Max 10 characters. + */ + public String Name; + /** + * An int value (int32) to store. + */ + public int IntValue; + /** + * A string value (text) to store. Max 10000 characters. + */ + public String StringValue; + + public AddDataObjectToKeyModel(int productId, String key, String name, int intValue, String stringValue) { + Name = name; + StringValue = stringValue; + IntValue = intValue; + this.ProductId = productId; + this.Key = key; + } +} diff --git a/src/main/java/io/cryptolens/models/DataObject.java b/src/main/java/io/cryptolens/models/DataObject.java index fecfd9f..d7fe21e 100644 --- a/src/main/java/io/cryptolens/models/DataObject.java +++ b/src/main/java/io/cryptolens/models/DataObject.java @@ -1,8 +1,14 @@ package io.cryptolens.models; +import com.google.gson.annotations.SerializedName; + public class DataObject { + @SerializedName(value = "id", alternate = {"Id"}) public int Id; + @SerializedName(value = "name", alternate = {"Name"}) public String Name; + @SerializedName(value = "stringValue", alternate = {"StringValue"}) public String StringValue; + @SerializedName(value = "intValue", alternate = {"IntValue"}) public int IntValue; } diff --git a/src/main/java/io/cryptolens/models/DecrementIntValueToKeyModel.java b/src/main/java/io/cryptolens/models/DecrementIntValueToKeyModel.java new file mode 100644 index 0000000..9a77076 --- /dev/null +++ b/src/main/java/io/cryptolens/models/DecrementIntValueToKeyModel.java @@ -0,0 +1,17 @@ +package io.cryptolens.models; + +public class DecrementIntValueToKeyModel extends ProductAndKeyModel { + public long Id; + public int IntValue; + public boolean EnableBound; + public int Bound; + + public DecrementIntValueToKeyModel(int productId, String key, long id, int intValue, boolean enableBound, int bound) { + Id = id; + IntValue = intValue; + this.ProductId = productId; + this.Key = key; + this.EnableBound = enableBound; + this.Bound = bound; + } +} diff --git a/src/main/java/io/cryptolens/models/IncrementIntValueToKeyModel.java b/src/main/java/io/cryptolens/models/IncrementIntValueToKeyModel.java new file mode 100644 index 0000000..32e6936 --- /dev/null +++ b/src/main/java/io/cryptolens/models/IncrementIntValueToKeyModel.java @@ -0,0 +1,17 @@ +package io.cryptolens.models; + +public class IncrementIntValueToKeyModel extends ProductAndKeyModel { + public long Id; + public int IntValue; + public boolean EnableBound; + public int Bound; + + public IncrementIntValueToKeyModel(int productId, String key, long id, int intValue, boolean enableBound, int bound) { + Id = id; + this.IntValue = intValue; + this.ProductId = productId; + this.Key = key; + this.EnableBound = enableBound; + this.Bound = bound; + } +} diff --git a/src/main/java/io/cryptolens/models/ListDataObjectsToKeyModel.java b/src/main/java/io/cryptolens/models/ListDataObjectsToKeyModel.java new file mode 100644 index 0000000..bc51276 --- /dev/null +++ b/src/main/java/io/cryptolens/models/ListDataObjectsToKeyModel.java @@ -0,0 +1,11 @@ +package io.cryptolens.models; + +public class ListDataObjectsToKeyModel extends ProductAndKeyModel { + public String Contains = ""; + + public ListDataObjectsToKeyModel(int productId, String key, String contains) { + Contains = contains; + this.ProductId = productId; + this.Key = key; + } +} diff --git a/src/main/java/io/cryptolens/models/ListOfDataObjectsResult.java b/src/main/java/io/cryptolens/models/ListOfDataObjectsResult.java new file mode 100644 index 0000000..581f914 --- /dev/null +++ b/src/main/java/io/cryptolens/models/ListOfDataObjectsResult.java @@ -0,0 +1,11 @@ +package io.cryptolens.models; + +import com.google.gson.annotations.SerializedName; +import io.cryptolens.internal.BasicResult; + +import java.util.List; + +public class ListOfDataObjectsResult extends BasicResult { + @SerializedName(value = "dataObjects", alternate = {"DataObjects"}) + public List DataObjects; +} diff --git a/src/main/java/io/cryptolens/models/ProductAndKeyModel.java b/src/main/java/io/cryptolens/models/ProductAndKeyModel.java new file mode 100644 index 0000000..98db5e3 --- /dev/null +++ b/src/main/java/io/cryptolens/models/ProductAndKeyModel.java @@ -0,0 +1,13 @@ +package io.cryptolens.models; + +public abstract class ProductAndKeyModel { + /** + * The product id. + */ + public int ProductId; + + /** + * The license key string. + */ + public String Key; +} diff --git a/src/main/java/io/cryptolens/models/RemoveDataObjectToKeyModel.java b/src/main/java/io/cryptolens/models/RemoveDataObjectToKeyModel.java new file mode 100644 index 0000000..f51273b --- /dev/null +++ b/src/main/java/io/cryptolens/models/RemoveDataObjectToKeyModel.java @@ -0,0 +1,15 @@ +package io.cryptolens.models; + +public class RemoveDataObjectToKeyModel extends ProductAndKeyModel { + + /** + * The unique id of the data object to be removed. + */ + public long Id; + + public RemoveDataObjectToKeyModel(int productId, String key, long id) { + Id = id; + this.ProductId = productId; + this.Key = key; + } +} diff --git a/src/main/java/io/cryptolens/models/SetIntValueToKeyModel.java b/src/main/java/io/cryptolens/models/SetIntValueToKeyModel.java new file mode 100644 index 0000000..0c81715 --- /dev/null +++ b/src/main/java/io/cryptolens/models/SetIntValueToKeyModel.java @@ -0,0 +1,13 @@ +package io.cryptolens.models; + +public class SetIntValueToKeyModel extends ProductAndKeyModel { + public long Id; + public int IntValue; + + public SetIntValueToKeyModel(int productId, String key, long id, int intValue) { + Id = id; + IntValue = intValue; + this.ProductId = productId; + this.Key = key; + } +} diff --git a/src/main/java/io/cryptolens/models/SetStringValueToKeyModel.java b/src/main/java/io/cryptolens/models/SetStringValueToKeyModel.java new file mode 100644 index 0000000..7308eef --- /dev/null +++ b/src/main/java/io/cryptolens/models/SetStringValueToKeyModel.java @@ -0,0 +1,13 @@ +package io.cryptolens.models; + +public class SetStringValueToKeyModel extends ProductAndKeyModel { + public long Id; + public String StringValue; + + public SetStringValueToKeyModel(int productId, String key, long id, String stringValue) { + Id = id; + StringValue = stringValue; + this.ProductId = productId; + this.Key = key; + } +} diff --git a/src/test/java/io/cryptolens/DataTest.java b/src/test/java/io/cryptolens/DataTest.java new file mode 100644 index 0000000..f9f1d66 --- /dev/null +++ b/src/test/java/io/cryptolens/DataTest.java @@ -0,0 +1,113 @@ +package io.cryptolens; + +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import io.cryptolens.internal.BasicResult; +import io.cryptolens.methods.Data; +import io.cryptolens.methods.Helpers; +import io.cryptolens.methods.Key; +import io.cryptolens.models.ActivateModel; +import io.cryptolens.models.DataObject; +import io.cryptolens.models.LicenseKey; +import io.cryptolens.models.ListOfDataObjectsResult; +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; + +import java.io.File; +import java.lang.reflect.Type; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashMap; + + +/** + * Unit test for simple App. + */ +public class DataTest + extends TestCase +{ + + HashMap APIKey = null; + LicenseKey license = null; + + /** + * Create the test case + * + * @param testName name of the test case + */ + public DataTest(String testName ) + { + super( testName ); + } + + /** + * @return the suite of tests being tested + */ + public static Test suite() + { + return new TestSuite( DataTest.class ); + } + + public void init() throws Exception{ + String api_content = new String(Files.readAllBytes(Paths.get("apikeys.json")), "UTF-8"); + Type type = new TypeToken>(){}.getType(); + APIKey = new Gson().fromJson(api_content, type); + + license = new LicenseKey(); + license.ProductId = 3349; + license.Key = "GEBNC-WZZJD-VJIHG-GCMVD"; + assertTrue( true ); + + } + /** + * Rigourous Test :-) + */ + public void testApp() throws Exception { + + + } + + public void testAdd() throws Exception{ + + init(); + + BasicResult addResult = Data.AddDataObject(APIKey.get("data"), license, "test", 3, "test"); + + if(!Helpers.IsSuccessful(addResult)) { + fail("Could not add a new data object"); + } + + ListOfDataObjectsResult listResult = Data.ListDataObjects(APIKey.get("data"), license); + + if(!Helpers.IsSuccessful(listResult)) { + fail("Could not list data objects"); + } + + int count = listResult.DataObjects.size(); + + DataObject dObj = listResult.DataObjects.get(0); + + int currentVal = dObj.IntValue; + BasicResult incrementResult = Data.IncrementIntValue(APIKey.get("data"), license, dObj.Id, 1); + listResult = Data.ListDataObjects(APIKey.get("data"), license); + + if(!Helpers.IsSuccessful(incrementResult) || listResult.DataObjects.get(0).IntValue != currentVal + 1) { + fail("Could not increment the data object."); + } + + BasicResult decrementResult = Data.DecrementIntValue(APIKey.get("data"), license, dObj.Id, 1); + listResult = Data.ListDataObjects(APIKey.get("data"), license); + + if(!Helpers.IsSuccessful(decrementResult) || listResult.DataObjects.get(0).IntValue != currentVal) { + fail("Could not increment the data object."); + } + + BasicResult removeResult = Data.RemoveDataObject(APIKey.get("data"), license, dObj.Id); + if(!Helpers.IsSuccessful(removeResult)) { + fail("Could not increment the data object."); + } + + } +}