Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
SignContract.cs
1using System;
3using System.Text;
4using System.Threading.Tasks;
5using System.Xml;
6using Waher.Content;
8using Waher.Events;
17using Waher.Script;
19using Waher.Security;
24
26{
31 {
35 public SignContract()
36 : base("Legal/SignContract",
37 new KeyValuePair<Type, Expression>(typeof(Dictionary<string, object>), new Expression(jsonPattern)),
38 new KeyValuePair<Type, Expression>(typeof(XmlDocument), new Expression(xmlPattern)))
39 {
40 }
41
42 private static readonly string jsonPattern = Resources.LoadResourceAsText(typeof(SignContract).Namespace + ".JSON.SignContract.req");
43 private static readonly string xmlPattern = Resources.LoadResourceAsText(typeof(SignContract).Namespace + ".XML.SignContract.req");
44
49 public override bool Synchronous => false;
50
59 public override async Task POST(HttpRequest Request, HttpResponse Response, Dictionary<string, IElement> Parameters)
60 {
62
63 string KeyId = (string)Parameters["PKeyId"].AssociatedObjectValue;
64 CaseInsensitiveString LegalId = (string)Parameters["PLegalId"].AssociatedObjectValue;
65 CaseInsensitiveString ContractId = (string)Parameters["PContractId"].AssociatedObjectValue;
66 string Role = (string)Parameters["PRole"].AssociatedObjectValue;
67 string Nonce = (string)Parameters["PNonce"].AssociatedObjectValue;
68 string KeySignature = (string)Parameters["PKeySignature"].AssociatedObjectValue;
69 string RequestSignature = (string)Parameters["PRequestSignature"].AssociatedObjectValue;
70
71 if (string.IsNullOrEmpty(KeyId))
72 throw new BadRequestException("Key ID cannot be empty.");
73
74 if (string.IsNullOrEmpty(Nonce) || Nonce.Length < 32)
75 throw new ForbiddenException(Request, "Nonce too short.");
76
77 LegalIdentity Identity = await LegalComponent.GetLocalLegalIdentity(LegalId)
78 ?? throw new NotFoundException("Legal identity not found.");
79
80 if (Identity.Account != User.UserName)
81 throw new ForbiddenException(Request, "Only allowed to add attachments to your own legal identities.");
82
83 if (Identity.State != IoTBroker.Legal.Identity.IdentityState.Approved)
84 throw new ForbiddenException(Request, "Legal Identity not approved.");
85
86 AgentKey AgentKey = await Database.FindFirstDeleteRest<AgentKey>(new FilterAnd(
87 new FilterFieldEqualTo("Account", User.UserName),
88 new FilterFieldEqualTo("Id", KeyId)))
89 ?? throw new NotFoundException("Key not found.");
90
91 StringBuilder sb = new StringBuilder();
92
93 sb.Append(User.UserName);
94 sb.Append(':');
95 sb.Append(Request.Header.Host.Value);
96 sb.Append(':');
97 sb.Append(AgentKey.LocalName);
98 sb.Append(':');
99 sb.Append(AgentKey.Namespace);
100 sb.Append(':');
101 sb.Append(KeyId);
102
103 //string s1 = sb.ToString();
104
105 sb.Append(':');
106 sb.Append(KeySignature);
107
108 string s2 = sb.ToString();
109
110 sb.Append(':');
111 sb.Append(Nonce);
112 sb.Append(':');
113 sb.Append(LegalId);
114 sb.Append(':');
115 sb.Append(ContractId);
116 sb.Append(':');
117 sb.Append(Role);
118
119 string s3 = sb.ToString();
120
121 string s = Convert.ToBase64String(
123 Encoding.UTF8.GetBytes(User.Account.Password),
124 Encoding.UTF8.GetBytes(s3)));
125
126 if (s != RequestSignature)
127 {
128 string Msg = "Request Signature invalid.";
129 throw new ForbiddenException(Request, Msg);
130 }
131
132 if (await Gateway.HasNonceBeenUsed(Nonce))
133 {
134 string Msg = "Nonce value has already been used.";
135 throw new ForbiddenException(Request, Msg);
136 }
137
138 await Gateway.RegisterNonceValue(Nonce);
139
140 EllipticCurveEndpoint KeyEndpoint = ApplyId.GetEndpoint(Request, AgentKey, s2);
141 int i = ContractId.IndexOf('@');
142 if (i < 0)
143 throw new BadRequestException("Invalid Contract ID.");
144
145 string LegalDomain = ContractId.Substring(i + 1);
146
147 if (XmppServerModule.Server is null)
148 {
150
151 if (Client is null)
152 {
153 if (Types.TryGetModuleParameter("XMPP", out XmppClient Client2))
154 Client = Client2;
155 }
156
157 if (Client is null)
158 throw new ServiceUnavailableException("XMPP Client not available.");
159
160 await Client.SendIqGet(LegalDomain, "<getContract id=\"" + XML.Encode(ContractId) + "\" xmlns=\"" +
161 LegalComponent.NamespaceSmartContracts(NamespaceSet.Current) + "\"/>", async (Sender, e) =>
162 {
163 try
164 {
165 XmlElement E;
166
167 if (e.Ok && !((E = e.FirstElement) is null) && E.LocalName == "contract")
168 {
169 Networking.XMPP.Contracts.ParsedContract Contract;
170
171 try
172 {
173 Contract = await Networking.XMPP.Contracts.Contract.Parse(E, Gateway.ContractsClient, true);
174 if (Contract?.Contract is null)
175 {
176 await Response.SendResponse(new InternalServerErrorException("Unable to parse contract."));
177 return;
178 }
179 }
180 catch (Exception ex)
181 {
182 await Response.SendResponse(new InternalServerErrorException("Unable to parse contract: " + Log.UnnestException(ex).Message));
183 return;
184 }
185
186 StringBuilder Xml = new StringBuilder();
187 Contract.Contract.Serialize(Xml, false, false, false, false, false, false, false);
188 byte[] Data = Encoding.UTF8.GetBytes(Xml.ToString());
189
190 byte[] Signature = KeyEndpoint.Sign(Data);
191
192 await Client.SendIqSet(LegalDomain, "<signContract id=\"" + XML.Encode(ContractId) +
193 "\" role=\"" + XML.Encode(Role) + "\" s=\"" +
194 Convert.ToBase64String(Signature) + "\" xmlns=\"" +
195 LegalComponent.NamespaceSmartContracts(NamespaceSet.Current) + "\"/>", async (sender2, e2) =>
196 {
197 try
198 {
199 if (e2.Ok && !((E = e2.FirstElement) is null) && E.LocalName == "contract")
200 {
201 await Response.Return(new NamedDictionary<string, object>("ContractResponse", AgentNamespace)
202 {
203 { "Contract", E }
204 });
205 }
206 else
207 await Response.SendResponse(ToHttpException(Request, XmppClient.GetExceptionObject(e2.ErrorElement)) ?? new ServiceUnavailableException("Unable to get contract."));
208 }
209 catch (Exception ex2)
210 {
211 await Response.SendResponse(ex2);
212 }
213
214 }, null);
215 }
216 else
217 await Response.SendResponse(ToHttpException(Request, e.StanzaError) ?? new ServiceUnavailableException("Unable to get contract."));
218 }
219 catch (Exception ex)
220 {
221 await Response.SendResponse(ex);
222 }
223 }, null);
224 }
225 else
226 {
227 await XmppServerModule.Server.SendIqRequest("get",
228 User.UserName + "@" + (Gateway.Domain?.Value ?? string.Empty),
229 LegalDomain, string.Empty, "<getContract id=\"" + XML.Encode(ContractId) + "\" xmlns=\"" +
230 LegalComponent.NamespaceSmartContracts(NamespaceSet.Current) + "\"/>", true, async (Sender, e) =>
231 {
232 try
233 {
234 XmlElement E;
235
236 if (e.Ok && !((E = e.FirstElement) is null) && E.LocalName == "contract")
237 {
238 IoTBroker.Legal.Contracts.ParsedContract Contract = await IoTBroker.Legal.Contracts.Contract.Parse(E, XmppServerModule.Legal);
239
240 if (Contract?.Contract is null)
241 {
242 await Response.SendResponse(new InternalServerErrorException("Unable to parse contract."));
243 return;
244 }
245
246 StringBuilder Xml = new StringBuilder();
247 await Contract.Contract.Serialize(Xml, false, false, false, false, false, false, false, null, XmppServerModule.Legal);
248 byte[] Data = Encoding.UTF8.GetBytes(Xml.ToString());
249
250 byte[] Signature = KeyEndpoint.Sign(Data);
251
252 await XmppServerModule.Server.SendIqRequest("set",
253 User.UserName + "@" + (Gateway.Domain?.Value ?? string.Empty),
254 LegalDomain, string.Empty, "<signContract id=\"" + XML.Encode(ContractId) +
255 "\" role=\"" + XML.Encode(Role) + "\" s=\"" +
256 Convert.ToBase64String(Signature) + "\" xmlns=\"" +
257 LegalComponent.NamespaceSmartContracts(Contract.Contract.Version) + "\"/>", false, async (sender2, e2) =>
258 {
259 try
260 {
261 if (e2.Ok && !((E = e2.FirstElement) is null) && E.LocalName == "contract")
262 {
263 await Response.Return(new NamedDictionary<string, object>("ContractResponse", AgentNamespace)
264 {
265 { "Contract", E }
266 });
267 }
268 else
269 await Response.SendResponse(ToHttpException(Request, XmppClient.GetExceptionObject(e2.ErrorElement)) ?? new ServiceUnavailableException("Unable to get contract."));
270 }
271 catch (Exception ex2)
272 {
273 await Response.SendResponse(ex2);
274 }
275
276 }, null);
277 }
278 else
279 await Response.SendResponse(ToHttpException(Request, XmppClient.GetExceptionObject(e.ErrorElement)) ?? new ServiceUnavailableException("Unable to get contract."));
280
281 }
282 catch (Exception ex)
283 {
284 await Response.SendResponse(ex);
285 }
286 }, null);
287 }
288 }
289
290 }
291}
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 class managing the application event log. Applications and services log events on this static ...
Definition: Log.cs:14
static Exception UnnestException(Exception Exception)
Unnests an exception, to extract the relevant inner exception.
Definition: Log.cs:828
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 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
async Task SendResponse()
Sends the response back to the client. If the resource is synchronous, there's no need to call this m...
The server encountered an unexpected condition which prevented it from fulfilling the request.
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...
Abstract base class for Elliptic Curve endpoints.
override byte[] Sign(byte[] Data)
Signs binary data using the local private key.
Manages an XMPP client connection. Implements XMPP, as defined in https://tools.ietf....
Definition: XmppClient.cs:58
static XmppException GetExceptionObject(XmlElement StanzaElement)
Gets an XMPP Exception object corresponding to its XML definition.
Definition: XmppClient.cs:3340
Task< uint > SendIqSet(string To, string Xml, EventHandlerAsync< IqResultEventArgs > Callback, object State)
Sends an IQ Set request.
Definition: XmppClient.cs:3646
Task< uint > SendIqGet(string To, string Xml, EventHandlerAsync< IqResultEventArgs > Callback, object State)
Sends an IQ Get request.
Definition: XmppClient.cs:3598
Represents a case-insensitive string.
string Value
String-representation of the case-insensitive string. (Representation is case sensitive....
int IndexOf(CaseInsensitiveString value, StringComparison comparisonType)
Reports the zero-based index of the first occurrence of the specified string in the current System....
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
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 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
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
string Password
Password of account
Definition: Account.cs:151
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.
static Exception ToHttpException(HttpRequest Request, XmppException ex)
Tries to convert an XMPP Exception to an HTTP Exception.
Contains an encrypted key for an agent.
Definition: AgentKey.cs:13
Service Module hosting the XMPP broker and its components.
NamespaceSet
Namespace versions
Definition: NamespaceSet.cs:7