Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
AgentResource.cs
1using System;
2using System.Collections.Generic;
3using System.Text;
4using System.Threading.Tasks;
12using Waher.Script;
17
19{
24 {
28 public const string AgentNamespace = "https://waher.se/Schema/BrokerAgent.xsd";
29
30 private readonly Dictionary<Type, Expression> patternMatches;
31 private AgentApi api;
32
38 public AgentResource(string AgentResourceName,
39 params KeyValuePair<Type, Expression>[] PatternMatches)
40 : base("/Agent/" + AgentResourceName)
41 {
42 this.patternMatches = new Dictionary<Type, Expression>();
43
44 foreach (KeyValuePair<Type, Expression> P in PatternMatches)
45 this.patternMatches[P.Key] = P.Value;
46 }
47
53 public Task Register(HttpServer WebServer, AgentApi AgentApi)
54 {
55 this.api = AgentApi;
56 WebServer.Register(this);
57 return Task.CompletedTask;
58 }
59
64 public Task Unregister(HttpServer WebServer)
65 {
66 WebServer.Unregister(this);
67 return Task.CompletedTask;
68 }
69
73 public override bool HandlesSubPaths => false;
74
78 public override bool UserSessions => false;
79
86 {
87 if (this.authenticationSchemes is null)
88 {
89 if (accounts is null)
90 {
91 PersistenceLayer PersistenceLayer = XmppServerModule.PersistenceLayer ?? new PersistenceLayer();
92 accounts = new Accounts(PersistenceLayer);
93 }
94
95 List<HttpAuthenticationScheme> Schemes = new List<HttpAuthenticationScheme>();
96
97 this.AddAuthenticationSchemes(accounts, Schemes);
98
99 if (XmppServerModule.PersistenceLayer is null)
100 return Schemes.ToArray();
101
102 this.authenticationSchemes = Schemes.ToArray();
103 }
104
105 return this.authenticationSchemes;
106 }
107
113 protected virtual void AddAuthenticationSchemes(Accounts Accounts, List<HttpAuthenticationScheme> Schemes)
114 {
115 bool RequireEncryption = !(DomainConfiguration.Instance is null) &&
117 !string.IsNullOrEmpty(DomainConfiguration.Instance.Domain);
118 int MinStrength = RequireEncryption ? 128 : 0;
119
120 Schemes.Add(new JwtAuthentication(RequireEncryption, MinStrength, Gateway.Domain, Accounts, Factory));
121
122 if (!(Gateway.HttpServer is null) &&
123 Gateway.HttpServer.ClientCertificates != ClientCertificates.NotUsed)
124 {
125 Schemes.Add(new MutualTlsAuthentication(Accounts));
126 }
127
128 Schemes.Add(new BasicAuthentication(RequireEncryption, MinStrength, Gateway.Domain, Accounts));
129 Schemes.Add(new DigestAuthentication(RequireEncryption, MinStrength, DigestAlgorithm.MD5, Gateway.Domain, Accounts));
130 Schemes.Add(new DigestAuthentication(RequireEncryption, MinStrength, DigestAlgorithm.SHA256, Gateway.Domain, Accounts));
131 Schemes.Add(new DigestAuthentication(RequireEncryption, MinStrength, DigestAlgorithm.SHA3_256, Gateway.Domain, Accounts));
132 }
133
134 private static Accounts accounts = null;
135 private static JwtFactory factory = null;
136 private HttpAuthenticationScheme[] authenticationSchemes = null;
137
141 protected AgentApi Api => this.api;
142
146 internal static JwtFactory Factory
147 {
148 get
149 {
150 if (factory is null)
151 {
152 if (Types.TryGetModuleParameter("JWT", out object Obj) &&
153 Obj is JwtFactory JwtFactory &&
155 {
156 factory = JwtFactory;
157 }
158 else
159 factory = JwtFactory.CreateHmacSha256();
160 }
161
162 return factory;
163 }
164
165 set => factory = value;
166 }
167
171 protected static Accounts Accounts => accounts;
172
179 protected async Task<Dictionary<string, IElement>> AssertMatch(HttpRequest Request)
180 {
181 if (!Request.HasData)
182 throw new BadRequestException("No content is request.");
183
184 object Decoded = await Request.DecodeDataAsync();
185 Type T = Decoded.GetType();
186
187 if (!this.patternMatches.TryGetValue(T, out Expression Pattern))
188 throw new UnsupportedMediaTypeException("Unhandled content type in request.");
189
190 IElement E = Expression.Encapsulate(Decoded);
191 Dictionary<string, IElement> Matches = new Dictionary<string, IElement>();
192
193 switch (Pattern.Root.PatternMatch(E, Matches))
194 {
195 case PatternMatchResult.Match:
196 return Matches;
197
198 case PatternMatchResult.NoMatch:
199 case PatternMatchResult.Unknown:
200 default:
201 throw new BadRequestException("Content does not match specification.");
202 }
203 }
204
212 {
213 return AssertUserAuthenticated(Request, true);
214 }
215
224 bool MustBeEnabled)
225 {
226 if (!(Request.User is AccountUser User))
227 throw new ForbiddenException("User not authenticated.");
228
229 if (MustBeEnabled && !User.Account.Enabled)
230 throw new ForbiddenException("Account not enabled.");
231
232 return User;
233 }
234
241 {
242 return GetAuthenticatedUser(Request, true);
243 }
244
251 protected static AccountUser GetAuthenticatedUser(HttpRequest Request, bool MustBeEnabled)
252 {
253 if (!(Request.User is AccountUser User))
254 return null;
255
256 if (MustBeEnabled && !User.Account.Enabled)
257 return null;
258
259 return User;
260 }
261
268 protected async Task CheckBlocks(HttpRequest Request)
269 {
270 DateTime? Next = await this.api.Auditor.GetEarliestLoginOpportunity(Request.RemoteEndPoint, "HTTPS");
271
272 if (Next.HasValue)
273 {
274 DateTime TP = Next.Value;
275 DateTime Today = DateTime.Today;
276 StringBuilder sb = new StringBuilder();
277
278 if (Next.Value == DateTime.MaxValue)
279 {
280 sb.Append("This endpoint (");
281 sb.Append(Request.RemoteEndPoint);
282 sb.Append(") has been blocked from the system.");
283 }
284 else
285 {
286 sb.Append("Too many failed login attempts in a row registered. Try again after ");
287 sb.Append(TP.ToLongTimeString());
288
289 if (TP.Date != Today)
290 {
291 if (TP.Date == Today.AddDays(1))
292 sb.Append(" tomorrow");
293 else
294 {
295 sb.Append(", ");
296 sb.Append(TP.ToShortDateString());
297 }
298 }
299
300 sb.Append(". Remote Endpoint: ");
301 sb.Append(Request.RemoteEndPoint);
302 }
303
304 throw new TooManyRequestsException(sb.ToString());
305 }
306 }
307
314 protected static Exception ToHttpException(XmppException ex)
315 {
316 if (ex is null)
317 return null;
318
319 if (ex is Networking.XMPP.StanzaErrors.BadRequestException)
320 return new BadRequestException(ex.Message);
321 else if (ex is Networking.XMPP.StanzaErrors.ConflictException)
322 return new ConflictException(ex.Message);
323 else if (ex is Networking.XMPP.StanzaErrors.ForbiddenException)
324 return new ForbiddenException(ex.Message);
325 else if (ex is Networking.XMPP.StanzaErrors.FeatureNotImplementedException)
326 return new Networking.HTTP.NotImplementedException(ex.Message);
327 else if (ex is Networking.XMPP.StanzaErrors.GoneException)
328 return new GoneException(ex.Message);
329 else if (ex is Networking.XMPP.StanzaErrors.InternalServerErrorException)
330 return new InternalServerErrorException(ex.Message);
331 else if (ex is Networking.XMPP.StanzaErrors.ItemNotFoundException)
332 return new NotFoundException(ex.Message);
333 else if (ex is Networking.XMPP.StanzaErrors.NotAllowedException)
334 return new MethodNotAllowedException(new string[0], ex.Message);
335 else if (ex is Networking.XMPP.StanzaErrors.ResourceConstraintException)
336 return new TooManyRequestsException(ex.Message);
337 else if (ex is Networking.XMPP.StanzaErrors.ServiceUnavailableException)
338 return new ServiceUnavailableException(ex.Message);
339 else if (ex is Networking.XMPP.StanzaErrors.NotAuthorizedException)
340 return new NetworkAuthenticationRequiredException(ex.Message);
341 else
342 return ex;
343 }
344
345 }
346}
Static class managing the runtime environment of the IoT Gateway.
Definition: Gateway.cs:126
static CaseInsensitiveString Domain
Domain name.
Definition: Gateway.cs:2354
static HttpServer HttpServer
HTTP Server
Definition: Gateway.cs:3299
static DomainConfiguration Instance
Current instance of configuration.
bool UseEncryption
If the server uses server-side encryption.
Basic authentication mechanism, as defined in RFC 2617: https://tools.ietf.org/html/rfc2617
Digest authentication mechanism, as defined in RFC 2617: https://tools.ietf.org/html/rfc2617
mTLS authentication mechanism, where identity is taken from a valid client certificate.
The request could not be understood by the server due to malformed syntax. The client SHOULD NOT repe...
The request could not be completed due to a conflict with the current state of the resource....
The server understood the request, but is refusing to fulfill it. Authorization will not help and the...
The requested resource is no longer available at the server and no forwarding address is known....
Base class for all HTTP authentication schemes, as defined in RFC-7235: https://datatracker....
Represents an HTTP request.
Definition: HttpRequest.cs:18
string RemoteEndPoint
Remote end-point.
Definition: HttpRequest.cs:195
bool HasData
If the request has data.
Definition: HttpRequest.cs:74
IUser User
Authenticated user, if available, or null if not available.
Definition: HttpRequest.cs:155
async Task< object > DecodeDataAsync()
Decodes data sent in request.
Definition: HttpRequest.cs:95
Implements an HTTP server.
Definition: HttpServer.cs:36
HttpResource Register(HttpResource Resource)
Registers a resource with the server.
Definition: HttpServer.cs:1287
bool Unregister(HttpResource Resource)
Unregisters a resource from the server.
Definition: HttpServer.cs:1438
Base class for all synchronous HTTP resources. A synchronous resource responds within the method hand...
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 client needs to authenticate to gain network access. Intended for use by intercepting proxies use...
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...
The user has sent too many requests in a given amount of time. Intended for use with rate limiting sc...
The server is refusing to service the request because the entity of the request is in a format not su...
Base class of XMPP exceptions
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
static IElement Encapsulate(object Value)
Encapsulates an object.
Definition: Expression.cs:4955
Use JWT tokens for authentication. The Bearer scheme defined in RFC 6750 is used: https://tools....
A factory that can create and validate JWT tokens.
Definition: JwtFactory.cs:53
bool Disposed
If the factory has been disposed.
Definition: JwtFactory.cs:169
static JwtFactory CreateHmacSha256()
Creates a JWT factory that can create and validate JWT tokens using the HMAC-SHA256 algorithm.
Definition: JwtFactory.cs:92
Collection of broker accounts
Definition: Accounts.cs:14
Abstract base class for agent resources
Task Register(HttpServer WebServer, AgentApi AgentApi)
Registers the resource on a web server.
static Accounts Accounts
Reference to available accounts.
Task Unregister(HttpServer WebServer)
Unregisters the resource from a web server.
static AccountUser AssertUserAuthenticated(HttpRequest Request)
Makes sure the request is made by an authenticated API user.
override bool UserSessions
If the resource uses user sessions.
override HttpAuthenticationScheme[] GetAuthenticationSchemes(HttpRequest Request)
Any authentication schemes used to authenticate users before access is granted to the corresponding r...
const string AgentNamespace
https://waher.se/Schema/BrokerAgent.xsd
static Exception ToHttpException(XmppException ex)
Tries to convert an XMPP Exception to an HTTP Exception.
async Task< Dictionary< string, IElement > > AssertMatch(HttpRequest Request)
Checks if content matches specification in resource.
static AccountUser GetAuthenticatedUser(HttpRequest Request, bool MustBeEnabled)
Gets the authenticated user object, if one exists.
virtual void AddAuthenticationSchemes(Accounts Accounts, List< HttpAuthenticationScheme > Schemes)
Adds authentication schemes to the resource.
override bool HandlesSubPaths
If the resource handles sub-paths.
static AccountUser AssertUserAuthenticated(HttpRequest Request, bool MustBeEnabled)
Makes sure the request is made by an authenticated API user.
static AccountUser GetAuthenticatedUser(HttpRequest Request)
Gets the authenticated user object, if one exists.
AgentResource(string AgentResourceName, params KeyValuePair< Type, Expression >[] PatternMatches)
Abstract base class for agent resources
async Task CheckBlocks(HttpRequest Request)
Checks if the client is blocked.
Service Module hosting the XMPP broker and its components.
Basic interface for all types of elements.
Definition: IElement.cs:20
ClientCertificates
Client Certificate Options
PatternMatchResult
Status result of a pattern matching operation.
Definition: ScriptNode.cs:17