2using System.Collections.Generic;
4using System.Threading.Tasks;
39 : base(
"Legal/AddIdAttachment",
40 new KeyValuePair<Type,
Expression>(typeof(Dictionary<string, object>), new
Expression(jsonPattern)),
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;
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();
70 object DecodedAttachment;
72 if (
string.IsNullOrEmpty(KeyId))
75 if (
string.IsNullOrEmpty(Nonce) || Nonce.Length < 32)
83 if (Identity.Account != User.
UserName)
84 throw new ForbiddenException(
"Only allowed to add attachments to your own legal identities.");
87 throw new ForbiddenException(
"Attachments can only be added to newly created identities before they are approved.");
113 sb.Append(KeySignature);
115 string s2 = sb.ToString();
120 sb.Append(AttachmentBase64);
122 sb.Append(AttachmentFileName);
124 sb.Append(AttachmentContentType);
126 string s3 = sb.ToString();
128 string s = Convert.ToBase64String(
131 Encoding.UTF8.GetBytes(s3)));
133 if (s != RequestSignature)
135 string Msg =
"Request Signature invalid.";
141 string Msg =
"Nonce value has already been used.";
149 Attachment = Convert.FromBase64String(AttachmentBase64);
162 if (!(Identity.Attachments is
null))
164 string AttachmentSignatureBase64 = Convert.ToBase64String(AttachmentSignature);
168 if (Convert.ToBase64String(Ref.
Signature) == s)
173 bool IncNrPeerReviews =
false;
174 List<LegalIdentity> Reviewers =
null;
175 string LegalNamespace = NamespaceLegalIdentity(Identity.Version);
177 if (DecodedAttachment is XmlDocument Doc)
179 if (Doc.DocumentElement.LocalName ==
"peerReview" && Doc.DocumentElement.NamespaceURI == LegalNamespace)
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;
192 foreach (XmlNode N
in Doc.DocumentElement.ChildNodes)
194 if (!(N is XmlElement E) || N.NamespaceURI != LegalNamespace)
197 foreach (XmlNode N2
in E.ChildNodes)
199 if (N2 is XmlElement E2 &&
200 E2.LocalName ==
"identity" &&
201 E2.NamespaceURI == LegalNamespace)
206 ReviewedIdentity =
LegalIdentity.Parse(E2, out ReviewedHasStatus,
207 out Dictionary<string, string> ReviewedIdentityAttachmentsUrls);
210 ReviewedIdentity.Serialize(sb,
true,
true,
true,
false,
false,
false,
false,
null,
XmppServerModule.Legal);
211 string s1 = sb.ToString();
214 Identity.Serialize(sb,
true,
true,
true,
false,
false,
false,
false,
null,
XmppServerModule.Legal);
221 ReviewedIdentity.Serialize(sb,
true,
true,
true,
true,
true,
true,
true, ReviewedIdentityAttachmentsUrls,
XmppServerModule.Legal);
222 SignedIdentity = Encoding.UTF8.GetBytes(sb.ToString());
226 ReviewerIdentity =
LegalIdentity.Parse(E2, out ReviewerHasStatus,
227 out Dictionary<string, string> ReviewerIdentityAttachmentsUrls);
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"];
236 (ReviewerIdentity, _) = await
XmppServerModule.Legal.ValidateSignature(
new XmppAddress(ReviewerJid), TP, Data, ReviewerIdentity.ClientSignature);
237 if (ReviewerIdentity is
null || ReviewerJid != ReviewerIdentity[
"JID"])
244 if (Now.Date.AddDays(1) < ReviewerIdentity.From ||
245 Now.Date.AddDays(-1) > ReviewerIdentity.To)
253 if (
string.IsNullOrEmpty(ReviewerIdentity.ClientKeyName) ||
254 ReviewerIdentity.ClientPubKey is
null ||
255 ReviewerIdentity.ClientPubKey.Length == 0)
260 if (ReviewerIdentity.ClientSignature is
null ||
261 ReviewerIdentity.ClientSignature.Length == 0)
277 if (ReviewedIdentity is
null || !ReviewedHasStatus ||
278 ReviewerIdentity is
null || !ReviewerHasStatus ||
279 SignedIdentity is
null)
281 throw new ForbiddenException(
"Attachment not a correctly formed peer-review document.");
287 throw new ForbiddenException(
"Reviewer cannot be the same person as the reviewed person.");
289 if (!ReviewerIdentity.ValidateSignature(SignedIdentity, PeerSignature))
296 if (!(Identity.Attachments is
null))
307 throw new ForbiddenException(
"Peer review not accepted: Identity lacks sufficient photos (" +
311 PersonalInfo RI = GetPersonalInfo(ReviewerIdentity);
314 throw new BadRequestException(
"Reviewer identity lacks a First name. Peer review not accepted.");
317 throw new BadRequestException(
"Reviewer identity lacks a Middle name. Peer review not accepted.");
320 throw new BadRequestException(
"Reviewer identity lacks a Last name. Peer review not accepted.");
323 throw new BadRequestException(
"Reviewer identity lacks a Personal number. Peer review not accepted.");
326 throw new BadRequestException(
"Reviewer identity lacks an address. Peer review not accepted.");
329 throw new BadRequestException(
"Reviewer identity lacks a Postal Code (ZIP). Peer review not accepted.");
332 throw new BadRequestException(
"Reviewer identity lacks an Area. Peer review not accepted.");
335 throw new BadRequestException(
"Reviewer identity lacks a City. Peer review not accepted.");
338 throw new BadRequestException(
"Reviewer identity lacks a Region. Peer review not accepted.");
341 throw new BadRequestException(
"Reviewer identity lacks a Country. Peer review not accepted.");
346 throw new BadRequestException(
"Reviewer Country is not an ISO 3166-1 Country Code. Peer review not accepted.");
351 if (Valid.HasValue && !Valid.Value)
352 throw new BadRequestException(
"The personal number format used by the reviewer does not comply with regulations.");
357 throw new BadRequestException(
"Reviewer identity lacks a Nationality. Peer review not accepted.");
360 throw new BadRequestException(
"Reviewer identity lacks a Gender. Peer review not accepted.");
363 throw new BadRequestException(
"Reviewer identity lacks a Birth Date. Peer review not accepted.");
368 throw new BadRequestException(
"Reviewer identity lacks an organization name. Peer review not accepted.");
371 throw new BadRequestException(
"Reviewer identity lacks a department. Peer review not accepted.");
374 throw new BadRequestException(
"Reviewer identity lacks a role. Peer review not accepted.");
377 throw new BadRequestException(
"Reviewer identity lacks an organization number. Peer review not accepted.");
380 throw new BadRequestException(
"Reviewer identity lacks an organization address. Peer review not accepted.");
383 throw new BadRequestException(
"Reviewer identity lacks an organization Postal Code (ZIP). Peer review not accepted.");
386 throw new BadRequestException(
"Reviewer identity lacks an organization Area. Peer review not accepted.");
389 throw new BadRequestException(
"Reviewer identity lacks an organization City. Peer review not accepted.");
392 throw new BadRequestException(
"Reviewer identity lacks an organization Region. Peer review not accepted.");
395 throw new BadRequestException(
"Reviewer identity lacks an organization Country. Peer review not accepted.");
400 throw new BadRequestException(
"Reviewer organization Country is not an ISO 3166-1 Country Code. Peer review not accepted.");
404 await CheckPeerReviewFields(ReviewedIdentity);
406 PersonalInfo PI = GetPersonalInfo(ReviewedIdentity);
411 throw new BadRequestException(
"Peer reviewer lacks organization information. Peer review not accepted.");
413 if (PI.OrgName != RI.OrgName ||
414 PI.OrgNumber != RI.OrgNumber ||
415 PI.OrgCountry != RI.OrgCountry)
417 throw new BadRequestException(
"Peer reviewer must be from same company. Peer review not accepted.");
421 if (!(Identity.Attachments is
null))
423 LegalIdentity[] Reviewers2 = await GetPeerReviewers(Identity);
427 if (ReviewerID[
"PNR"] == Pnr)
431 if (Reviewers is
null)
432 Reviewers =
new List<LegalIdentity>();
434 Reviewers.AddRange(Reviewers2);
438 if (Reviewers is
null)
439 Reviewers =
new List<LegalIdentity>();
441 Reviewers.Add(ReviewerIdentity);
442 IncNrPeerReviews =
true;
452 KeyValuePair<Attachment, AttachmentReference> A =
453 await
LegalComponent.CreateAttachment(AttachmentFileName, Identity,
454 AttachmentSignature, TempFile, AttachmentContentType,
457 List<AttachmentReference> References =
new List<AttachmentReference>();
458 bool Approved =
false;
460 if (!(Identity.Attachments is
null))
461 References.AddRange(Identity.Attachments);
463 References.Add(A.Value);
464 Identity.Attachments = References.ToArray();
467 if (IncNrPeerReviews)
469 Identity.NrPeerReviews++;
478 StringBuilder Markdown =
new StringBuilder();
480 Markdown.AppendLine(
"Peer reviewed Legal ID approved");
481 Markdown.AppendLine(
"===================================");
482 Markdown.AppendLine();
491 XmppServerModule.Legal.AppendMarkdown(Markdown, Reviewer,
"Reviewer " + Index.ToString());
502 await CopyPropertiesFromApprovedIdentity(Identity,
Account as DataStorage.
Account);
507 Identity.Id.
Value, BareJid,
"LegalIdUpdated", Identity.GetTags());
510 Identity.Serialize(sb,
true,
true,
true,
true,
true,
true,
true,
null,
XmppServerModule.Legal);
511 string IdentityXml = sb.ToString();
519 string JidDomain = i < 0 ? XmppServerModule.Server.Domain : IdentityAddress.
Domain.
Substring(i + 1);
525 XmlDocument IdentityDoc =
new XmlDocument()
527 PreserveWhitespace =
true
529 IdentityDoc.LoadXml(IdentityXml);
533 {
"Identity", IdentityDoc },
540 if (DecodedAttachment is IDisposable Disposable)
541 Disposable.Dispose();
546 internal static async Task CheckPeerReviewFields(
LegalIdentity ReviewedIdentity)
550 PersonalInfo PI = GetPersonalInfo(ReviewedIdentity);
593 if (Valid.HasValue && !Valid.Value)
594 throw new BadRequestException(
"The personal number format does not comply with regulations in your country.");
610 throw new BadRequestException(
"Organization name is a required field for work identities.");
619 throw new BadRequestException(
"Organization Number is a required field for work identites.");
622 throw new BadRequestException(
"Organization Address is a required field for work identites.");
625 throw new BadRequestException(
"Organization Postal Code (ZIP) is a required field for work identites.");
628 throw new BadRequestException(
"Organization Area is a required field for work identites.");
631 throw new BadRequestException(
"Organization City is a required field for work identites.");
634 throw new BadRequestException(
"Organization Region is a required field for work identites.");
637 throw new BadRequestException(
"Organization Country is a required field for work identites.");
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.
static string LoadResourceAsText(string ResourceName)
Loads a text resource from an embedded resource.
Helps with common XML-related tasks.
static string Attribute(XmlElement E, string Name)
Gets the value of an XML attribute.
Static class managing the application event log. Applications and services log events on this static ...
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.
Static class managing the runtime environment of the IoT Gateway.
static CaseInsensitiveString Domain
Domain name.
static Task SendNotification(Graph Graph)
Sends a graph as a notification message to configured notification recipients.
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...
Represents an HTTP request.
HttpRequestHeader Header
Request header.
Represets a response of an HTTP client request.
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
Contains information about one XMPP address.
CaseInsensitiveString Domain
Domain
static readonly XmppAddress Empty
Empty address.
Task< bool > SendMessage(string Type, string Id, string From, string To, string Language, string ContentXml)
Sends a Message stanza to a recipient.
CaseInsensitiveString Domain
Domain name.
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...
static async Task Update(object Object)
Updates an object in the database.
This filter selects objects that conform to all child-filters provided.
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...
Static class of application-wide semaphores that can be used to order access to editable objects.
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...
Class managing a script expression.
Contains methods for simple hash calculations.
static byte[] ComputeHMACSHA256Hash(byte[] Key, byte[] Data)
Computes the HMAC-SHA-256 hash of a block of binary data.
Contains information about a broker account.
bool Enabled
If account is enabled
string Password
Password of account
Account()
Contains information about a broker account.
Account Account
Account object.
string UserName
User Name.
Represents an attachment to a document.
Represents an attachment to a document.
string ContentType
Internet Content Type of binary attachment.
byte[] Signature
Binary signature of the attachment, generated by an approved legal identity of the account-holder....
Data in accordance with ISO 3166
static bool TryGetCountry(string Code, out string Country)
Tries to get the Country Name from a ISO 3166-1 Country Code. String comparisons are case-insensitive...
Personal Number Schemes available in different countries.
static async Task< bool?> IsValid(string CountryCode, string PersonalNumber)
Checks if a personal number is valid, in accordance with registered personal number schemes.
Provisioning and registry service component.
string AttachmentsFolder
Attachments folder.
static DateTime NowSecond
Current Date & Time, to a precision of one second.
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.
Task RegisterNonceValue(string Nonce)
Registers a nonce value.
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.
AgentApi Api
Reference to Agent API.
const string AgentNamespace
https://waher.se/Schema/BrokerAgent.xsd
Contains an encrypted key for an agent.
string Namespace
Namespace of algorithm
string LocalName
Local Name of algorithm
Adds an attachment to a new Legal ID
AddIdAttachment()
Adds an attachment to a new Legal ID
override async Task POST(HttpRequest Request, HttpResponse Response, Dictionary< string, IElement > Parameters)
Executes the POST method on the resource.
Applies for a new Legal ID
Service Module hosting the XMPP broker and its components.
Interface for XMPP user accounts.
IdentityState
Lists recognized legal identity states.