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 Paiwise;
2using System;
4using System.Text;
5using System.Threading.Tasks;
6using System.Xml;
7using Waher.Content;
9using Waher.Events;
20using Waher.Script;
23using Waher.Security;
31
33{
38 {
43 : base("Legal/AddIdAttachment",
44 new KeyValuePair<Type, Expression>(typeof(Dictionary<string, object>), new Expression(jsonPattern)),
45 new KeyValuePair<Type, Expression>(typeof(XmlDocument), new Expression(xmlPattern)))
46 {
47 }
48
49 private static readonly string jsonPattern = Resources.LoadResourceAsText(typeof(AddIdAttachment).Namespace + ".JSON.AddIdAttachment.req");
50 private static readonly string xmlPattern = Resources.LoadResourceAsText(typeof(AddIdAttachment).Namespace + ".XML.AddIdAttachment.req");
51
60 public override async Task POST(HttpRequest Request, HttpResponse Response, Dictionary<string, IElement> Parameters)
61 {
63
64 string KeyId = (string)Parameters["PKeyId"].AssociatedObjectValue;
65 string Nonce = (string)Parameters["PNonce"].AssociatedObjectValue;
66 string KeySignature = (string)Parameters["PKeySignature"].AssociatedObjectValue;
67 string RequestSignature = (string)Parameters["PRequestSignature"].AssociatedObjectValue;
68 CaseInsensitiveString LegalId = (string)Parameters["PLegalId"].AssociatedObjectValue;
69 string AttachmentBase64 = (string)Parameters["PAttachmentBase64"].AssociatedObjectValue;
70 string AttachmentFileName = (string)Parameters["PAttachmentFileName"].AssociatedObjectValue;
71 string AttachmentContentType = (string)Parameters["PAttachmentContentType"].AssociatedObjectValue;
72 StringBuilder sb = new StringBuilder();
73 byte[] Attachment;
74 object DecodedAttachment;
75
76 if (string.IsNullOrEmpty(KeyId))
77 {
78 await Response.SendResponse(new BadRequestException("Key ID cannot be empty."));
79 return;
80 }
81
82 if (string.IsNullOrEmpty(Nonce) || Nonce.Length < 32)
83 {
84 await Response.SendResponse(new ForbiddenException(Request, "Nonce too short."));
85 return;
86 }
87
88 using Semaphore Semaphore = await Semaphores.BeginWrite("iotid:" + LegalId.LowerCase);
89 LegalIdentity Identity = await LegalComponent.GetLocalLegalIdentity(LegalId);
90 if (Identity is null)
91 {
92 await Response.SendResponse(new NotFoundException("Legal identity not found."));
93 return;
94 }
95
96 if (Identity.Account != User.UserName)
97 {
98 await Response.SendResponse(new ForbiddenException(Request, "Only allowed to add attachments to your own legal identities."));
99 return;
100 }
101
102 if (Identity.State != IoTBroker.Legal.Identity.IdentityState.Created)
103 {
104 await Response.SendResponse(new ForbiddenException(Request, "Attachments can only be added to newly created identities before they are approved."));
105 return;
106 }
107
108 IAccount Account = await XmppServerModule.GetAccountAsync(Identity.Account);
109 if (Account is null)
110 {
111 await Response.SendResponse(new ForbiddenException(Request, "Account has been removed."));
112 return;
113 }
114
115 if (!Account.Enabled)
116 {
117 await Response.SendResponse(new ForbiddenException(Request, "Account has been disabled."));
118 return;
119 }
120
121 AgentKey AgentKey = await Database.FindFirstDeleteRest<AgentKey>(new FilterAnd(
122 new FilterFieldEqualTo("Account", User.UserName),
123 new FilterFieldEqualTo("Id", KeyId)));
124
125 if (AgentKey is null)
126 {
127 await Response.SendResponse(new NotFoundException("Key not found."));
128 return;
129 }
130
131 sb.Append(User.UserName);
132 sb.Append(':');
133 sb.Append(Request.Header.Host.Value);
134 sb.Append(':');
135 sb.Append(AgentKey.LocalName);
136 sb.Append(':');
137 sb.Append(AgentKey.Namespace);
138 sb.Append(':');
139 sb.Append(KeyId);
140
141 //string s1 = sb.ToString();
142
143 sb.Append(':');
144 sb.Append(KeySignature);
145
146 string s2 = sb.ToString();
147
148 sb.Append(':');
149 sb.Append(Nonce);
150 sb.Append(':');
151 sb.Append(AttachmentBase64);
152 sb.Append(':');
153 sb.Append(AttachmentFileName);
154 sb.Append(':');
155 sb.Append(AttachmentContentType);
156 sb.Append(':');
157 sb.Append(LegalId.Value);
158
159 string s3 = sb.ToString();
160
161 string s = Convert.ToBase64String(
163 Encoding.UTF8.GetBytes(User.Account.Password),
164 Encoding.UTF8.GetBytes(s3)));
165
166 if (s != RequestSignature)
167 {
168 string Msg = "Request Signature invalid.";
169 await Response.SendResponse(new ForbiddenException(Request, Msg));
170 return;
171 }
172
173 if (await Gateway.HasNonceBeenUsed(Nonce))
174 {
175 string Msg = "Nonce value has already been used.";
176 await Response.SendResponse(new ForbiddenException(Request, Msg));
177 return;
178 }
179
180 await Gateway.RegisterNonceValue(Nonce);
181
182 try
183 {
184 Attachment = Convert.FromBase64String(AttachmentBase64);
185 ContentResponse ContentResponse = await InternetContent.DecodeAsync(AttachmentContentType, Attachment, null);
186
188 {
189 await Response.SendResponse(ContentResponse.Error);
190 return;
191 }
192
193 DecodedAttachment = ContentResponse.Decoded;
194 }
195 catch (Exception)
196 {
197 await Response.SendResponse(new BadRequestException("Invalid attachment."));
198 return;
199 }
200
201 try
202 {
203 EllipticCurveEndpoint KeyEndpoint = ApplyId.GetEndpoint(Request, AgentKey, s2);
204 byte[] AttachmentSignature = KeyEndpoint.Sign(Attachment);
205
206 if (!(Identity.Attachments is null))
207 {
208 string AttachmentSignatureBase64 = Convert.ToBase64String(AttachmentSignature);
209
210 foreach (AttachmentReference Ref in Identity.Attachments)
211 {
212 if (Convert.ToBase64String(Ref.Signature) == s)
213 {
214 await Response.SendResponse(new BadRequestException("Attachment already assigned to identity."));
215 return;
216 }
217 }
218 }
219
220 bool IncNrPeerReviews = false;
221 List<LegalIdentity> Reviewers = null;
222 string LegalNamespace = NamespaceLegalIdentity(Identity.Version);
223
224 if (DecodedAttachment is XmlDocument Doc)
225 {
226 if (Doc.DocumentElement.LocalName == "identityReview" &&
227 Doc.DocumentElement.NamespaceURI == LegalNamespace)
228 {
229 await Response.SendResponse(new ForbiddenException(Request, "Forbidden to add Identity Review documents."));
230 return;
231 }
232 else if (Doc.DocumentElement.LocalName == "peerReview" &&
233 Doc.DocumentElement.NamespaceURI == LegalNamespace)
234 {
235 LegalIdentity ReviewedIdentity = null;
236 LegalIdentity ReviewerIdentity = null;
239 bool ReviewedHasStatus = false;
240 bool ReviewerHasStatus = false;
241 byte[] PeerSignature = Convert.FromBase64String(XML.Attribute(Doc.DocumentElement, "s"));
242 DateTime TP = XML.Attribute(Doc.DocumentElement, "tp", DateTime.Now);
243 byte[] SignedIdentity = null;
244 DateTime UtcNow = DateTime.UtcNow;
245
246 foreach (XmlNode N in Doc.DocumentElement.ChildNodes)
247 {
248 if (!(N is XmlElement E) || N.NamespaceURI != LegalNamespace)
249 continue;
250
251 foreach (XmlNode N2 in E.ChildNodes)
252 {
253 if (N2 is XmlElement E2 &&
254 E2.LocalName == "identity" &&
255 E2.NamespaceURI == LegalNamespace)
256 {
257 switch (E.LocalName)
258 {
259 case "reviewed":
260 ReviewedIdentity = LegalIdentity.Parse(E2, out ReviewedHasStatus,
261 out Dictionary<string, string> ReviewedIdentityAttachmentsUrls);
262
263 sb.Clear();
264 ReviewedIdentity.Serialize(sb, true, true, true, false, false, false, false, null, XmppServerModule.Legal);
265 string s1 = sb.ToString();
266
267 sb.Clear();
268 Identity.Serialize(sb, true, true, true, false, false, false, false, null, XmppServerModule.Legal);
269 s2 = sb.ToString();
270
271 if (s1 != s2)
272 {
273 await Response.SendResponse(new ForbiddenException(Request, "Reviewed identity mismatch."));
274 return;
275 }
276
277 sb.Clear();
278 ReviewedIdentity.Serialize(sb, true, true, true, true, true, true, true, ReviewedIdentityAttachmentsUrls, XmppServerModule.Legal);
279 SignedIdentity = Encoding.UTF8.GetBytes(sb.ToString());
280 break;
281
282 case "reviewer":
283 ReviewerIdentity = LegalIdentity.Parse(E2, out ReviewerHasStatus,
284 out Dictionary<string, string> ReviewerIdentityAttachmentsUrls);
285
286 sb.Clear();
287 ReviewerIdentity.Serialize(sb, false, false, false, false, false, false, false, null, XmppServerModule.Legal);
288 byte[] Data = Encoding.UTF8.GetBytes(sb.ToString());
289 ReviewerJid = ReviewerIdentity[PersonalInformation.JidTag];
290
291 if (!CaseInsensitiveString.IsNullOrEmpty(ReviewerJid))
292 {
293 (ReviewerIdentity, _) = await XmppServerModule.Legal.ValidateSignature(new XmppAddress(ReviewerJid), TP, Data, ReviewerIdentity.ClientSignature);
294 if (ReviewerIdentity is null || ReviewerJid != ReviewerIdentity[PersonalInformation.JidTag])
295 {
296 await Response.SendResponse(new ForbiddenException(Request, "Peer identity signature invalid."));
297 return;
298 }
299 }
300
301 if (ReviewerIdentity.State != IoTBroker.Legal.Identity.IdentityState.Approved)
302 {
303 await Response.SendResponse(new ForbiddenException(Request, "Reviewer identity not approved."));
304 return;
305 }
306
307 if (UtcNow < ReviewerIdentity.From.ToUniversalTime() ||
308 UtcNow > ReviewerIdentity.To.ToUniversalTime())
309 {
310 await Response.SendResponse(new ForbiddenException(Request, "Reviewer identity not valid."));
311 return;
312 }
313
314 if (CaseInsensitiveString.IsNullOrEmpty(ReviewerIdentity.Provider))
315 {
316 await Response.SendResponse(new ForbiddenException(Request, "Reviewer identity lacks a provider."));
317 return;
318 }
319
320 if (string.IsNullOrEmpty(ReviewerIdentity.ClientKeyName) ||
321 ReviewerIdentity.ClientPubKey is null ||
322 ReviewerIdentity.ClientPubKey.Length == 0)
323 {
324 await Response.SendResponse(new ForbiddenException(Request, "Reviewer identity lacks a key."));
325 return;
326 }
327
328 if (ReviewerIdentity.ClientSignature is null ||
329 ReviewerIdentity.ClientSignature.Length == 0)
330 {
331 await Response.SendResponse(new ForbiddenException(Request, "Reviewer identity lacks a signature."));
332 return;
333 }
334
335 if (CaseInsensitiveString.IsNullOrEmpty(ReviewerJid))
336 {
337 await Response.SendResponse(new ForbiddenException(Request, "Reviewer identity not trusted for reviewing."));
338 return;
339 }
340
341 break;
342 }
343 }
344 }
345 }
346
347 if (ReviewedIdentity is null || !ReviewedHasStatus ||
348 ReviewerIdentity is null || !ReviewerHasStatus ||
349 SignedIdentity is null)
350 {
351 await Response.SendResponse(new ForbiddenException(Request, "Attachment not a correctly formed peer-review document."));
352 return;
353 }
354
356
357 if (Pnr == ReviewerIdentity[PersonalInformation.PersonalNumberTag] && !CaseInsensitiveString.IsNullOrEmpty(Pnr))
358 {
359 await Response.SendResponse(new ForbiddenException(Request, "Reviewer cannot be the same person as the reviewed person."));
360 return;
361 }
362
363 if (!ReviewerIdentity.ValidateSignature(SignedIdentity, PeerSignature))
364 {
365 await Response.SendResponse(new ForbiddenException(Request, "Peer signature invalid."));
366 return;
367 }
368
370 {
371 int NrPhotos = 0;
372
373 if (!(Identity.Attachments is null))
374 {
375 foreach (AttachmentReference Ref in Identity.Attachments)
376 {
377 if (Ref.ContentType.StartsWith("image/"))
378 NrPhotos++;
379 }
380 }
381
383 {
384 await Response.SendResponse(new ForbiddenException(Request, "Peer review not accepted: Identity lacks sufficient photos (" +
386 return;
387 }
388
389 global::Paiwise.PersonalInformation RI = GetPersonalInformation(ReviewerIdentity);
390
391 if (!PeerReviewConfiguration.Instance.IsReviewerAllowed(RI.Jid, ReviewerIdentity.Id))
392 {
393 await Response.SendResponse(new BadRequestException("Reviewer not white-listed. Peer review not accepted."));
394 return;
395 }
397 {
398 await Response.SendResponse(new BadRequestException("Reviewer identity lacks a First name. Peer review not accepted."));
399 return;
400 }
401
403 {
404 await Response.SendResponse(new BadRequestException("Reviewer identity lacks a Middle name. Peer review not accepted."));
405 return;
406 }
407
409 {
410 await Response.SendResponse(new BadRequestException("Reviewer identity lacks a Last name. Peer review not accepted."));
411 return;
412 }
413
415 {
416 await Response.SendResponse(new BadRequestException("Reviewer identity lacks a Personal number. Peer review not accepted."));
417 return;
418 }
419
421 {
422 await Response.SendResponse(new BadRequestException("Reviewer identity lacks an address. Peer review not accepted."));
423 return;
424 }
425
427 {
428 await Response.SendResponse(new BadRequestException("Reviewer identity lacks a Postal Code (ZIP). Peer review not accepted."));
429 return;
430 }
431
433 {
434 await Response.SendResponse(new BadRequestException("Reviewer identity lacks an Area. Peer review not accepted."));
435 return;
436 }
437
439 {
440 await Response.SendResponse(new BadRequestException("Reviewer identity lacks a City. Peer review not accepted."));
441 return;
442 }
443
445 {
446 await Response.SendResponse(new BadRequestException("Reviewer identity lacks a Region. Peer review not accepted."));
447 return;
448 }
449
451 {
452 await Response.SendResponse(new BadRequestException("Reviewer identity lacks a Country. Peer review not accepted."));
453 return;
454 }
455
456 if (!CaseInsensitiveString.IsNullOrEmpty(RI.Country))
457 {
459 {
460 await Response.SendResponse(new BadRequestException("Reviewer Country is not an ISO 3166-1 Country Code. Peer review not accepted."));
461 return;
462 }
463
464 if (!CaseInsensitiveString.IsNullOrEmpty(RI.PersonalNumber))
465 {
466 bool? Valid = await PersonalNumberSchemes.IsValid(RI.Country, RI.PersonalNumber);
467 if (Valid.HasValue && !Valid.Value)
468 {
469 await Response.SendResponse(new BadRequestException("The personal number format used by the reviewer does not comply with regulations."));
470 return;
471 }
472 }
473 }
474
476 {
477 await Response.SendResponse(new BadRequestException("Reviewer identity lacks a Nationality. Peer review not accepted."));
478 return;
479 }
480
481 if (PeerReviewConfiguration.Instance.RequireGender && !RI.Gender.HasValue)
482 {
483 await Response.SendResponse(new BadRequestException("Reviewer identity lacks a Gender. Peer review not accepted."));
484 return;
485 }
486
487 if (PeerReviewConfiguration.Instance.RequireBirthDate && !RI.BirthDate.HasValue)
488 {
489 await Response.SendResponse(new BadRequestException("Reviewer identity lacks a Birth Date. Peer review not accepted."));
490 return;
491 }
492
493 if (RI.HasOrg)
494 {
495 if (CaseInsensitiveString.IsNullOrEmpty(RI.OrgName)) // TODO: Configurable
496 {
497 await Response.SendResponse(new BadRequestException("Reviewer identity lacks an organization name. Peer review not accepted."));
498 return;
499 }
500
501 if (CaseInsensitiveString.IsNullOrEmpty(RI.OrgDepartment)) // TODO: Configurable
502 {
503 await Response.SendResponse(new BadRequestException("Reviewer identity lacks a department. Peer review not accepted."));
504 return;
505 }
506
507 if (CaseInsensitiveString.IsNullOrEmpty(RI.OrgRole)) // TODO: Configurable
508 {
509 await Response.SendResponse(new BadRequestException("Reviewer identity lacks a role. Peer review not accepted."));
510 return;
511 }
512
513 if (CaseInsensitiveString.IsNullOrEmpty(RI.OrgNumber)) // TODO: Configurable
514 {
515 await Response.SendResponse(new BadRequestException("Reviewer identity lacks an organization number. Peer review not accepted."));
516 return;
517 }
518
520 {
521 await Response.SendResponse(new BadRequestException("Reviewer identity lacks an organization address. Peer review not accepted."));
522 return;
523 }
524
526 {
527 await Response.SendResponse(new BadRequestException("Reviewer identity lacks an organization Postal Code (ZIP). Peer review not accepted."));
528 return;
529 }
530
532 {
533 await Response.SendResponse(new BadRequestException("Reviewer identity lacks an organization Area. Peer review not accepted."));
534 return;
535 }
536
538 {
539 await Response.SendResponse(new BadRequestException("Reviewer identity lacks an organization City. Peer review not accepted."));
540 return;
541 }
542
544 {
545 await Response.SendResponse(new BadRequestException("Reviewer identity lacks an organization Region. Peer review not accepted."));
546 return;
547 }
548
550 {
551 await Response.SendResponse(new BadRequestException("Reviewer identity lacks an organization Country. Peer review not accepted."));
552 return;
553 }
554
555 if (!CaseInsensitiveString.IsNullOrEmpty(RI.Country))
556 {
558 {
559 await Response.SendResponse(new BadRequestException("Reviewer organization Country is not an ISO 3166-1 Country Code. Peer review not accepted."));
560 return;
561 }
562 }
563 }
564
565 if (!await CheckPeerReviewFields(ReviewedIdentity, Response))
566 return;
567
568 global::Paiwise.PersonalInformation PI = GetPersonalInformation(ReviewedIdentity);
569
570 if (PI.HasOrg)
571 {
572 if (!RI.HasOrg)
573 {
574 await Response.SendResponse(new BadRequestException("Peer reviewer lacks organization information. Peer review not accepted."));
575 return;
576 }
577
578 if (PI.OrgName != RI.OrgName ||
579 PI.OrgNumber != RI.OrgNumber ||
580 PI.OrgCountry != RI.OrgCountry)
581 {
582 await Response.SendResponse(new BadRequestException("Peer reviewer must be from same company. Peer review not accepted."));
583 return;
584 }
585 }
586
587 if (!(Identity.Attachments is null))
588 {
589 LegalIdentity[] Reviewers2 = await GetPeerReviewers(Identity);
590
591 foreach (LegalIdentity ReviewerID in Reviewers2)
592 {
593 if (ReviewerID[PersonalInformation.PersonalNumberTag] == Pnr)
594 {
595 await Response.SendResponse(new BadRequestException("A reviewer can only review the application once."));
596 return;
597 }
598 }
599
600 Reviewers ??= new List<LegalIdentity>();
601 Reviewers.AddRange(Reviewers2);
602 }
603 }
604
605 Reviewers ??= new List<LegalIdentity>();
606 Reviewers.Add(ReviewerIdentity);
607 IncNrPeerReviews = true;
608
609 sb.Clear();
610 }
611 }
612 else
613 Doc = null;
614
615 using TemporaryStream TempFile = new TemporaryStream();
616
617 await TempFile.WriteAsync(Attachment, 0, Attachment.Length);
618
619 KeyValuePair<Attachment, AttachmentReference> A =
620 await CreateAttachment(AttachmentFileName, Identity, AttachmentSignature,
621 TempFile, AttachmentContentType, User.Account, null,
622 XmppServerModule.Legal?.AttachmentsFolder ?? "Attachments",
623 Identity.To.ToUniversalTime(), false);
624
625 // TODO: ID Preview
626
627 List<AttachmentReference> References = new List<AttachmentReference>();
628 bool Approved = false;
629
630 if (!(Identity.Attachments is null))
631 References.AddRange(Identity.Attachments);
632
633 References.Add(A.Value);
634 Identity.Attachments = References.ToArray();
635 Identity.Updated = LegalComponent.UtcNowSecond;
636
637 if (IncNrPeerReviews)
638 {
639 Identity.NrPeerReviews++;
640
642 Identity.NrPeerReviews >= (PeerReviewConfiguration.Instance?.NrReviewersToApprove ?? 2) &&
643 Identity.State == IoTBroker.Legal.Identity.IdentityState.Created)
644 {
645 Identity.State = IoTBroker.Legal.Identity.IdentityState.Approved;
646 Approved = true;
647
648 await RuntimeCounters.IncrementCounter("Legal.ID." + Identity.State.ToString());
649
651 {
652 StringBuilder Markdown = new StringBuilder();
653
654 Markdown.AppendLine("Peer reviewed Legal ID approved");
655 Markdown.AppendLine("===================================");
656 Markdown.AppendLine();
657
658 XmppServerModule.Legal.AppendMarkdown(Markdown, Identity, "Applicant");
659
660 int Index = 0;
661
662 foreach (LegalIdentity Reviewer in Reviewers)
663 {
664 Index++;
665 XmppServerModule.Legal.AppendMarkdown(Markdown, Reviewer, "Reviewer " + Index.ToString());
666 }
667
668 await Gateway.SendNotification(Markdown.ToString());
669 }
670 }
671 }
672
673 Identity.Sign(XmppServerModule.Legal);
674 await Database.Update(Identity);
675
676 if (Identity.State == IoTBroker.Legal.Identity.IdentityState.Approved)
677 await CopyPropertiesFromApprovedIdentity(Identity, Account as DataStorage.Account);
678
679 string BareJid = User.UserName + "@" + Gateway.Domain;
680
681 Log.Informational("Attachment added to Legal Identity registration.",
682 Identity.Id.Value, BareJid, "LegalIdUpdated", Identity.GetTags());
683
684 sb.Clear();
685 Identity.Serialize(sb, true, true, true, true, true, true, true, null, XmppServerModule.Legal);
686 string IdentityXml = sb.ToString();
687
688 if (Approved)
689 await LegalComponent.AddLegalIdentityReference(Identity);
690
691 if (Approved || Identity.State == IoTBroker.Legal.Identity.IdentityState.Approved)
692 {
693 XmppAddress IdentityAddress = new XmppAddress(Identity.Id);
694 int i = IdentityAddress.Domain.IndexOf('.');
695 string JidDomain = i < 0 ? XmppServerModule.Server.Domain : IdentityAddress.Domain.Substring(i + 1);
696
697 await XmppServerModule.Server.SendMessage(string.Empty, string.Empty, XmppServerModule.Legal.MainDomain,
698 new XmppAddress(Identity.Account + "@" + XmppServerModule.Server.Domain), string.Empty, IdentityXml);
699 }
700
701 XmlDocument IdentityDoc = XML.ParseXml(IdentityXml, true);
702
703 await Response.Return(new NamedDictionary<string, object>("IdentityResponse", AgentNamespace)
704 {
705 { "Identity", IdentityDoc },
706 });
707
708 }
709 finally
710 {
711 if (DecodedAttachment is IDisposableAsync DisposableAsync)
712 await DisposableAsync.DisposeAsync();
713 else if (DecodedAttachment is IDisposable Disposable)
714 Disposable.Dispose();
715 }
716 }
717
718 internal static async Task<bool> CheckPeerReviewFields(LegalIdentity ReviewedIdentity, HttpResponse Response)
719 {
721 {
722 global::Paiwise.PersonalInformation PI = GetPersonalInformation(ReviewedIdentity);
723
725 {
726 await Response.SendResponse(new BadRequestException("First name (FIRST) is a required field."));
727 return false;
728 }
729
731 {
732 await Response.SendResponse(new BadRequestException("Middle name (MIDDLE) is a required field."));
733 return false;
734 }
735
737 {
738 await Response.SendResponse(new BadRequestException("Last name (LAST) is a required field."));
739 return false;
740 }
741
743 {
744 await Response.SendResponse(new BadRequestException("Personal Number (PNR) is a required field."));
745 return false;
746 }
747
748
750 {
751 await Response.SendResponse(new BadRequestException("Address (ADDR) is a required field."));
752 return false;
753 }
754
756 {
757 await Response.SendResponse(new BadRequestException("Postal Code (ZIP) is a required field."));
758 return false;
759 }
760
762 {
763 await Response.SendResponse(new BadRequestException("Area (AREA) is a required field."));
764 return false;
765 }
766
768 {
769 await Response.SendResponse(new BadRequestException("City (CITY) is a required field."));
770 return false;
771 }
772
774 {
775 await Response.SendResponse(new BadRequestException("Region (REGION) is a required field."));
776 return false;
777 }
778
780 {
781 await Response.SendResponse(new BadRequestException("Country (COUNTRY) is a required field."));
782 return false;
783 }
784
785 if (!CaseInsensitiveString.IsNullOrEmpty(PI.Country))
786 {
788 !Iso3166.TryGetCountry(PI.Country, out string _))
789 {
790 await Response.SendResponse(new BadRequestException("Country must be a ISO 3166-1 Country Code."));
791 return false;
792 }
793
794 if (!CaseInsensitiveString.IsNullOrEmpty(PI.PersonalNumber))
795 {
796 bool? Valid = await PersonalNumberSchemes.IsValid(PI.Country, PI.PersonalNumber);
797 if (Valid.HasValue && !Valid.Value)
798 {
799 await Response.SendResponse(new BadRequestException("The personal number format does not comply with regulations in your country."));
800 return false;
801 }
802 }
803 }
804
806 {
807 await Response.SendResponse(new BadRequestException("Nationality (NATIONALITY) is a required field."));
808 return false;
809 }
810
811 if (PeerReviewConfiguration.Instance.RequireGender && !PI.Gender.HasValue)
812 {
813 await Response.SendResponse(new BadRequestException("Gender (GENDER) is a required field."));
814 return false;
815 }
816
817 if (PeerReviewConfiguration.Instance.RequireBirthDate && !PI.BirthDate.HasValue)
818 {
819 await Response.SendResponse(new BadRequestException("Birth Date (BDAY, BMONTH & BYEAR) are required fields."));
820 return false;
821 }
822
823 if (PI.HasOrg)
824 {
825 if (CaseInsensitiveString.IsNullOrEmpty(PI.OrgName)) // TODO: Make configurable
826 {
827 await Response.SendResponse(new BadRequestException("Organization name is a required field for work identities."));
828 return false;
829 }
830
831 if (CaseInsensitiveString.IsNullOrEmpty(PI.OrgDepartment)) // TODO: Make configurable
832 {
833 await Response.SendResponse(new BadRequestException("Department is a required field for work identities."));
834 return false;
835 }
836
837 if (CaseInsensitiveString.IsNullOrEmpty(PI.OrgRole)) // TODO: Make configurable
838 {
839 await Response.SendResponse(new BadRequestException("Role is a required field for work identites."));
840 return false;
841 }
842
844 {
845 await Response.SendResponse(new BadRequestException("Organization Number is a required field for work identites."));
846 return false;
847 }
848
850 {
851 await Response.SendResponse(new BadRequestException("Organization Address is a required field for work identites."));
852 return false;
853 }
854
856 {
857 await Response.SendResponse(new BadRequestException("Organization Postal Code (ZIP) is a required field for work identites."));
858 return false;
859 }
860
862 {
863 await Response.SendResponse(new BadRequestException("Organization Area is a required field for work identites."));
864 return false;
865 }
866
868 {
869 await Response.SendResponse(new BadRequestException("Organization City is a required field for work identites."));
870 return false;
871 }
872
874 {
875 await Response.SendResponse(new BadRequestException("Organization Region is a required field for work identites."));
876 return false;
877 }
878
880 {
881 await Response.SendResponse(new BadRequestException("Organization Country is a required field for work identites."));
882 return false;
883 }
884
885 if (!CaseInsensitiveString.IsNullOrEmpty(PI.OrgCountry))
886 {
888 {
889 await Response.SendResponse(new BadRequestException("Organization Country must be a ISO 3166-1 Country Code."));
890 return false;
891 }
892 }
893 }
894 }
895
896 return true;
897 }
898
899 }
900}
Contains personal information found in a legal identity.
Contains information about a response to a content request.
bool HasError
If an error occurred.
object Decoded
Decoded object.
Exception Error
Error response.
Static class managing encoding and decoding of internet content.
static Task< ContentResponse > 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 ...
Helps with common XML-related tasks.
Definition: XML.cs:21
static string Attribute(XmlElement E, string Name)
Gets the value of an XML attribute.
Definition: XML.cs:1062
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 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 Task SendNotification(Graph Graph)
Sends a graph as a notification message to configured notification recipients.
Definition: Gateway.cs:4677
static Task RegisterNonceValue(string Nonce)
Registers a nonce value.
Definition: Gateway.cs:6351
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
async Task SendResponse()
Sends the response back to the client. If the resource is synchronous, there's no need to call this m...
Task Return(Exception ex)
Returns an error to the client.
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:87
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:3862
CaseInsensitiveString Domain
Domain name.
Definition: XmppServer.cs:922
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:21
static async Task Update(object Object)
Updates an object in the database.
Definition: Database.cs:1211
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.
Static class managing persistent counters.
static Task< long > IncrementCounter(CaseInsensitiveString Key)
Increments a counter.
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
Manages a temporary stream. Contents is kept in-memory, if below a memory threshold,...
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:17
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:91
Euler's number.
Definition: E.cs:12
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
Contains information about a broker account.
Definition: Account.cs:41
bool Enabled
If account is enabled
Definition: Account.cs:354
string Password
Password of account
Definition: Account.cs:151
Account()
Contains information about a broker account.
Definition: Account.cs:114
Provides the user with options to control notifications from the Broker.
static BrokerNotificationConfiguration Instance
Current instance of configuration.
bool PeerReviewApproved
If a notification should be sent when a peer review of a Legal ID has been approved.
Provides the user configuration options regarding peer-review of new legal identities.
bool AllowPeerReview
If peer-review is allowed on the broker.
bool RequireFirstName
If first name is required in applications for them to be peer-reviewable.
int NrReviewersToApprove
Number of peers required to review and approve a legal identity application before it can be approved...
bool RequireBirthDate
If birth date is required in applications for them to be peer-reviewable.
int NrPhotosRequired
Number of photos required in an application for it to be peer-reviewable.
bool RequirePostalCode
If postal code is required in applications for them to be peer-reviewable.
bool IsReviewerAllowed(string Jid, string LegalId)
Checks if a reviewer is allowed to perform peer review, based on the white-list, if configured.
bool RequireCity
If city is required in applications for them to be peer-reviewable.
static PeerReviewConfiguration Instance
Current instance of configuration.
bool RequireNationality
If nationality is required in applications for them to be peer-reviewable.
bool RequireMiddleName
If middle name(s) is/are required in applications for them to be peer-reviewable.
bool RequireRegion
If region is required in applications for them to be peer-reviewable.
bool RequireIso3166Compliance
If country codes are required to be ISO 3166 compliant in applications for them to be peer-reviewable...
bool RequireCountry
If country is required in applications for them to be peer-reviewable.
bool RequirePersonalNumber
If personal number is required in applications for them to be peer-reviewable.
bool RequireAddress
If address is required in applications for them to be peer-reviewable.
bool RequireLastName
If last names is/are is required in applications for them to be peer-reviewable.
bool RequireArea
If area is required in applications for them to be peer-reviewable.
bool RequireGender
If gender is required in applications for them to be peer-reviewable.
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 asynchronously disposable objects.
Interface for XMPP user accounts.
Definition: IAccount.cs:9
Definition: ImplTypes.g.cs:58
Definition: App.xaml.cs:4