Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
Transfer.cs
1using System;
3using System.Net;
5using System.Text;
6using System.Threading.Tasks;
7using System.Xml;
8using Waher.Content;
10using Waher.Events;
19using Waher.Script;
22using Waher.Security;
29
31{
36 {
40 public Transfer()
41 : base("Account/Transfer",
42 new KeyValuePair<Type, Expression>(typeof(Dictionary<string, object>), new Expression(jsonPattern)),
43 new KeyValuePair<Type, Expression>(typeof(XmlDocument), new Expression(xmlPattern)))
44 {
45 }
46
47 private static readonly string jsonPattern = Resources.LoadResourceAsText(typeof(Transfer).Namespace + ".JSON.Transfer.req");
48 private static readonly string xmlPattern = Resources.LoadResourceAsText(typeof(Transfer).Namespace + ".XML.Transfer.req");
49
58 public override async Task POST(HttpRequest Request, HttpResponse Response, Dictionary<string, IElement> Parameters)
59 {
61
62 string Token = JwtAuthentication.GetAccessToken(Request);
63 string KeyId = (string)Parameters["PKeyId"]?.AssociatedObjectValue;
64 string Nonce = (string)Parameters["PNonce"].AssociatedObjectValue;
65 string KeySignature = (string)Parameters["PKeySignature"]?.AssociatedObjectValue;
66 string RequestSignature = (string)Parameters["PRequestSignature"].AssociatedObjectValue;
67 string Pin = (string)Parameters["PPin"]?.AssociatedObjectValue;
68 string BareJid = User.UserName + "@" + Gateway.Domain;
69 StringBuilder sb = new StringBuilder();
71
72 if (string.IsNullOrEmpty(Nonce) || Nonce.Length < 32)
73 throw new ForbiddenException(Request, "Nonce too short.");
74
75 if (string.IsNullOrEmpty(KeyId))
76 {
77 if (!string.IsNullOrEmpty(KeySignature))
78 throw new BadRequestException("No Key ID provided");
79
80 AgentKey = null;
81 }
82 else
83 {
84 AgentKey = await Database.FindFirstDeleteRest<AgentKey>(new FilterAnd(
85 new FilterFieldEqualTo("Account", User.UserName),
86 new FilterFieldEqualTo("Id", KeyId)))
87 ?? throw new NotFoundException("Key not found.");
88 }
89
90 sb.Append(User.UserName);
91 sb.Append(':');
92 sb.Append(Request.Header.Host.Value);
93 sb.Append(':');
94 sb.Append(AgentKey?.LocalName ?? string.Empty);
95 sb.Append(':');
96 sb.Append(AgentKey?.Namespace ?? string.Empty);
97 sb.Append(':');
98 sb.Append(KeyId);
99
100 //string s1 = sb.ToString();
101
102 sb.Append(':');
103 sb.Append(KeySignature);
104
105 string s2 = sb.ToString();
106
107 sb.Append(':');
108 sb.Append(Nonce);
109 sb.Append(':');
110 sb.Append(Pin);
111
112 string s3 = sb.ToString();
113
114 string s = Convert.ToBase64String(
116 Encoding.UTF8.GetBytes(User.Account.Password),
117 Encoding.UTF8.GetBytes(s3)));
118
119 if (s != RequestSignature)
120 {
121 string Msg = "Request Signature invalid.";
122 throw new ForbiddenException(Request, Msg);
123 }
124
125 if (await Gateway.HasNonceBeenUsed(Nonce))
126 {
127 string Msg = "Nonce value has already been used.";
128 throw new ForbiddenException(Request, Msg);
129 }
130
131 await Gateway.RegisterNonceValue(Nonce);
132
133 DateTime TP;
134
135 if (!(Gateway.LoginAuditor is null))
136 {
137 DateTime? Next = await Gateway.LoginAuditor.GetEarliestLoginOpportunity(BareJid, "XMPP");
138
139 if (Next.HasValue)
140 {
141 sb.Clear();
142
143 TP = Next.Value;
144 DateTime Today = DateTime.Today;
145
146 if (Next.Value == DateTime.MaxValue)
147 {
148 sb.Append("This endpoint (");
149 sb.Append(BareJid);
150 sb.Append(") has been blocked from the system.");
151 }
152 else
153 {
154 sb.Append("Too many failed identity applications in a row registered. Try again after ");
155 sb.Append(TP.ToLongTimeString());
156
157 if (TP.Date != Today)
158 {
159 if (TP.Date == Today.AddDays(1))
160 sb.Append(" tomorrow");
161 else
162 {
163 sb.Append(", ");
164 sb.Append(TP.ToShortDateString());
165 }
166 }
167
168 sb.Append(". Remote Endpoint: ");
169 sb.Append(BareJid);
170 }
171
172 throw new TooManyRequestsException(sb.ToString());
173 }
174 }
175
176 sb.Clear();
177 sb.Append("<Transfer xmlns=\"");
178 sb.Append(Networking.XMPP.Contracts.ContractsClient.NamespaceOnboarding);
179 sb.Append("\">");
180
181 if (!(AgentKey is null))
182 {
183 EllipticCurveEndpoint KeyEndpoint = ApplyId.GetEndpoint(Request, AgentKey, s2);
184 if (!KeyEndpoint.HasPrivateKey)
185 throw new BadRequestException("Key does not have a private key.");
186
187 // TODO: Add Support for PQC.
188
189 s = KeyEndpoint.Curve.Export();
190 XmlDocument Doc = XML.ParseXml(s, true);
191
192 sb.Append("<LegalId>");
193
194 sb.Append("<S n=\"");
195 sb.Append(XML.Encode(KeyEndpoint.LocalName));
196 sb.Append("\" v=\"");
197 sb.Append(XML.Encode(Doc.DocumentElement.GetAttribute("d")));
198 sb.Append("\"/>");
199
200 sb.Append("<DT n=\"Timestamp\" v=\"");
201 sb.Append(XML.Encode(AgentKey.Updated));
202 sb.Append("\"/>");
203
204 foreach (LegalIdentity Identity in await Database.Find<LegalIdentity>(
205 new FilterAnd(
206 new FilterFieldEqualTo("Account", User.UserName),
207 new FilterFieldEqualTo("State", IoTBroker.Legal.Identity.IdentityState.Approved))))
208 {
209 if (Identity.HasClientPublicKey &&
210 Convert.ToBase64String(Identity.ClientPubKey) == KeyEndpoint.PublicKeyBase64)
211 {
212 sb.Append("<State legalId=\"");
213 sb.Append(XML.Encode(Identity.Id));
214 sb.Append("\" publicKey=\"");
215 sb.Append(KeyEndpoint.PublicKeyBase64);
216 sb.Append("\" timestamp=\"");
217 sb.Append(XML.Encode(AgentKey.Created));
218 sb.Append("\"/>");
219 }
220 }
221
222 sb.Append("</LegalId>");
223 }
224
225 if (!string.IsNullOrEmpty(Pin))
226 {
227 sb.Append("<Pin pin=\"");
228 sb.Append(XML.Encode(Pin));
229 sb.Append("\"/>");
230 }
231
232 User.Account.AppendAccountXml(sb, false);
233
234 sb.Append("</Transfer>");
235
236 string TransferXml = sb.ToString();
237 byte[] Bin = Encoding.UTF8.GetBytes(TransferXml);
238 byte[] Key = Gateway.NextBytes(16);
239 byte[] IV = Gateway.NextBytes(16);
240 byte[] Encrypted = Aes256Encrypt.Encrypt(Bin, Key, IV, CipherMode.CBC, PaddingMode.PKCS7);
241
242 sb.Clear();
243
244 sb.Append("<Info xmlns='");
245 sb.Append(Networking.XMPP.Contracts.ContractsClient.NamespaceOnboarding);
246 sb.Append("' base64='");
247 sb.Append(Convert.ToBase64String(Encrypted));
248 sb.Append("' once='true' expires='");
249 sb.Append(XML.Encode(DateTime.UtcNow.AddHours(1)));
250 sb.Append("'/>");
251
252 string OnboardingNeuron = await LegalComponent.GetOnboardingNeuronDomainName();
253
255 if (Client.Domain == "example.com" &&
256 Types.TryGetModuleParameter("XMPP_TEST", out XmppClient TestClient))
257 {
258 Client = TestClient;
259 }
260 XmlElement E = await Client.IqSetAsync("onboarding." + OnboardingNeuron, sb.ToString());
261
262 foreach (XmlNode N in E.ChildNodes)
263 {
264 if (N is XmlElement E2 && E2.LocalName == "Code" && E2.HasAttribute("code"))
265 {
266 string Code = E2.GetAttribute("code");
267
268 sb.Clear();
269
270 sb.Append("obinfo:");
271 sb.Append(OnboardingNeuron);
272 sb.Append(':');
273 sb.Append(Code);
274 sb.Append(':');
275 sb.Append(Convert.ToBase64String(Key));
276 sb.Append(':');
277 sb.Append(Convert.ToBase64String(IV));
278
279 string OnboardingUri = sb.ToString();
280
281 sb.Clear();
282
283 sb.Append("/QR/");
284 sb.Append(WebUtility.UrlEncode(OnboardingUri));
285 sb.Append("?w=400&h=400&q=2");
286
288 new FilterFieldLesserOrEqualTo("Created", DateTime.UtcNow.AddDays(-2)));
289
290 await Database.Insert(new TransferCode()
291 {
292 Code = Code,
293 Created = DateTime.UtcNow,
294 Account = User.UserName,
295 Token = Token
296 });
297
298 await Response.Return(new NamedDictionary<string, object>("TransferCode", AgentNamespace)
299 {
300 { "onboardingUri", OnboardingUri },
301 { "qrCodeUrl", Gateway.GetUrl(sb.ToString()) },
302 { "qrCodeWidth", 400 },
303 { "qrCodeHeight", 400 }
304 });
305
306 return;
307 }
308 }
309
310 throw new ServiceUnavailableException("Unable to generate onboarding code. Please try again later.");
311 }
312
317 public static async Task TransferCodeDelivered(string Code)
318 {
319 TransferCode CodeObj = await Database.FindFirstIgnoreRest<TransferCode>(new FilterFieldEqualTo("Code", Code));
320 if (CodeObj is null)
321 return;
322
323 if (!string.IsNullOrEmpty(CodeObj.Token))
324 {
325 try
326 {
327 if (JwtToken.TryParse(CodeObj.Token, out JwtToken ParsedToken, out string Reason))
328 JwtFactory.Deprecate(ParsedToken);
329 else
331 }
332 catch (Exception ex)
333 {
334 Log.Exception(ex);
335 }
336 }
337
338 int KeyCount = await Database.Delete<AgentKey>(
339 new FilterFieldEqualTo("Account", CodeObj.Account));
340
341 Log.Informational("Agent API Account transferred.",
342 new KeyValuePair<string, object>("Account", CodeObj.Account),
343 new KeyValuePair<string, object>("Keys Deleted", KeyCount));
344 }
345 }
346}
A Named dictionary is a dictionary, with a local name and a namespace. Use it to return content that ...
Helps with common XML-related tasks.
Definition: XML.cs:21
static string Encode(string s)
Encodes a string for use in XML.
Definition: XML.cs:29
static XmlDocument ParseXml(string Xml)
Parses an XML Document from its string representation.
Definition: XML.cs:713
Static class managing the application event log. Applications and services log events on this static ...
Definition: Log.cs:14
static void Exception(Exception Exception, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, params KeyValuePair< string, object >[] Tags)
Logs an exception. Event type will be determined by the severity of the exception.
Definition: Log.cs:1657
static void Error(string Message, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, string StackTrace, params KeyValuePair< string, object >[] Tags)
Logs an error event.
Definition: Log.cs:692
static void Informational(string Message, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, string StackTrace, params KeyValuePair< string, object >[] Tags)
Logs an informational event.
Definition: Log.cs:344
Static class managing the runtime environment of the IoT Gateway.
Definition: Gateway.cs:147
static CaseInsensitiveString Domain
Domain name.
Definition: Gateway.cs:3087
static Task< bool > HasNonceBeenUsed(string Nonce)
Checks if a Nonce value has been used.
Definition: Gateway.cs:6342
static LoginAuditor LoginAuditor
Current Login Auditor. Should be used by modules accepting user logins, to protect the system from un...
Definition: Gateway.cs:3860
static byte[] NextBytes(int NrBytes)
Generates an array of random bytes.
Definition: Gateway.cs:4335
static string GetUrl(string LocalResource)
Gets a URL for a resource.
Definition: Gateway.cs:5079
static Task RegisterNonceValue(string Nonce)
Registers a nonce value.
Definition: Gateway.cs:6351
static XmppClient XmppClient
XMPP Client connection of gateway.
Definition: Gateway.cs:4038
The request could not be understood by the server due to malformed syntax. The client SHOULD NOT repe...
The server understood the request, but is refusing to fulfill it. Authorization will not help and the...
HttpFieldHost Host
Host HTTP Field header. (RFC 2616, §14.23)
Represents an HTTP request.
Definition: HttpRequest.cs:22
HttpRequestHeader Header
Request header.
Definition: HttpRequest.cs:182
Represets a response of an HTTP client request.
Definition: HttpResponse.cs:23
The server has not found anything matching the Request-URI. No indication is given of whether the con...
The server is currently unable to handle the request due to a temporary overloading or maintenance of...
The user has sent too many requests in a given amount of time. Intended for use with rate limiting sc...
abstract string LocalName
Local name of the E2E encryption scheme
Definition: E2eEndpoint.cs:55
Abstract base class for Elliptic Curve endpoints.
bool HasPrivateKey
If the key contains a private key.
Manages an XMPP client connection. Implements XMPP, as defined in https://tools.ietf....
Definition: XmppClient.cs:58
async Task< XmlElement > IqSetAsync(string To, string Xml)
Performs an asynchronous IQ Set request/response operation.
Definition: XmppClient.cs:4101
string Domain
Current Domain.
Definition: XmppClient.cs:3492
Static interface for database persistence. In order to work, a database provider has to be assigned t...
Definition: Database.cs:21
static async Task Delete(object Object)
Deletes an object in the database.
Definition: Database.cs:1291
static Task< IEnumerable< object > > Find(string Collection, params string[] SortOrder)
Finds objects in a given collection.
Definition: Database.cs:238
static async Task Insert(object Object)
Inserts an object into the default collection of the database.
Definition: Database.cs:97
This filter selects objects that conform to all child-filters provided.
Definition: FilterAnd.cs:10
This filter selects objects that have a named field equal to a given value.
This filter selects objects that have a named field lesser or equal to a given value.
Static class managing loading of resources stored as embedded resources or in content files.
Definition: Resources.cs:13
static string LoadResourceAsText(string ResourceName)
Loads a text resource from an embedded resource.
Definition: Resources.cs:55
Static class that dynamically manages types and interfaces available in the runtime environment.
Definition: Types.cs:15
static bool TryGetModuleParameter(string Name, out object Value)
Tries to get a module parameter value.
Definition: Types.cs:607
static byte[] Encrypt(byte[] Data, byte[] Key, byte[] IV, CipherMode CipherMode=CipherMode.CBC, PaddingMode PaddingMode=PaddingMode.PKCS7)
Performs AES encryption of data.
Class managing a script expression.
Definition: Expression.cs:41
Contains methods for simple hash calculations.
Definition: Hashes.cs:57
static byte[] ComputeHMACSHA256Hash(byte[] Key, byte[] Data)
Computes the HMAC-SHA-256 hash of a block of binary data.
Definition: Hashes.cs:735
Use JWT tokens for authentication. The Bearer scheme defined in RFC 6750 is used: https://tools....
static string GetAccessToken(HttpRequest Request)
Gets the access token from an HTTP request.
A factory that can create and validate JWT tokens.
Definition: JwtFactory.cs:66
static void Deprecate(JwtToken Token)
Deprecates a token.
Definition: JwtFactory.cs:467
Contains information about a Java Web Token (JWT). JWT is defined in RFC 7519: https://tools....
Definition: JwtToken.cs:22
static bool TryParse(string Token, out JwtToken ParsedToken)
Tries to parse a JWT token.
Definition: JwtToken.cs:68
Contains information about a broker account.
Definition: Account.cs:41
CaseInsensitiveString UserName
User Name of account
Definition: Account.cs:141
string Password
Password of account
Definition: Account.cs:151
Contains information about an Agent Transfer code.
Definition: TransferCode.cs:13
string Account
Account the code is associated with.
Definition: TransferCode.cs:40
static async Task TransferCodeDelivered(string Code)
Called when a transfer code has been delivered to a recipient.
Definition: Transfer.cs:317
override async Task POST(HttpRequest Request, HttpResponse Response, Dictionary< string, IElement > Parameters)
Executes the POST method on the resource.
Definition: Transfer.cs:58
Abstract base class for agent resources supporting the POST method.
static AccountUser AssertUserAuthenticated(HttpRequest Request)
Makes sure the request is made by an authenticated API user.
const string AgentNamespace
https://waher.se/Schema/BrokerAgent.xsd
Contains an encrypted key for an agent.
Definition: AgentKey.cs:13
DateTime Created
When key was first created.
Definition: AgentKey.cs:65
DateTime Updated
When key was last updated.
Definition: AgentKey.cs:70
Reason
Reason a token is not valid.
Definition: JwtFactory.cs:15