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;
2using System.Collections.Generic;
3using System.Text;
4using System.Threading.Tasks;
5using System.Xml;
6using Waher.Content;
15using Waher.Script;
17using Waher.Security;
22
24{
29 {
33 public SignContract()
34 : base("Legal/SignContract",
35 new KeyValuePair<Type, Expression>(typeof(Dictionary<string, object>), new Expression(jsonPattern)),
36 new KeyValuePair<Type, Expression>(typeof(XmlDocument), new Expression(xmlPattern)))
37 {
38 }
39
40 private static readonly string jsonPattern = Resources.LoadResourceAsText(typeof(SignContract).Namespace + ".JSON.SignContract.req");
41 private static readonly string xmlPattern = Resources.LoadResourceAsText(typeof(SignContract).Namespace + ".XML.SignContract.req");
42
47 public override bool Synchronous => false;
48
57 public override async Task POST(HttpRequest Request, HttpResponse Response, Dictionary<string, IElement> Parameters)
58 {
60
61 string KeyId = (string)Parameters["PKeyId"].AssociatedObjectValue;
62 CaseInsensitiveString LegalId = (string)Parameters["PLegalId"].AssociatedObjectValue;
63 CaseInsensitiveString ContractId = (string)Parameters["PContractId"].AssociatedObjectValue;
64 string Role = (string)Parameters["PRole"].AssociatedObjectValue;
65 string Nonce = (string)Parameters["PNonce"].AssociatedObjectValue;
66 string KeySignature = (string)Parameters["PKeySignature"].AssociatedObjectValue;
67 string RequestSignature = (string)Parameters["PRequestSignature"].AssociatedObjectValue;
68
69 if (string.IsNullOrEmpty(KeyId))
70 throw new BadRequestException("Key ID cannot be empty.");
71
72 if (string.IsNullOrEmpty(Nonce) || Nonce.Length < 32)
73 throw new ForbiddenException("Nonce too short.");
74
75 LegalIdentity Identity = await LegalComponent.GetLocalLegalIdentity(LegalId)
76 ?? throw new NotFoundException("Legal identity not found.");
77
78 if (Identity.Account != User.UserName)
79 throw new ForbiddenException("Only allowed to add attachments to your own legal identities.");
80
81 if (Identity.State != IdentityState.Approved)
82 throw new ForbiddenException("Legal Identity not approved.");
83
84 AgentKey AgentKey = await Database.FindFirstDeleteRest<AgentKey>(new FilterAnd(
85 new FilterFieldEqualTo("Account", User.UserName),
86 new FilterFieldEqualTo("Id", KeyId)))
87 ?? throw new NotFoundException("Key not found.");
88
89 StringBuilder sb = new StringBuilder();
90
91 sb.Append(User.UserName);
92 sb.Append(':');
93 sb.Append(Request.Header.Host.Value);
94 sb.Append(':');
95 sb.Append(AgentKey.LocalName);
96 sb.Append(':');
97 sb.Append(AgentKey.Namespace);
98 sb.Append(':');
99 sb.Append(KeyId);
100
101 //string s1 = sb.ToString();
102
103 sb.Append(':');
104 sb.Append(KeySignature);
105
106 string s2 = sb.ToString();
107
108 sb.Append(':');
109 sb.Append(Nonce);
110 sb.Append(':');
111 sb.Append(LegalId);
112 sb.Append(':');
113 sb.Append(ContractId);
114 sb.Append(':');
115 sb.Append(Role);
116
117 string s3 = sb.ToString();
118
119 string s = Convert.ToBase64String(
121 Encoding.UTF8.GetBytes(User.Account.Password),
122 Encoding.UTF8.GetBytes(s3)));
123
124 if (s != RequestSignature)
125 {
126 string Msg = "Request Signature invalid.";
127 throw new ForbiddenException(Msg);
128 }
129
130 if (await this.Api.HasNonceBeenUsed(Nonce))
131 {
132 string Msg = "Nonce value has already been used.";
133 throw new ForbiddenException(Msg);
134 }
135
136 await this.Api.RegisterNonceValue(Nonce);
137
138 EllipticCurveEndpoint KeyEndpoint = ApplyId.GetEndpoint(AgentKey, s2);
139 int i = ContractId.IndexOf('@');
140 if (i < 0)
141 throw new BadRequestException("Invalid Contract ID.");
142
143 string LegalDomain = ContractId.Substring(i + 1);
144
145 if (XmppServerModule.Server is null)
146 {
148
149 if (Client is null)
150 {
151 if (Types.TryGetModuleParameter("XMPP", out object Obj) && Obj is XmppClient Client2)
152 Client = Client2;
153 }
154
155 if (Client is null)
156 throw new ServiceUnavailableException("XMPP Client not available.");
157
158 await Client.SendIqGet(LegalDomain, "<getContract id=\"" + XML.Encode(ContractId) + "\" xmlns=\"" +
159 LegalComponent.NamespaceSmartContracts(NamespaceSet.Current) + "\"/>", async (Sender, e) =>
160 {
161 try
162 {
163 XmlElement E;
164
165 if (e.Ok && !((E = e.FirstElement) is null) && E.LocalName == "contract")
166 {
167 Networking.XMPP.Contracts.ParsedContract Contract;
168
169 try
170 {
171 Contract = await Networking.XMPP.Contracts.Contract.Parse(E, Gateway.ContractsClient, true);
172 if (Contract?.Contract is null)
173 {
174 await Response.Return(new InternalServerErrorException("Unable to parse contract."));
175 return;
176 }
177 }
178 catch (Exception ex)
179 {
180 await Response.Return(new InternalServerErrorException("Unable to parse contract: " + ex.Message));
181 return;
182 }
183
184 StringBuilder Xml = new StringBuilder();
185 Contract.Contract.Serialize(Xml, false, false, false, false, false, false, false);
186 byte[] Data = Encoding.UTF8.GetBytes(Xml.ToString());
187
188 byte[] Signature = KeyEndpoint.Sign(Data);
189
190 await Client.SendIqSet(LegalDomain, "<signContract id=\"" + XML.Encode(ContractId) +
191 "\" role=\"" + XML.Encode(Role) + "\" s=\"" +
192 Convert.ToBase64String(Signature) + "\" xmlns=\"" +
193 LegalComponent.NamespaceSmartContracts(NamespaceSet.Current) + "\"/>", async (sender2, e2) =>
194 {
195 try
196 {
197 if (e2.Ok && !((E = e2.FirstElement) is null) && E.LocalName == "contract")
198 {
199 await Response.Return(new NamedDictionary<string, object>("ContractResponse", AgentNamespace)
200 {
201 { "Contract", E }
202 });
203 }
204 else
205 await Response.SendResponse(ToHttpException(XmppClient.GetExceptionObject(e2.ErrorElement)) ?? new ServiceUnavailableException("Unable to get contract."));
206 }
207 catch (Exception ex2)
208 {
209 await Response.SendResponse(ex2);
210 }
211
212 }, null);
213 }
214 else
215 await Response.SendResponse(ToHttpException(e.StanzaError) ?? new ServiceUnavailableException("Unable to get contract."));
216 }
217 catch (Exception ex)
218 {
219 await Response.SendResponse(ex);
220 }
221 }, null);
222 }
223 else
224 {
225 await XmppServerModule.Server.SendIqRequest("get",
226 User.UserName + "@" + (Gateway.Domain?.Value ?? string.Empty),
227 LegalDomain, string.Empty, "<getContract id=\"" + XML.Encode(ContractId) + "\" xmlns=\"" +
228 LegalComponent.NamespaceSmartContracts(NamespaceSet.Current) + "\"/>", async (Sender, e) =>
229 {
230 try
231 {
232 XmlElement E;
233
234 if (e.Ok && !((E = e.FirstElement) is null) && E.LocalName == "contract")
235 {
236 IoTBroker.Legal.Contracts.ParsedContract Contract = await IoTBroker.Legal.Contracts.Contract.Parse(E, XmppServerModule.Legal);
237
238 if (Contract?.Contract is null)
239 {
240 await Response.Return(new InternalServerErrorException("Unable to parse contract."));
241 return;
242 }
243
244 StringBuilder Xml = new StringBuilder();
245 Contract.Contract.Serialize(Xml, false, false, false, false, false, false, false, null, XmppServerModule.Legal);
246 byte[] Data = Encoding.UTF8.GetBytes(Xml.ToString());
247
248 byte[] Signature = KeyEndpoint.Sign(Data);
249
250 await XmppServerModule.Server.SendIqRequest("set",
251 User.UserName + "@" + (Gateway.Domain?.Value ?? string.Empty),
252 LegalDomain, string.Empty, "<signContract id=\"" + XML.Encode(ContractId) +
253 "\" role=\"" + XML.Encode(Role) + "\" s=\"" +
254 Convert.ToBase64String(Signature) + "\" xmlns=\"" +
255 LegalComponent.NamespaceSmartContracts(Contract.Contract.Version) + "\"/>", async (sender2, e2) =>
256 {
257 try
258 {
259 if (e2.Ok && !((E = e2.FirstElement) is null) && E.LocalName == "contract")
260 {
261 await Response.Return(new NamedDictionary<string, object>("ContractResponse", AgentNamespace)
262 {
263 { "Contract", E }
264 });
265 }
266 else
267 await Response.SendResponse(ToHttpException(XmppClient.GetExceptionObject(e2.ErrorElement)) ?? new ServiceUnavailableException("Unable to get contract."));
268 }
269 catch (Exception ex2)
270 {
271 await Response.SendResponse(ex2);
272 }
273
274 }, null);
275 }
276 else
277 await Response.SendResponse(ToHttpException(XmppClient.GetExceptionObject(e.ErrorElement)) ?? new ServiceUnavailableException("Unable to get contract."));
278
279 }
280 catch (Exception ex)
281 {
282 await Response.SendResponse(ex);
283 }
284 }, null);
285 }
286 }
287
288 }
289}
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 Encode(string s)
Encodes a string for use in XML.
Definition: XML.cs:27
Static class managing the runtime environment of the IoT Gateway.
Definition: Gateway.cs:126
static CaseInsensitiveString Domain
Domain name.
Definition: Gateway.cs:2354
static XmppClient XmppClient
XMPP Client connection of gateway.
Definition: Gateway.cs:3187
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 SendResponse()
Sends the response back to the client. If the resource is synchronous, there's no need to call this m...
async Task Return(object Object)
Returns an object to the client. This method can only be called once per response,...
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:59
static XmppException GetExceptionObject(XmlElement StanzaElement)
Gets an XMPP Exception object corresponding to its XML definition.
Definition: XmppClient.cs:3301
Task< uint > SendIqSet(string To, string Xml, EventHandlerAsync< IqResultEventArgs > Callback, object State)
Sends an IQ Set request.
Definition: XmppClient.cs:3607
Task< uint > SendIqGet(string To, string Xml, EventHandlerAsync< IqResultEventArgs > Callback, object State)
Sends an IQ Get request.
Definition: XmppClient.cs:3559
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:19
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 that dynamically manages types and interfaces available in the runtime environment.
Definition: Types.cs:14
static bool TryGetModuleParameter(string Name, out object Value)
Tries to get a module parameter value.
Definition: Types.cs:583
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
string Password
Password of account
Definition: Account.cs:109
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.
static Exception ToHttpException(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