1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-02-08 04:20:09 +00:00

Added expiry time support to the juce_tracktion_marketplace module

This commit is contained in:
jules 2016-07-14 10:05:50 +01:00
parent 05835ecfa1
commit 49989308b7
4 changed files with 124 additions and 28 deletions

View file

@ -51,6 +51,22 @@ public:
const String& machineNumbers,
const RSAKey& privateKey);
/** Similar to the above key file generation method but with an expiry time.
You must supply a Time after which this key file should no longer be considered as active.
N.B. when an app is unlocked with an expiring key file, OnlineUnlockStatus::isUnlocked will
still return false. You must then check OnlineUnlockStatus::getExpiryTime to see if this
expiring key file is still in date and act accordingly.
@see OnlineUnlockStatus
*/
static String JUCE_CALLTYPE generateExpiringKeyFile (const String& appName,
const String& userEmail,
const String& userName,
const String& machineNumbers,
const Time expiryTime,
const RSAKey& privateKey);
//==============================================================================
/** This is a simple implementation of a key-generator that you could easily wrap in
a command-line main() function for use on your server.

View file

@ -207,12 +207,12 @@ void OnlineUnlockForm::resized()
passwordBox.setInputRestrictions (64);
passwordBox.setFont (font);
r.removeFromBottom (30);
r.removeFromBottom (20);
emailBox.setBounds (r.removeFromBottom (boxHeight));
emailBox.setInputRestrictions (512);
emailBox.setFont (font);
r.removeFromBottom (30);
r.removeFromBottom (20);
message.setBounds (r);

View file

@ -29,6 +29,42 @@
struct KeyFileUtils
{
static XmlElement createKeyFileContent (const String& appName,
const String& userEmail,
const String& userName,
const String& machineNumbers,
const String& machineNumbersAttributeName)
{
XmlElement xml ("key");
xml.setAttribute ("user", userName);
xml.setAttribute ("email", userEmail);
xml.setAttribute (machineNumbersAttributeName, machineNumbers);
xml.setAttribute ("app", appName);
xml.setAttribute ("date", String::toHexString (Time::getCurrentTime().toMilliseconds()));
return xml;
}
static String createKeyFileComment (const String& appName,
const String& userEmail,
const String& userName,
const String& machineNumbers)
{
String comment;
comment << "Keyfile for " << appName << newLine;
if (userName.isNotEmpty())
comment << "User: " << userName << newLine;
comment << "Email: " << userEmail << newLine
<< "Machine numbers: " << machineNumbers << newLine
<< "Created: " << Time::getCurrentTime().toString (true, true);
return comment;
}
//==============================================================================
static String encryptXML (const XmlElement& xml, RSAKey privateKey)
{
MemoryOutputStream text;
@ -93,10 +129,10 @@ struct KeyFileUtils
return decryptXML (keyFileText.fromLastOccurrenceOf ("#", false, false).trim(), rsaPublicKey);
}
static StringArray getMachineNumbers (XmlElement xml)
static StringArray getMachineNumbers (XmlElement xml, StringRef attributeName)
{
StringArray numbers;
numbers.addTokens (xml.getStringAttribute ("mach"), ",; ", StringRef());
numbers.addTokens (xml.getStringAttribute (attributeName), ",; ", StringRef());
numbers.trim();
numbers.removeEmptyStrings();
return numbers;
@ -110,6 +146,9 @@ struct KeyFileUtils
{
String licensee, email, appID;
StringArray machineNumbers;
bool keyFileExpires;
Time expiryTime;
};
static KeyFileData getDataFromKeyFile (XmlElement xml)
@ -119,7 +158,18 @@ struct KeyFileUtils
data.licensee = getLicensee (xml);
data.email = getEmail (xml);
data.appID = getAppID (xml);
data.machineNumbers.addArray (getMachineNumbers (xml));
if (xml.hasAttribute ("expiryTime") && xml.hasAttribute ("expiring_mach"))
{
data.keyFileExpires = true;
data.machineNumbers.addArray (getMachineNumbers (xml, "expiring_mach"));
data.expiryTime = Time (xml.getStringAttribute ("expiryTime").getHexValue64());
}
else
{
data.keyFileExpires = false;
data.machineNumbers.addArray (getMachineNumbers (xml, "mach"));
}
return data;
}
@ -127,6 +177,7 @@ struct KeyFileUtils
//==============================================================================
const char* OnlineUnlockStatus::unlockedProp = "u";
const char* OnlineUnlockStatus::expiryTimeProp = "t";
static const char* stateTagName = "REG";
static const char* userNameProp = "user";
static const char* keyfileDataProp = "key";
@ -183,11 +234,22 @@ void OnlineUnlockStatus::load()
KeyFileUtils::KeyFileData data;
data = KeyFileUtils::getDataFromKeyFile (KeyFileUtils::getXmlFromKeyFile (status[keyfileDataProp], getPublicKey()));
if (! doesProductIDMatch (data.appID))
status.removeProperty (unlockedProp, nullptr);
if (data.keyFileExpires)
{
if (! doesProductIDMatch (data.appID))
status.removeProperty (expiryTimeProp, nullptr);
if (! machineNumberAllowed (data.machineNumbers, localMachineNums))
status.removeProperty (unlockedProp, nullptr);
if (! machineNumberAllowed (data.machineNumbers, localMachineNums))
status.removeProperty (expiryTimeProp, nullptr);
}
else
{
if (! doesProductIDMatch (data.appID))
status.removeProperty (unlockedProp, nullptr);
if (! machineNumberAllowed (data.machineNumbers, localMachineNums))
status.removeProperty (unlockedProp, nullptr);
}
}
void OnlineUnlockStatus::save()
@ -289,7 +351,7 @@ bool OnlineUnlockStatus::applyKeyFile (String keyFileContent)
{
setUserEmail (data.email);
status.setProperty (keyfileDataProp, keyFileContent, nullptr);
status.removeProperty (unlockedProp, nullptr);
status.removeProperty (data.keyFileExpires ? expiryTimeProp : unlockedProp, nullptr);
var actualResult (0), dummyResult (1.0);
var v (machineNumberAllowed (data.machineNumbers, getLocalMachineIDs()));
@ -298,6 +360,14 @@ bool OnlineUnlockStatus::applyKeyFile (String keyFileContent)
dummyResult.swapWith (v);
jassert (! dummyResult);
if (data.keyFileExpires)
{
if ((! dummyResult) && actualResult)
status.setProperty (expiryTimeProp, data.expiryTime.toMilliseconds(), nullptr);
return getExpiryTime().toMilliseconds() > 0;
}
if ((! dummyResult) && actualResult)
status.setProperty (unlockedProp, actualResult, nullptr);
@ -395,23 +465,24 @@ String KeyGeneration::generateKeyFile (const String& appName,
const String& machineNumbers,
const RSAKey& privateKey)
{
XmlElement xml ("key");
xml.setAttribute ("user", userName);
xml.setAttribute ("email", userEmail);
xml.setAttribute ("mach", machineNumbers);
xml.setAttribute ("app", appName);
xml.setAttribute ("date", String::toHexString (Time::getCurrentTime().toMilliseconds()));
String comment;
comment << "Keyfile for " << appName << newLine;
if (userName.isNotEmpty())
comment << "User: " << userName << newLine;
comment << "Email: " << userEmail << newLine
<< "Machine numbers: " << machineNumbers << newLine
<< "Created: " << Time::getCurrentTime().toString (true, true);
XmlElement xml (KeyFileUtils::createKeyFileContent (appName, userEmail, userName, machineNumbers, "mach"));
const String comment (KeyFileUtils::createKeyFileComment (appName, userEmail, userName, machineNumbers));
return KeyFileUtils::createKeyFile (comment, xml, privateKey);
}
String KeyGeneration::generateExpiringKeyFile (const String& appName,
const String& userEmail,
const String& userName,
const String& machineNumbers,
const Time expiryTime,
const RSAKey& privateKey)
{
XmlElement xml (KeyFileUtils::createKeyFileContent (appName, userEmail, userName, machineNumbers, "expiring_mach"));
xml.setAttribute ("expiryTime", String::toHexString (expiryTime.toMilliseconds()));
String comment (KeyFileUtils::createKeyFileComment (appName, userEmail, userName, machineNumbers));
comment << "Expires: " << expiryTime.toString (true, true);
return KeyFileUtils::createKeyFile (comment, xml, privateKey);
}

View file

@ -126,7 +126,15 @@ public:
changed by a cracker in order to unlock your app, so the more places you call this
method, the more hassle it will be for them to find and crack them all.
*/
inline var isUnlocked() const { return status[unlockedProp]; }
inline var isUnlocked() const { return status[unlockedProp]; }
/** Returns the Time when the keyfile expires.
If a the key file obtained has an expiry time, isUnlocked will return false and this
will return a non-zero time. The interpretation of this is up to your app but could
be used for subscription based models or trial periods.
*/
inline Time getExpiryTime() const { return Time (static_cast<int64> (status[expiryTimeProp])); }
/** Optionally allows the app to provide the user's email address if
it is known.
@ -244,6 +252,7 @@ private:
UnlockResult handleFailedConnection();
static const char* unlockedProp;
static const char* expiryTimeProp;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OnlineUnlockStatus)
};