markus / Rhino.Licensing / LicensingService.cs @ 42d49521
이력 | 보기 | 이력해설 | 다운로드 (8.42 KB)
1 |
namespace Rhino.Licensing |
---|---|
2 |
{ |
3 |
using System; |
4 |
using System.Collections.Generic; |
5 |
using System.Diagnostics; |
6 |
using System.IO; |
7 |
using System.ServiceModel; |
8 |
using System.Linq; |
9 |
|
10 |
/// <summary> |
11 |
/// Licensing server implementation. |
12 |
/// Because we use this service behavior, we don't have to worry |
13 |
/// about multi threading issues. it is not something that we |
14 |
/// expect to have to deal with huge load, anyway. |
15 |
/// </summary> |
16 |
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Single)] |
17 |
public class LicensingService : ILicensingService |
18 |
{ |
19 |
private readonly List<LicenseValidator> availableLicenses = new List<LicenseValidator>(); |
20 |
private readonly Dictionary<string, KeyValuePair<DateTime, LicenseValidator>> leasedLicenses = new Dictionary<string, KeyValuePair<DateTime, LicenseValidator>>(); |
21 |
private readonly string state; |
22 |
|
23 |
/// <summary> |
24 |
/// Creates a new instance of <seealso cref="LicensingService"/>. |
25 |
/// </summary> |
26 |
public LicensingService() |
27 |
{ |
28 |
if (SoftwarePublicKey == null) |
29 |
throw new InvalidOperationException("SoftwarePublicKey must be set before starting the service"); |
30 |
|
31 |
if (LicenseServerPrivateKey == null) |
32 |
throw new InvalidOperationException("LicenseServerPrivateKey must be set before starting the service"); |
33 |
|
34 |
var licensesDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Licenses"); |
35 |
state = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "licenseServer.state"); |
36 |
|
37 |
EnsureLicenseDirectoryExists(licensesDirectory); |
38 |
|
39 |
ReadAvailableLicenses(licensesDirectory); |
40 |
|
41 |
ReadInitialState(); |
42 |
} |
43 |
|
44 |
/// <summary> |
45 |
/// Gets or Sets the public key of the product |
46 |
/// </summary> |
47 |
public static string SoftwarePublicKey |
48 |
{ |
49 |
get; set; |
50 |
} |
51 |
|
52 |
/// <summary> |
53 |
/// Gets or Sets the private key of the license server |
54 |
/// </summary> |
55 |
public static string LicenseServerPrivateKey |
56 |
{ |
57 |
get; set; |
58 |
} |
59 |
|
60 |
private static void EnsureLicenseDirectoryExists(string licensesDirectory) |
61 |
{ |
62 |
if (Directory.Exists(licensesDirectory) == false) |
63 |
{ |
64 |
try |
65 |
{ |
66 |
Directory.CreateDirectory(licensesDirectory); |
67 |
} |
68 |
catch (Exception e) |
69 |
{ |
70 |
throw new DirectoryNotFoundException("Could not find licenses directory: " + licensesDirectory, e); |
71 |
} |
72 |
} |
73 |
} |
74 |
|
75 |
private void ReadAvailableLicenses(string licensesDirectory) |
76 |
{ |
77 |
foreach (var license in Directory.GetFiles(licensesDirectory, "*.xml")) |
78 |
{ |
79 |
var set = new HashSet<Guid>(); |
80 |
var validator = new LicenseValidator(SoftwarePublicKey, license) |
81 |
{ |
82 |
DisableFloatingLicenses = true |
83 |
}; |
84 |
try |
85 |
{ |
86 |
validator.AssertValidLicense(); |
87 |
Debug.WriteLine("Found license for " + validator.Name + " of type: " + validator.LicenseType); |
88 |
if (validator.LicenseType == LicenseType.Standard && |
89 |
// this prevent a simple cheating of simply copying the same |
90 |
// license file several times |
91 |
set.Add(validator.UserId)) |
92 |
{ |
93 |
availableLicenses.Add(validator); |
94 |
Debug.WriteLine("Accepting license for: " + validator.Name + " " + validator.UserId); |
95 |
} |
96 |
} |
97 |
catch |
98 |
{ |
99 |
continue; |
100 |
} |
101 |
} |
102 |
} |
103 |
|
104 |
private void ReadInitialState() |
105 |
{ |
106 |
try |
107 |
{ |
108 |
using (var file = new FileStream(state, FileMode.OpenOrCreate, FileAccess.ReadWrite)) |
109 |
{ |
110 |
ReadState(file); |
111 |
} |
112 |
} |
113 |
catch (AccessViolationException e) |
114 |
{ |
115 |
throw new AccessViolationException("Could not open file '" + state + "' for read/write, please grant read/write access to the file.", e); |
116 |
} |
117 |
catch (Exception e) |
118 |
{ |
119 |
throw new InvalidOperationException("Could not understand file '" + state + "'.", e); |
120 |
} |
121 |
} |
122 |
|
123 |
private void ReadState(Stream stream) |
124 |
{ |
125 |
try |
126 |
{ |
127 |
using (var binaryReader = new BinaryReader(stream)) |
128 |
{ |
129 |
while (true) |
130 |
{ |
131 |
var identifier = binaryReader.ReadString(); |
132 |
var time = DateTime.FromBinary(binaryReader.ReadInt64()); |
133 |
var userId = new Guid(binaryReader.ReadBytes(16)); |
134 |
|
135 |
var licenseValidator = availableLicenses.FirstOrDefault(x => x.UserId == userId); |
136 |
if (licenseValidator == null) |
137 |
continue; |
138 |
|
139 |
leasedLicenses[identifier] = new KeyValuePair<DateTime, LicenseValidator>(time, licenseValidator); |
140 |
availableLicenses.Remove(licenseValidator); |
141 |
} |
142 |
} |
143 |
} |
144 |
catch (EndOfStreamException) |
145 |
{ |
146 |
} |
147 |
} |
148 |
|
149 |
private void WriteState(Stream stream) |
150 |
{ |
151 |
using (var binaryWriter = new BinaryWriter(stream)) |
152 |
{ |
153 |
foreach (var pair in leasedLicenses) |
154 |
{ |
155 |
binaryWriter.Write(pair.Key); |
156 |
binaryWriter.Write(pair.Value.Key.ToBinary()); |
157 |
binaryWriter.Write(pair.Value.Value.UserId.ToByteArray()); |
158 |
} |
159 |
binaryWriter.Flush(); |
160 |
stream.Flush(); |
161 |
} |
162 |
} |
163 |
|
164 |
|
165 |
/// <summary> |
166 |
/// Leases a new license to the client machine. |
167 |
/// </summary> |
168 |
/// <param name="machine">client machine name</param> |
169 |
/// <param name="user">user name</param> |
170 |
/// <param name="id">Id of the license holder</param> |
171 |
/// <returns></returns> |
172 |
public string LeaseLicense( |
173 |
string machine, |
174 |
string user, |
175 |
Guid id) |
176 |
{ |
177 |
KeyValuePair<DateTime, LicenseValidator> value; |
178 |
var identifier = machine + @"\" + user + ": " + id; |
179 |
if (leasedLicenses.TryGetValue(identifier, out value)) |
180 |
{ |
181 |
Debug.WriteLine(id + " is already leased, so extending lease"); |
182 |
var licenseValidator = value.Value; |
183 |
return GenerateLicenseAndRenewLease(identifier, id, licenseValidator, value.Value.LicenseAttributes); |
184 |
} |
185 |
if (availableLicenses.Count > 0) |
186 |
{ |
187 |
var availableLicense = availableLicenses[availableLicenses.Count - 1]; |
188 |
availableLicenses.RemoveAt(availableLicenses.Count - 1); |
189 |
Debug.WriteLine("Found available license to give, leasing it"); |
190 |
return GenerateLicenseAndRenewLease(identifier, id, availableLicense, availableLicense.LicenseAttributes); |
191 |
} |
192 |
foreach (var kvp in leasedLicenses) |
193 |
{ |
194 |
if ((DateTime.UtcNow - kvp.Value.Key).TotalMinutes < 45) |
195 |
continue; |
196 |
leasedLicenses.Remove(kvp.Key); |
197 |
Debug.WriteLine("Found expired leased license, leasing it"); |
198 |
return GenerateLicenseAndRenewLease(identifier, id, kvp.Value.Value, kvp.Value.Value.LicenseAttributes); |
199 |
} |
200 |
Debug.WriteLine("Could not find license to lease"); |
201 |
return null; |
202 |
} |
203 |
|
204 |
private string GenerateLicenseAndRenewLease(string identifier, Guid id, LicenseValidator licenseValidator, IDictionary<string, string> attributes) |
205 |
{ |
206 |
leasedLicenses[identifier] = new KeyValuePair<DateTime, LicenseValidator>(DateTime.UtcNow.AddMinutes(30), licenseValidator); |
207 |
using (var file = new FileStream(state, FileMode.Create, FileAccess.ReadWrite)) |
208 |
{ |
209 |
WriteState(file); |
210 |
} |
211 |
return GenerateLicense(id, licenseValidator, attributes); |
212 |
} |
213 |
|
214 |
private static string GenerateLicense(Guid id, LicenseValidator validator, IDictionary<string, string> attributes) |
215 |
{ |
216 |
var generator = new LicenseGenerator(LicenseServerPrivateKey); |
217 |
return generator.Generate(validator.Name, id, DateTime.UtcNow.AddMinutes(45), attributes ,LicenseType.Floating); |
218 |
} |
219 |
|
220 |
/// <summary> |
221 |
/// |
222 |
/// </summary> |
223 |
/// <param name="key"></param> |
224 |
/// <returns></returns> |
225 |
public string GetLicense(string key) |
226 |
{ |
227 |
throw new NotImplementedException(); |
228 |
} |
229 |
} |
230 |
} |