Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
AddIdAttachment.cs
1using System;
2using System.Collections.Generic;
3using System.Text;
4using System.Threading.Tasks;
5using System.Xml;
6using Waher.Content;
8using Waher.Events;
17using Waher.Script;
19using Waher.Security;
27
29{
34 {
39 : base("Legal/AddIdAttachment",
40 new KeyValuePair<Type, Expression>(typeof(Dictionary<string, object>), new Expression(jsonPattern)),
41 new KeyValuePair<Type, Expression>(typeof(XmlDocument), new Expression(xmlPattern)))
42 {
43 }
44
45 private static readonly string jsonPattern = Resources.LoadResourceAsText(typeof(AddIdAttachment).Namespace + ".JSON.AddIdAttachment.req");
46 private static readonly string xmlPattern = Resources.LoadResourceAsText(typeof(AddIdAttachment).Namespace + ".XML.AddIdAttachment.req");
47
56 public override async Task POST(HttpRequest Request, HttpResponse Response, Dictionary<string, IElement> Parameters)
57 {
59
60 string KeyId = (string)Parameters["PKeyId"].AssociatedObjectValue;
61 string Nonce = (string)Parameters["PNonce"].AssociatedObjectValue;
62 string KeySignature = (string)Parameters["PKeySignature"].AssociatedObjectValue;
63 string RequestSignature = (string)Parameters["PRequestSignature"].AssociatedObjectValue;
64 CaseInsensitiveString LegalId = (string)Parameters["PLegalId"].AssociatedObjectValue;
65 string AttachmentBase64 = (string)Parameters["PAttachmentBase64"].AssociatedObjectValue;
66 string AttachmentFileName = (string)Parameters["PAttachmentFileName"].AssociatedObjectValue;
67 string AttachmentContentType = (string)Parameters["PAttachmentContentType"].AssociatedObjectValue;
68 StringBuilder sb = new StringBuilder();
69 byte[] Attachment;
70 object DecodedAttachment;
71
72 if (string.IsNullOrEmpty(KeyId))
73 throw new BadRequestException("Key ID cannot be empty.");
74
75 if (string.IsNullOrEmpty(Nonce) || Nonce.Length < 32)
76 throw new ForbiddenException("Nonce too short.");
77
78 using (Semaphore Semaphore = await Semaphores.BeginWrite("iotid:" + LegalId.LowerCase))
79 {
80 LegalIdentity Identity = await LegalComponent.GetLocalLegalIdentity(LegalId)
81 ?? throw new NotFoundException("Legal identity not found.");
82
83 if (Identity.Account != User.UserName)
84 throw new ForbiddenException("Only allowed to add attachments to your own legal identities.");
85
86 if (Identity.State != IdentityState.Created)
87 throw new ForbiddenException("Attachments can only be added to newly created identities before they are approved.");
88
89 IAccount Account = await XmppServerModule.GetAccountAsync(Identity.Account)
90 ?? throw new ForbiddenException("Account has been removed.");
91
92 if (!Account.Enabled)
93 throw new ForbiddenException("Account has been disabled.");
94
95 AgentKey AgentKey = await Database.FindFirstDeleteRest<AgentKey>(new FilterAnd(
96 new FilterFieldEqualTo("Account", User.UserName),
97 new FilterFieldEqualTo("Id", KeyId)))
98 ?? throw new NotFoundException("Key not found.");
99
100 sb.Append(User.UserName);
101 sb.Append(':');
102 sb.Append(Request.Header.Host.Value);
103 sb.Append(':');
104 sb.Append(AgentKey.LocalName);
105 sb.Append(':');
106 sb.Append(AgentKey.Namespace);
107 sb.Append(':');
108 sb.Append(KeyId);
109
110 //string s1 = sb.ToString();
111
112 sb.Append(':');
113 sb.Append(KeySignature);
114
115 string s2 = sb.ToString();
116
117 sb.Append(':');
118 sb.Append(Nonce);
119 sb.Append(':');
120 sb.Append(AttachmentBase64);
121 sb.Append(':');
122 sb.Append(AttachmentFileName);
123 sb.Append(':');
124 sb.Append(AttachmentContentType);
125
126 string s3 = sb.ToString();
127
128 string s = Convert.ToBase64String(
130 Encoding.UTF8.GetBytes(User.Account.Password),
131 Encoding.UTF8.GetBytes(s3)));
132
133 if (s != RequestSignature)
134 {
135 string Msg = "Request Signature invalid.";
136 throw new ForbiddenException(Msg);
137 }
138
139 if (await this.Api.HasNonceBeenUsed(Nonce))
140 {
141 string Msg = "Nonce value has already been used.";
142 throw new ForbiddenException(Msg);
143 }
144
145 await this.Api.RegisterNonceValue(Nonce);
146
147 try
148 {
149 Attachment = Convert.FromBase64String(AttachmentBase64);
150 DecodedAttachment = await InternetContent.DecodeAsync(AttachmentContentType, Attachment, null);
151 }
152 catch (Exception)
153 {
154 throw new BadRequestException("Invalid attachment.");
155 }
156
157 try
158 {
159 EllipticCurveEndpoint KeyEndpoint = ApplyId.GetEndpoint(AgentKey, s2);
160 byte[] AttachmentSignature = KeyEndpoint.Sign(Attachment);
161
162 if (!(Identity.Attachments is null))
163 {
164 string AttachmentSignatureBase64 = Convert.ToBase64String(AttachmentSignature);
165
166 foreach (AttachmentReference Ref in Identity.Attachments)
167 {
168 if (Convert.ToBase64String(Ref.Signature) == s)
169 throw new BadRequestException("Attachment already assigned to identity.");
170 }
171 }
172
173 bool IncNrPeerReviews = false;
174 List<LegalIdentity> Reviewers = null;
175 string LegalNamespace = NamespaceLegalIdentity(Identity.Version);
176
177 if (DecodedAttachment is XmlDocument Doc)
178 {
179 if (Doc.DocumentElement.LocalName == "peerReview" && Doc.DocumentElement.NamespaceURI == LegalNamespace)
180 {
181 LegalIdentity ReviewedIdentity = null;
182 LegalIdentity ReviewerIdentity = null;
185 bool ReviewedHasStatus = false;
186 bool ReviewerHasStatus = false;
187 byte[] PeerSignature = Convert.FromBase64String(XML.Attribute(Doc.DocumentElement, "s"));
188 DateTime TP = XML.Attribute(Doc.DocumentElement, "tp", DateTime.Now);
189 byte[] SignedIdentity = null;
190 DateTime Now = DateTime.Now;
191
192 foreach (XmlNode N in Doc.DocumentElement.ChildNodes)
193 {
194 if (!(N is XmlElement E) || N.NamespaceURI != LegalNamespace)
195 continue;
196
197 foreach (XmlNode N2 in E.ChildNodes)
198 {
199 if (N2 is XmlElement E2 &&
200 E2.LocalName == "identity" &&
201 E2.NamespaceURI == LegalNamespace)
202 {
203 switch (E.LocalName)
204 {
205 case "reviewed":
206 ReviewedIdentity = LegalIdentity.Parse(E2, out ReviewedHasStatus,
207 out Dictionary<string, string> ReviewedIdentityAttachmentsUrls);
208
209 sb.Clear();
210 ReviewedIdentity.Serialize(sb, true, true, true, false, false, false, false, null, XmppServerModule.Legal);
211 string s1 = sb.ToString();
212
213 sb.Clear();
214 Identity.Serialize(sb, true, true, true, false, false, false, false, null, XmppServerModule.Legal);
215 s2 = sb.ToString();
216
217 if (s1 != s2)
218 throw new ForbiddenException("Reviewed identity mismatch.");
219
220 sb.Clear();
221 ReviewedIdentity.Serialize(sb, true, true, true, true, true, true, true, ReviewedIdentityAttachmentsUrls, XmppServerModule.Legal);
222 SignedIdentity = Encoding.UTF8.GetBytes(sb.ToString());
223 break;
224
225 case "reviewer":
226 ReviewerIdentity = LegalIdentity.Parse(E2, out ReviewerHasStatus,
227 out Dictionary<string, string> ReviewerIdentityAttachmentsUrls);
228
229 sb.Clear();
230 ReviewerIdentity.Serialize(sb, false, false, false, false, false, false, false, null, XmppServerModule.Legal);
231 byte[] Data = Encoding.UTF8.GetBytes(sb.ToString());
232 ReviewerJid = ReviewerIdentity["JID"];
233
234 if (!CaseInsensitiveString.IsNullOrEmpty(ReviewerJid))
235 {
236 (ReviewerIdentity, _) = await XmppServerModule.Legal.ValidateSignature(new XmppAddress(ReviewerJid), TP, Data, ReviewerIdentity.ClientSignature);
237 if (ReviewerIdentity is null || ReviewerJid != ReviewerIdentity["JID"])
238 throw new ForbiddenException("Peer identity signature invalid.");
239 }
240
241 if (ReviewerIdentity.State != IdentityState.Approved)
242 throw new ForbiddenException("Reviewer identity not approved.");
243
244 if (Now.Date.AddDays(1) < ReviewerIdentity.From ||
245 Now.Date.AddDays(-1) > ReviewerIdentity.To)
246 {
247 throw new ForbiddenException("Reviewer identity not valid.");
248 }
249
250 if (CaseInsensitiveString.IsNullOrEmpty(ReviewerIdentity.Provider))
251 throw new ForbiddenException("Reviewer identity lacks a provider.");
252
253 if (string.IsNullOrEmpty(ReviewerIdentity.ClientKeyName) ||
254 ReviewerIdentity.ClientPubKey is null ||
255 ReviewerIdentity.ClientPubKey.Length == 0)
256 {
257 throw new ForbiddenException("Reviewer identity lacks a key.");
258 }
259
260 if (ReviewerIdentity.ClientSignature is null ||
261 ReviewerIdentity.ClientSignature.Length == 0)
262 {
263 throw new ForbiddenException("Reviewer identity lacks a signature.");
264 }
265
266 if (CaseInsensitiveString.IsNullOrEmpty(ReviewerJid))
267 {
268 throw new ForbiddenException("Reviewer identity not trusted for reviewing.");
269 }
270
271 break;
272 }
273 }
274 }
275 }
276
277 if (ReviewedIdentity is null || !ReviewedHasStatus ||
278 ReviewerIdentity is null || !ReviewerHasStatus ||
279 SignedIdentity is null)
280 {
281 throw new ForbiddenException("Attachment not a correctly formed peer-review document.");
282 }
283
284 CaseInsensitiveString Pnr = ReviewedIdentity["PNR"];
285
286 if (Pnr == ReviewerIdentity["PNR"] && !CaseInsensitiveString.IsNullOrEmpty(Pnr))
287 throw new ForbiddenException("Reviewer cannot be the same person as the reviewed person.");
288
289 if (!ReviewerIdentity.ValidateSignature(SignedIdentity, PeerSignature))
290 throw new ForbiddenException("Peer signature invalid.");
291
292 if (PeerReviewConfiguration.Instance?.AllowPeerReview ?? false)
293 {
294 int NrPhotos = 0;
295
296 if (!(Identity.Attachments is null))
297 {
298 foreach (AttachmentReference Ref in Identity.Attachments)
299 {
300 if (Ref.ContentType.StartsWith("image/"))
301 NrPhotos++;
302 }
303 }
304
305 if (NrPhotos < PeerReviewConfiguration.Instance.NrPhotosRequired)
306 {
307 throw new ForbiddenException("Peer review not accepted: Identity lacks sufficient photos (" +
308 PeerReviewConfiguration.Instance.NrPhotosRequired.ToString() + ").");
309 }
310
311 PersonalInfo RI = GetPersonalInfo(ReviewerIdentity);
312
313 if (PeerReviewConfiguration.Instance.RequireFirstName && CaseInsensitiveString.IsNullOrEmpty(RI.FirstName))
314 throw new BadRequestException("Reviewer identity lacks a First name. Peer review not accepted.");
315
316 if (PeerReviewConfiguration.Instance.RequireMiddleName && CaseInsensitiveString.IsNullOrEmpty(RI.MiddleNames))
317 throw new BadRequestException("Reviewer identity lacks a Middle name. Peer review not accepted.");
318
319 if (PeerReviewConfiguration.Instance.RequireLastName && CaseInsensitiveString.IsNullOrEmpty(RI.LastNames))
320 throw new BadRequestException("Reviewer identity lacks a Last name. Peer review not accepted.");
321
322 if (PeerReviewConfiguration.Instance.RequirePersonalNumber && CaseInsensitiveString.IsNullOrEmpty(RI.PersonalNumber))
323 throw new BadRequestException("Reviewer identity lacks a Personal number. Peer review not accepted.");
324
326 throw new BadRequestException("Reviewer identity lacks an address. Peer review not accepted.");
327
328 if (PeerReviewConfiguration.Instance.RequirePostalCode && CaseInsensitiveString.IsNullOrEmpty(RI.PostalCode))
329 throw new BadRequestException("Reviewer identity lacks a Postal Code (ZIP). Peer review not accepted.");
330
332 throw new BadRequestException("Reviewer identity lacks an Area. Peer review not accepted.");
333
335 throw new BadRequestException("Reviewer identity lacks a City. Peer review not accepted.");
336
338 throw new BadRequestException("Reviewer identity lacks a Region. Peer review not accepted.");
339
341 throw new BadRequestException("Reviewer identity lacks a Country. Peer review not accepted.");
342
343 if (!CaseInsensitiveString.IsNullOrEmpty(RI.Country))
344 {
345 if (PeerReviewConfiguration.Instance.RequireIso3166Compliance && !Iso3166.TryGetCountry(RI.Country, out string _))
346 throw new BadRequestException("Reviewer Country is not an ISO 3166-1 Country Code. Peer review not accepted.");
347
348 if (!CaseInsensitiveString.IsNullOrEmpty(RI.PersonalNumber))
349 {
350 bool? Valid = await PersonalNumberSchemes.IsValid(RI.Country, RI.PersonalNumber);
351 if (Valid.HasValue && !Valid.Value)
352 throw new BadRequestException("The personal number format used by the reviewer does not comply with regulations.");
353 }
354 }
355
356 if (PeerReviewConfiguration.Instance.RequireNationality && CaseInsensitiveString.IsNullOrEmpty(RI.Nationality))
357 throw new BadRequestException("Reviewer identity lacks a Nationality. Peer review not accepted.");
358
359 if (PeerReviewConfiguration.Instance.RequireGender && !RI.Gender.HasValue)
360 throw new BadRequestException("Reviewer identity lacks a Gender. Peer review not accepted.");
361
362 if (PeerReviewConfiguration.Instance.RequireBirthDate && !RI.BirthDate.HasValue)
363 throw new BadRequestException("Reviewer identity lacks a Birth Date. Peer review not accepted.");
364
365 if (RI.HasOrg)
366 {
367 if (CaseInsensitiveString.IsNullOrEmpty(RI.OrgName)) // TODO: Configurable
368 throw new BadRequestException("Reviewer identity lacks an organization name. Peer review not accepted.");
369
370 if (CaseInsensitiveString.IsNullOrEmpty(RI.OrgDepartment)) // TODO: Configurable
371 throw new BadRequestException("Reviewer identity lacks a department. Peer review not accepted.");
372
373 if (CaseInsensitiveString.IsNullOrEmpty(RI.OrgRole)) // TODO: Configurable
374 throw new BadRequestException("Reviewer identity lacks a role. Peer review not accepted.");
375
376 if (CaseInsensitiveString.IsNullOrEmpty(RI.OrgNumber)) // TODO: Configurable
377 throw new BadRequestException("Reviewer identity lacks an organization number. Peer review not accepted.");
378
379 if (PeerReviewConfiguration.Instance.RequireAddress && CaseInsensitiveString.IsNullOrEmpty(RI.OrgAddress))
380 throw new BadRequestException("Reviewer identity lacks an organization address. Peer review not accepted.");
381
382 if (PeerReviewConfiguration.Instance.RequirePostalCode && CaseInsensitiveString.IsNullOrEmpty(RI.OrgPostalCode))
383 throw new BadRequestException("Reviewer identity lacks an organization Postal Code (ZIP). Peer review not accepted.");
384
386 throw new BadRequestException("Reviewer identity lacks an organization Area. Peer review not accepted.");
387
389 throw new BadRequestException("Reviewer identity lacks an organization City. Peer review not accepted.");
390
391 if (PeerReviewConfiguration.Instance.RequireRegion && CaseInsensitiveString.IsNullOrEmpty(RI.OrgRegion))
392 throw new BadRequestException("Reviewer identity lacks an organization Region. Peer review not accepted.");
393
394 if (PeerReviewConfiguration.Instance.RequireCountry && CaseInsensitiveString.IsNullOrEmpty(RI.OrgCountry))
395 throw new BadRequestException("Reviewer identity lacks an organization Country. Peer review not accepted.");
396
397 if (!CaseInsensitiveString.IsNullOrEmpty(RI.Country))
398 {
399 if (PeerReviewConfiguration.Instance.RequireIso3166Compliance && !Iso3166.TryGetCountry(RI.Country, out string _))
400 throw new BadRequestException("Reviewer organization Country is not an ISO 3166-1 Country Code. Peer review not accepted.");
401 }
402 }
403
404 await CheckPeerReviewFields(ReviewedIdentity);
405
406 PersonalInfo PI = GetPersonalInfo(ReviewedIdentity);
407
408 if (PI.HasOrg)
409 {
410 if (!RI.HasOrg)
411 throw new BadRequestException("Peer reviewer lacks organization information. Peer review not accepted.");
412
413 if (PI.OrgName != RI.OrgName ||
414 PI.OrgNumber != RI.OrgNumber ||
415 PI.OrgCountry != RI.OrgCountry)
416 {
417 throw new BadRequestException("Peer reviewer must be from same company. Peer review not accepted.");
418 }
419 }
420
421 if (!(Identity.Attachments is null))
422 {
423 LegalIdentity[] Reviewers2 = await GetPeerReviewers(Identity);
424
425 foreach (LegalIdentity ReviewerID in Reviewers2)
426 {
427 if (ReviewerID["PNR"] == Pnr)
428 throw new BadRequestException("A reviewer can only review the application once.");
429 }
430
431 if (Reviewers is null)
432 Reviewers = new List<LegalIdentity>();
433
434 Reviewers.AddRange(Reviewers2);
435 }
436 }
437
438 if (Reviewers is null)
439 Reviewers = new List<LegalIdentity>();
440
441 Reviewers.Add(ReviewerIdentity);
442 IncNrPeerReviews = true;
443
444 sb.Clear();
445 }
446 }
447
448 using (TemporaryStream TempFile = new TemporaryStream())
449 {
450 await TempFile.WriteAsync(Attachment, 0, Attachment.Length);
451
452 KeyValuePair<Attachment, AttachmentReference> A =
453 await LegalComponent.CreateAttachment(AttachmentFileName, Identity,
454 AttachmentSignature, TempFile, AttachmentContentType,
455 User.UserName, null, XmppServerModule.Legal?.AttachmentsFolder ?? "Attachments");
456
457 List<AttachmentReference> References = new List<AttachmentReference>();
458 bool Approved = false;
459
460 if (!(Identity.Attachments is null))
461 References.AddRange(Identity.Attachments);
462
463 References.Add(A.Value);
464 Identity.Attachments = References.ToArray();
465 Identity.Updated = LegalComponent.NowSecond;
466
467 if (IncNrPeerReviews)
468 {
469 Identity.NrPeerReviews++;
470
471 if ((PeerReviewConfiguration.Instance?.AllowPeerReview ?? true) &&
472 Identity.NrPeerReviews >= (PeerReviewConfiguration.Instance?.NrReviewersToApprove ?? 2) &&
473 Identity.State == IdentityState.Created)
474 {
475 Identity.State = IdentityState.Approved;
476 Approved = true;
477
478 StringBuilder Markdown = new StringBuilder();
479
480 Markdown.AppendLine("Peer reviewed Legal ID approved");
481 Markdown.AppendLine("===================================");
482 Markdown.AppendLine();
483
484 XmppServerModule.Legal.AppendMarkdown(Markdown, Identity, "Applicant");
485
486 int Index = 0;
487
488 foreach (LegalIdentity Reviewer in Reviewers)
489 {
490 Index++;
491 XmppServerModule.Legal.AppendMarkdown(Markdown, Reviewer, "Reviewer " + Index.ToString());
492 }
493
494 await Gateway.SendNotification(Markdown.ToString());
495 }
496 }
497
498 Identity.Sign(XmppServerModule.Legal);
499 await Database.Update(Identity);
500
501 if (Identity.State == IdentityState.Approved)
502 await CopyPropertiesFromApprovedIdentity(Identity, Account as DataStorage.Account);
503
504 string BareJid = User.UserName + "@" + Gateway.Domain;
505
506 Log.Informational("Attachment added to Legal Identity registration.",
507 Identity.Id.Value, BareJid, "LegalIdUpdated", Identity.GetTags());
508
509 sb.Clear();
510 Identity.Serialize(sb, true, true, true, true, true, true, true, null, XmppServerModule.Legal);
511 string IdentityXml = sb.ToString();
512
513 if (Approved)
514 {
515 await LegalComponent.AddLegalIdentityReference(Identity);
516
517 XmppAddress IdentityAddress = new XmppAddress(Identity.Id);
518 int i = IdentityAddress.Domain.IndexOf('.');
519 string JidDomain = i < 0 ? XmppServerModule.Server.Domain : IdentityAddress.Domain.Substring(i + 1);
520
521 await XmppServerModule.Server.SendMessage(string.Empty, string.Empty, XmppServerModule.Legal.MainDomain,
522 new XmppAddress(Identity.Account + "@" + XmppServerModule.Server.Domain), string.Empty, IdentityXml);
523 }
524
525 XmlDocument IdentityDoc = new XmlDocument()
526 {
527 PreserveWhitespace = true
528 };
529 IdentityDoc.LoadXml(IdentityXml);
530
531 await Response.Return(new NamedDictionary<string, object>("IdentityResponse", AgentNamespace)
532 {
533 { "Identity", IdentityDoc },
534 });
535 }
536
537 }
538 finally
539 {
540 if (DecodedAttachment is IDisposable Disposable)
541 Disposable.Dispose();
542 }
543 }
544 }
545
546 internal static async Task CheckPeerReviewFields(LegalIdentity ReviewedIdentity)
547 {
548 if (PeerReviewConfiguration.Instance?.AllowPeerReview ?? false)
549 {
550 PersonalInfo PI = GetPersonalInfo(ReviewedIdentity);
551
552 if (PeerReviewConfiguration.Instance.RequireFirstName && CaseInsensitiveString.IsNullOrEmpty(PI.FirstName))
553 throw new BadRequestException("First name (FIRST) is a required field.");
554
555 if (PeerReviewConfiguration.Instance.RequireMiddleName && CaseInsensitiveString.IsNullOrEmpty(PI.MiddleNames))
556 throw new BadRequestException("Middle name (MIDDLE) is a required field.");
557
558 if (PeerReviewConfiguration.Instance.RequireLastName && CaseInsensitiveString.IsNullOrEmpty(PI.LastNames))
559 throw new BadRequestException("Last name (LAST) is a required field.");
560
561 if (PeerReviewConfiguration.Instance.RequirePersonalNumber && CaseInsensitiveString.IsNullOrEmpty(PI.PersonalNumber))
562 throw new BadRequestException("Personal Number (PNR) is a required field.");
563
565 throw new BadRequestException("Address (ADDR) is a required field.");
566
567 if (PeerReviewConfiguration.Instance.RequirePostalCode && CaseInsensitiveString.IsNullOrEmpty(PI.PostalCode))
568 throw new BadRequestException("Postal Code (ZIP) is a required field.");
569
571 throw new BadRequestException("Area (AREA) is a required field.");
572
574 throw new BadRequestException("City (CITY) is a required field.");
575
577 throw new BadRequestException("Region (REGION) is a required field.");
578
580 throw new BadRequestException("Country (COUNTRY) is a required field.");
581
582 if (!CaseInsensitiveString.IsNullOrEmpty(PI.Country))
583 {
584 if (PeerReviewConfiguration.Instance.RequireIso3166Compliance &&
585 !Iso3166.TryGetCountry(PI.Country, out string _))
586 {
587 throw new BadRequestException("Country must be a ISO 3166-1 Country Code.");
588 }
589
590 if (!CaseInsensitiveString.IsNullOrEmpty(PI.PersonalNumber))
591 {
592 bool? Valid = await PersonalNumberSchemes.IsValid(PI.Country, PI.PersonalNumber);
593 if (Valid.HasValue && !Valid.Value)
594 throw new BadRequestException("The personal number format does not comply with regulations in your country.");
595 }
596 }
597
598 if (PeerReviewConfiguration.Instance.RequireNationality && CaseInsensitiveString.IsNullOrEmpty(PI.Nationality))
599 throw new BadRequestException("Nationality (NATIONALITY) is a required field.");
600
601 if (PeerReviewConfiguration.Instance.RequireGender && !PI.Gender.HasValue)
602 throw new BadRequestException("Gender (GENDER) is a required field.");
603
604 if (PeerReviewConfiguration.Instance.RequireBirthDate && !PI.BirthDate.HasValue)
605 throw new BadRequestException("Birth Date (BDAY, BMONTH & BYEAR) are required fields.");
606
607 if (PI.HasOrg)
608 {
609 if (CaseInsensitiveString.IsNullOrEmpty(PI.OrgName)) // TODO: Make configurable
610 throw new BadRequestException("Organization name is a required field for work identities.");
611
612 if (CaseInsensitiveString.IsNullOrEmpty(PI.OrgDepartment)) // TODO: Make configurable
613 throw new BadRequestException("Department is a required field for work identities.");
614
615 if (CaseInsensitiveString.IsNullOrEmpty(PI.OrgRole)) // TODO: Make configurable
616 throw new BadRequestException("Role is a required field for work identites.");
617
618 if (PeerReviewConfiguration.Instance.RequirePersonalNumber && CaseInsensitiveString.IsNullOrEmpty(PI.OrgNumber))
619 throw new BadRequestException("Organization Number is a required field for work identites.");
620
621 if (PeerReviewConfiguration.Instance.RequireAddress && CaseInsensitiveString.IsNullOrEmpty(PI.OrgAddress))
622 throw new BadRequestException("Organization Address is a required field for work identites.");
623
624 if (PeerReviewConfiguration.Instance.RequirePostalCode && CaseInsensitiveString.IsNullOrEmpty(PI.OrgPostalCode))
625 throw new BadRequestException("Organization Postal Code (ZIP) is a required field for work identites.");
626
628 throw new BadRequestException("Organization Area is a required field for work identites.");
629
631 throw new BadRequestException("Organization City is a required field for work identites.");
632
633 if (PeerReviewConfiguration.Instance.RequireRegion && CaseInsensitiveString.IsNullOrEmpty(PI.OrgRegion))
634 throw new BadRequestException("Organization Region is a required field for work identites.");
635
636 if (PeerReviewConfiguration.Instance.RequireCountry && CaseInsensitiveString.IsNullOrEmpty(PI.OrgCountry))
637 throw new BadRequestException("Organization Country is a required field for work identites.");
638
639 if (!CaseInsensitiveString.IsNullOrEmpty(PI.OrgCountry))
640 {
641 if (PeerReviewConfiguration.Instance.RequireIso3166Compliance && !Iso3166.TryGetCountry(PI.OrgCountry, out string _))
642 throw new BadRequestException("Organization Country must be a ISO 3166-1 Country Code.");
643 }
644 }
645 }
646 }
647
648 }
649}
Static class managing encoding and decoding of internet content.
static Task< object > DecodeAsync(string ContentType, byte[] Data, Encoding Encoding, KeyValuePair< string, string >[] Fields, Uri BaseUri)
Decodes an object.
A Named dictionary is a dictionary, with a local name and a namespace. Use it to return content that ...
Static class managing loading of resources stored as embedded resources or in content files.
Definition: Resources.cs:15
static string LoadResourceAsText(string ResourceName)
Loads a text resource from an embedded resource.
Definition: Resources.cs:96
Helps with common XML-related tasks.
Definition: XML.cs:19
static string Attribute(XmlElement E, string Name)
Gets the value of an XML attribute.
Definition: XML.cs:914
Static class managing the application event log. Applications and services log events on this static ...
Definition: Log.cs:13
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:334
Static class managing the runtime environment of the IoT Gateway.
Definition: Gateway.cs:126
static CaseInsensitiveString Domain
Domain name.
Definition: Gateway.cs:2354
static Task SendNotification(Graph Graph)
Sends a graph as a notification message to configured notification recipients.
Definition: Gateway.cs:3826
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:18
HttpRequestHeader Header
Request header.
Definition: HttpRequest.cs:134
Represets a response of an HTTP client request.
Definition: HttpResponse.cs:21
async Task Return(object Object)
Returns an object to the client. This method can only be called once per response,...
The server has not found anything matching the Request-URI. No indication is given of whether the con...
Abstract base class for Elliptic Curve endpoints.
override byte[] Sign(byte[] Data)
Signs binary data using the local private key.
XmppAddress MainDomain
Main/principal domain address
Definition: Component.cs:86
Contains information about one XMPP address.
Definition: XmppAddress.cs:9
CaseInsensitiveString Domain
Domain
Definition: XmppAddress.cs:97
static readonly XmppAddress Empty
Empty address.
Definition: XmppAddress.cs:31
Task< bool > SendMessage(string Type, string Id, string From, string To, string Language, string ContentXml)
Sends a Message stanza to a recipient.
Definition: XmppServer.cs:3412
CaseInsensitiveString Domain
Domain name.
Definition: XmppServer.cs:882
Represents a case-insensitive string.
string Value
String-representation of the case-insensitive string. (Representation is case sensitive....
static readonly CaseInsensitiveString Empty
Empty case-insensitive string
string LowerCase
Lower-case representation of the case-insensitive string.
int IndexOf(CaseInsensitiveString value, StringComparison comparisonType)
Reports the zero-based index of the first occurrence of the specified string in the current System....
static bool IsNullOrEmpty(CaseInsensitiveString value)
Indicates whether the specified string is null or an CaseInsensitiveString.Empty string.
CaseInsensitiveString Substring(int startIndex, int length)
Retrieves a substring from this instance. The substring starts at a specified character position and ...
Static interface for database persistence. In order to work, a database provider has to be assigned t...
Definition: Database.cs:19
static async Task Update(object Object)
Updates an object in the database.
Definition: Database.cs:626
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.
Manages a temporary stream. Contents is kept in-memory, if below a memory threshold,...
override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
Asynchronously writes a sequence of bytes to the current stream, advances the current position within...
Represents a named semaphore, i.e. an object, identified by a name, that allows single concurrent wri...
Definition: Semaphore.cs:19
Static class of application-wide semaphores that can be used to order access to editable objects.
Definition: Semaphores.cs:16
static async Task< Semaphore > BeginWrite(string Key)
Waits until the semaphore identified by Key is ready for writing. Each call to BeginWrite must be fo...
Definition: Semaphores.cs:90
Class managing a script expression.
Definition: Expression.cs:39
Contains methods for simple hash calculations.
Definition: Hashes.cs:59
static byte[] ComputeHMACSHA256Hash(byte[] Key, byte[] Data)
Computes the HMAC-SHA-256 hash of a block of binary data.
Definition: Hashes.cs:585
Contains information about a broker account.
Definition: Account.cs:28
bool Enabled
If account is enabled
Definition: Account.cs:287
string Password
Password of account
Definition: Account.cs:109
Account()
Contains information about a broker account.
Definition: Account.cs:72
Provides the user configuration options regarding peer-review of new legal identities.
static PeerReviewConfiguration Instance
Current instance of configuration.
Task< bool > HasNonceBeenUsed(string Nonce)
Checks if a Nonce value has been used.
Definition: AgentApi.cs:113
Task RegisterNonceValue(string Nonce)
Registers a nonce value.
Definition: AgentApi.cs:122
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
Service Module hosting the XMPP broker and its components.
Interface for XMPP user accounts.
Definition: IAccount.cs:9
Definition: App.xaml.cs:4