forked from ServiceStack/ServiceStack.Text
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathLicenseUtils.cs
More file actions
429 lines (367 loc) · 16.4 KB
/
Copy pathLicenseUtils.cs
File metadata and controls
429 lines (367 loc) · 16.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
// Copyright (c) Service Stack LLC. All Rights Reserved.
// License: https://raw.github.com/ServiceStack/ServiceStack/master/license.txt
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using ServiceStack.Text;
namespace ServiceStack
{
public class LicenseException : Exception
{
public LicenseException(string message) : base(message) { }
}
public enum LicenseType
{
Free,
Indie,
Business,
Enterprise,
TextIndie,
TextBusiness,
OrmLiteIndie,
OrmLiteBusiness,
RedisIndie,
RedisBusiness,
Trial,
}
[Flags]
public enum LicenseFeature : long
{
None = 0,
All = Premium | Text | Client | Common | Redis | OrmLite | ServiceStack | Server | Razor | Admin,
RedisSku = Redis | Text,
OrmLiteSku = OrmLite | Text,
Free = None,
Premium = 1 << 0,
Text = 1 << 1,
Client = 1 << 2,
Common = 1 << 3,
Redis = 1 << 4,
OrmLite = 1 << 5,
ServiceStack = 1 << 6,
Server = 1 << 7,
Razor = 1 << 8,
Admin = 1 << 9,
}
public enum QuotaType
{
Operations, //ServiceStack
Types, //Text, Redis
Fields, //ServiceStack, Text, Redis, OrmLite
RequestsPerHour, //Redis
Tables, //OrmLite
PremiumFeature, //AdminUI, Advanced Redis APIs, etc
}
/// <summary>
/// Public Code API to register commercial license for ServiceStack.
/// </summary>
public static class Licensing
{
public static void RegisterLicense(string licenseKeyText)
{
LicenseUtils.RegisterLicense(licenseKeyText);
}
public static void RegisterLicenseFromFile(string filePath)
{
if (!filePath.FileExists())
throw new LicenseException("License file does not exist: " + filePath).Trace();
var licenseKeyText = filePath.ReadAllText();
LicenseUtils.RegisterLicense(licenseKeyText);
}
public static void RegisterLicenseFromFileIfExists(string filePath)
{
if (!filePath.FileExists())
return;
var licenseKeyText = filePath.ReadAllText();
LicenseUtils.RegisterLicense(licenseKeyText);
}
}
public class LicenseKey
{
public string Ref { get; set; }
public string Name { get; set; }
public LicenseType Type { get; set; }
public string Hash { get; set; }
public DateTime Expiry { get; set; }
}
/// <summary>
/// Internal Utilities to verify licensing
/// </summary>
public static class LicenseUtils
{
public const string RuntimePublicKey = "<RSAKeyValue><Modulus>nkqwkUAcuIlVzzOPENcQ+g5ALCe4LyzzWv59E4a7LuOM1Nb+hlNlnx2oBinIkvh09EyaxIX2PmaY0KtyDRIh+PoItkKeJe/TydIbK/bLa0+0Axuwa0MFShE6HdJo/dynpODm64+Sg1XfhICyfsBBSxuJMiVKjlMDIxu9kDg7vEs=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>";
public const string LicensePublicKey = "<RSAKeyValue><Modulus>w2fTTfr2SrGCclwLUkrbH0XsIUpZDJ1Kei2YUwYGmIn5AUyCPLTUv3obDBUBFJKLQ61Khs7dDkXlzuJr5tkGQ0zS0PYsmBPAtszuTum+FAYRH4Wdhmlfqu1Z03gkCIo1i11TmamN5432uswwFCVH60JU3CpaN97Ehru39LA1X9E=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>";
private const string ContactDetails = " Please see servicestack.net or contact [email protected] for more details.";
static LicenseUtils()
{
PclExport.Instance.RegisterLicenseFromConfig();
}
private static bool hasInit;
public static void Init()
{
hasInit = true; //Dummy method to init static constructor
}
public static class ErrorMessages
{
private const string UpgradeInstructions = " Please see https://servicestack.net to upgrade to a commercial license or visit https://github.com/ServiceStackV3/ServiceStackV3 to revert back to the free ServiceStack v3.";
internal const string ExceededTextTypes = "The free-quota limit on '{0} ServiceStack.Text Types' has been reached." + UpgradeInstructions;
internal const string ExceededRedisTypes = "The free-quota limit on '{0} Redis Types' has been reached." + UpgradeInstructions;
internal const string ExceededRedisRequests = "The free-quota limit on '{0} Redis requests per hour' has been reached." + UpgradeInstructions;
internal const string ExceededOrmLiteTables = "The free-quota limit on '{0} OrmLite Tables' has been reached." + UpgradeInstructions;
internal const string ExceededServiceStackOperations = "The free-quota limit on '{0} ServiceStack Operations' has been reached." + UpgradeInstructions;
internal const string ExceededAdminUi = "The Admin UI is a commerical-only premium feature." + UpgradeInstructions;
internal const string ExceededPremiumFeature = "Unauthorized use of a commerical-only premium feature." + UpgradeInstructions;
public const string UnauthorizedAccessRequest = "Unauthorized access request of a licensed feature.";
}
public static class FreeQuotas
{
public const int ServiceStackOperations = 10;
public const int TypeFields = 20;
public const int TextTypes = 20;
public const int RedisTypes = 20;
public const int RedisRequestPerHour = 6000;
public const int OrmLiteTables = 10;
public const int PremiumFeature = 0;
}
public static void AssertEvaluationLicense()
{
if (DateTime.UtcNow > new DateTime(2013, 12, 31))
throw new LicenseException("The evaluation license for this software has expired. " +
"See https://servicestack.net to upgrade to a valid license.").Trace();
}
private static LicenseKey __activatedLicense;
public static void RegisterLicense(string licenseKeyText)
{
string cutomerId = null;
try
{
var parts = licenseKeyText.SplitOnFirst('-');
cutomerId = parts[0];
LicenseKey key;
using (new AccessToken(LicenseFeature.Text))
{
key = PclExport.Instance.VerifyLicenseKeyText(licenseKeyText);
}
var releaseDate = Env.GetReleaseDate();
if (releaseDate > key.Expiry)
throw new LicenseException("This license has expired on {0} and is not valid for use with this release."
.Fmt(key.Expiry.ToString("d")) + ContactDetails).Trace();
if (key.Type == LicenseType.Trial && DateTime.UtcNow > key.Expiry)
throw new LicenseException("This trial license has expired on {0}."
.Fmt(key.Expiry.ToString("d")) + ContactDetails).Trace();
__activatedLicense = key;
}
catch (Exception ex)
{
if (ex is LicenseException)
throw;
var msg = "This license is invalid." + ContactDetails;
if (!string.IsNullOrEmpty(cutomerId))
msg += " The id for this license is '{0}'".Fmt(cutomerId);
throw new LicenseException(msg).Trace();
}
}
public static void RemoveLicense()
{
__activatedLicense = null;
}
public static LicenseFeature ActivatedLicenseFeatures()
{
return __activatedLicense != null ? __activatedLicense.GetLicensedFeatures() : LicenseFeature.None;
}
public static void ApprovedUsage(LicenseFeature licenseFeature, LicenseFeature requestedFeature,
int allowedUsage, int actualUsage, string message)
{
var hasFeature = (requestedFeature & licenseFeature) == requestedFeature;
if (hasFeature)
return;
if (actualUsage > allowedUsage)
throw new LicenseException(message.Fmt(allowedUsage)).Trace();
}
public static bool HasLicensedFeature(LicenseFeature feature)
{
var licensedFeatures = ActivatedLicenseFeatures();
return (feature & licensedFeatures) == feature;
}
public static void AssertValidUsage(LicenseFeature feature, QuotaType quotaType, int count)
{
var licensedFeatures = ActivatedLicenseFeatures();
if ((LicenseFeature.All & licensedFeatures) == LicenseFeature.All) //Standard Usage
return;
if (AccessTokenScope != null)
{
if ((feature & AccessTokenScope.tempFeatures) == feature)
return;
}
//Free Quotas
switch (feature)
{
case LicenseFeature.Text:
switch (quotaType)
{
case QuotaType.Types:
ApprovedUsage(licensedFeatures, feature, FreeQuotas.TextTypes, count, ErrorMessages.ExceededTextTypes);
return;
}
break;
case LicenseFeature.Redis:
switch (quotaType)
{
case QuotaType.Types:
ApprovedUsage(licensedFeatures, feature, FreeQuotas.RedisTypes, count, ErrorMessages.ExceededRedisTypes);
return;
case QuotaType.RequestsPerHour:
ApprovedUsage(licensedFeatures, feature, FreeQuotas.RedisRequestPerHour, count, ErrorMessages.ExceededRedisRequests);
return;
}
break;
case LicenseFeature.OrmLite:
switch (quotaType)
{
case QuotaType.Tables:
ApprovedUsage(licensedFeatures, feature, FreeQuotas.OrmLiteTables, count, ErrorMessages.ExceededOrmLiteTables);
return;
}
break;
case LicenseFeature.ServiceStack:
switch (quotaType)
{
case QuotaType.Operations:
ApprovedUsage(licensedFeatures, feature, FreeQuotas.ServiceStackOperations, count, ErrorMessages.ExceededServiceStackOperations);
return;
}
break;
case LicenseFeature.Admin:
switch (quotaType)
{
case QuotaType.PremiumFeature:
ApprovedUsage(licensedFeatures, feature, FreeQuotas.PremiumFeature, count, ErrorMessages.ExceededAdminUi);
return;
}
break;
case LicenseFeature.Premium:
switch (quotaType)
{
case QuotaType.PremiumFeature:
ApprovedUsage(licensedFeatures, feature, FreeQuotas.PremiumFeature, count, ErrorMessages.ExceededPremiumFeature);
return;
}
break;
}
throw new LicenseException("Unknown Quota Usage: {0}, {1}".Fmt(feature, quotaType)).Trace();
}
public static LicenseFeature GetLicensedFeatures(this LicenseKey key)
{
switch (key.Type)
{
case LicenseType.Free:
return LicenseFeature.Free;
case LicenseType.Indie:
case LicenseType.Business:
case LicenseType.Enterprise:
case LicenseType.Trial:
return LicenseFeature.All;
case LicenseType.TextIndie:
case LicenseType.TextBusiness:
return LicenseFeature.Text;
case LicenseType.OrmLiteIndie:
case LicenseType.OrmLiteBusiness:
return LicenseFeature.OrmLiteSku;
case LicenseType.RedisIndie:
case LicenseType.RedisBusiness:
return LicenseFeature.RedisSku;
}
throw new ArgumentException("Unknown License Type: " + key.Type).Trace();
}
public static LicenseKey ToLicenseKey(this string licenseKeyText)
{
licenseKeyText = Regex.Replace(licenseKeyText, @"\s+", "");
var parts = licenseKeyText.SplitOnFirst('-');
var refId = parts[0];
var base64 = parts[1];
var jsv = Convert.FromBase64String(base64).FromUtf8Bytes();
var hold = JsConfig<DateTime>.DeSerializeFn;
var holdRaw = JsConfig<DateTime>.RawDeserializeFn;
try
{
JsConfig<DateTime>.DeSerializeFn = null;
JsConfig<DateTime>.RawDeserializeFn = null;
var key = jsv.FromJsv<LicenseKey>();
if (key.Ref != refId)
throw new LicenseException("The license '{0}' is not assigned to CustomerId '{1}'.".Fmt(base64)).Trace();
return key;
}
finally
{
JsConfig<DateTime>.DeSerializeFn = hold;
JsConfig<DateTime>.RawDeserializeFn = holdRaw;
}
}
public static string GetHashKeyToSign(this LicenseKey key)
{
return "{0}:{1}:{2}:{3}".Fmt(key.Ref, key.Name, key.Expiry.ToString("yyyy-MM-dd"), key.Type);
}
public static Exception GetInnerMostException(this Exception ex)
{
//Extract true exception from static intializers (e.g. LicenseException)
while (ex.InnerException != null)
{
ex = ex.InnerException;
}
return ex;
}
[ThreadStatic]
private static AccessToken AccessTokenScope;
private class AccessToken : IDisposable
{
private readonly AccessToken prevToken;
internal readonly LicenseFeature tempFeatures;
internal AccessToken(LicenseFeature requested)
{
prevToken = AccessTokenScope;
AccessTokenScope = this;
tempFeatures = requested;
}
public void Dispose()
{
AccessTokenScope = prevToken;
}
}
static class _approved
{
internal static readonly HashSet<string> __tokens = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
"ServiceStack.ServiceClientBase+AccessToken",
"ServiceStack.JsonHttpClient+AccessToken",
"ServiceStack.RabbitMq.RabbitMqProducer+AccessToken",
"ServiceStack.Messaging.RedisMessageQueueClient+AccessToken",
"ServiceStack.Messaging.RedisMessageProducer+AccessToken",
};
internal static readonly HashSet<string> __dlls = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
"ServiceStack.HttpClient.dll",
"ServiceStack.Client.dll",
"ServiceStack.RabbitMq.dll",
"<Unknown>"
};
}
public static IDisposable RequestAccess(object accessToken, LicenseFeature srcFeature, LicenseFeature requestedAccess)
{
var accessType = accessToken.GetType();
if (srcFeature != LicenseFeature.Client || requestedAccess != LicenseFeature.Text || accessToken == null)
throw new LicenseException(ErrorMessages.UnauthorizedAccessRequest).Trace();
if (accessType.Name == "AccessToken" && accessType.GetAssembly().ManifestModule.Name.StartsWith("<")) //Smart Assembly
return new AccessToken(requestedAccess);
if (!_approved.__tokens.Contains(accessType.FullName))
{
var errorDetails = " __token: '{0}', Assembly: '{1}'".Fmt(
accessType.Name,
accessType.GetAssembly().ManifestModule.Name);
throw new LicenseException(ErrorMessages.UnauthorizedAccessRequest + errorDetails).Trace();
}
PclExport.Instance.VerifyInAssembly(accessType, _approved.__dlls);
return new AccessToken(requestedAccess);
}
}
}