Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
ProtectedMethod.cs
1using System;
3using System.Diagnostics.CodeAnalysis;
4using System.Reflection;
5using System.Threading.Tasks;
8using Waher.Script;
11
13{
17 public class ProtectedMethod
18 {
24 public ProtectedMethod(MethodInfo Method, bool CaseSensitive)
25 : this(Method, CaseSensitive, JsonRpcWebService.GetRequiredPrivileges(Method))
26 {
27 }
28
35 public ProtectedMethod(MethodInfo Method,
36 bool CaseSensitive, string[]? RequiredPrivileges)
37 {
38 ParameterInfo[] Arguments = Method.GetParameters();
39 bool IsSpecialArgument;
40 bool IsMetaDataArgument;
41 bool IsIdArgument;
42 this.Method = Method;
43 this.NrArguments = Arguments.Length;
44 this.NrSpecialArguments = 0;
45 this.RequiredPrivileges = RequiredPrivileges ?? Array.Empty<string>();
46 this.RequiresAuthentication = (RequiredPrivileges?.Length ?? 0) > 0;
47 this.Arguments = new ProtectedMethodArgumentInfo[this.NrArguments];
48
49 if (CaseSensitive)
50 this.NamedArguments = new Dictionary<string, int>(StringComparer.InvariantCulture);
51 else
52 this.NamedArguments = new Dictionary<string, int>(StringComparer.InvariantCultureIgnoreCase);
53
54 foreach (ParameterInfo P in Arguments)
55 {
56 IsMetaDataArgument = false;
57 IsIdArgument = false;
58
59 if (P.ParameterType == typeof(HttpRequest))
60 {
61 if (this.RequestArgument is null)
62 {
63 this.RequestArgument = P.Position;
64 this.NrSpecialArguments++;
65 IsSpecialArgument = true;
66 }
67 else
68 throw new ArgumentException("Only one argument of type HttpRequest is allowed.", nameof(Method));
69 }
70 else if (P.ParameterType == typeof(HttpResponse))
71 {
72 if (this.ResponseArgument is null)
73 {
74 this.ResponseArgument = P.Position;
75 this.NrSpecialArguments++;
76 IsSpecialArgument = true;
77 }
78 else
79 throw new ArgumentException("Only one argument of type HttpResponse is allowed.", nameof(Method));
80 }
81 else if (P.ParameterType == typeof(Dictionary<string, object?>) &&
82 P.IsDefined(typeof(JsonRpcMetaDataArgumentAttribute), true))
83 {
84 if (this.MetaDataArgument is null)
85 {
86 this.MetaDataArgument = P.Position;
87 this.NrSpecialArguments++;
88 IsSpecialArgument = true;
89 IsMetaDataArgument = true;
90 }
91 else
92 throw new ArgumentException("Only one meta-data argument is allowed.", nameof(Method));
93 }
94 else if (P.IsDefined(typeof(JsonRpcIdAttribute), true))
95 {
96 if (this.IdArgument is null)
97 {
98 this.IdArgument = P.Position;
99 this.NrSpecialArguments++;
100 IsSpecialArgument = true;
101 IsIdArgument = true;
102 }
103 else
104 throw new ArgumentException("Only one ID argument is allowed.", nameof(Method));
105 }
106 else
107 {
108 this.NamedArguments[P.Name] = P.Position;
109 IsSpecialArgument = false;
110 }
111
112 this.Arguments[P.Position] = new ProtectedMethodArgumentInfo(P, IsSpecialArgument,
113 P.HasDefaultValue, P.HasDefaultValue ? P.DefaultValue : null,
114 IsMetaDataArgument, IsIdArgument);
115 }
116
117 this.Documentation = GetDocumentation(Method);
118
119 this.HasReturnValue = Method.ReturnType != typeof(Task) &&
120 !Expression.IsVoid(Method.ReturnType);
121
122 if (this.HasReturnValue)
123 this.ReturnValueDocumentation = GetDocumentation(Method.ReturnParameter);
124 else
125 this.ReturnValueDocumentation = Array.Empty<KeyValuePair<bool, string>>();
126
127 JsonRpcDocNameAttribute? DocName = Method.GetCustomAttribute<JsonRpcDocNameAttribute>(true);
128 this.Name = DocName?.Name ?? this.Method.Name;
129 }
130
138 public static KeyValuePair<bool, string>[] GetDocumentation(ICustomAttributeProvider Object)
139 {
141
142 foreach (object Attribute in Object.GetCustomAttributes(typeof(JsonRpcDocumentationAttribute), true))
143 {
144 if (Attribute is JsonRpcDocumentationAttribute DocAttribute)
145 Documentation.Add(new KeyValuePair<bool, string>(DocAttribute.IsMarkdown, DocAttribute.Documentation));
146 }
147
148 return Documentation.ToArray();
149 }
150
156
160 public MethodInfo Method { get; }
161
165 public int NrArguments { get; }
166
170 public int NrSpecialArguments { get; }
171
175 public Dictionary<string, int> NamedArguments { get; }
176
180 public int? RequestArgument { get; }
181
185 public int? ResponseArgument { get; }
186
190 public int? MetaDataArgument { get; }
191
195 public int? IdArgument { get; }
196
201
205 public bool RequiresAuthentication { get; }
206
210 public string[] RequiredPrivileges { get; }
211
217 public KeyValuePair<bool, string>[] Documentation { get; }
218
224 public KeyValuePair<bool, string>[] ReturnValueDocumentation { get; }
225
229 public bool HasReturnValue { get; }
230
234 public string Name { get; }
235
241 public bool IsAuthorized(IUser? User)
242 {
243 return this.IsAuthorized(User, out _);
244 }
245
252 public bool IsAuthorized(IUser? User, out string? MissingPrivilege)
253 {
254 MissingPrivilege = null;
255
256 if (!this.RequiresAuthentication)
257 return true;
258
259 if (User is null)
260 return false;
261
262 foreach (string Privilege in this.RequiredPrivileges)
263 {
264 if (!User.HasPrivilege(Privilege))
265 {
266 MissingPrivilege = Privilege;
267 return false;
268 }
269 }
270
271 return true;
272 }
273
280 public void AssertAuthorized(string ObjectId, IUser? User)
281 {
282 if (!this.IsAuthorized(User, out string? MissingPrivilege))
283 {
284 throw ForbiddenException.AccessDenied(ObjectId,
285 User?.UserName ?? string.Empty,
286 MissingPrivilege ?? string.Empty);
287 }
288 }
289
300 public bool TryBuildRequest(object? Id, Dictionary<string, object?> Parameters,
301 Dictionary<string, object?>? MetaData,
302 [NotNullWhen(false)] out string? Reason,
303 [NotNullWhen(true)] out object?[]? Arguments)
304 {
305 int c = this.NrArguments;
306 int NrParametersSet = 0;
307
308 Arguments = new object?[c];
309
310 foreach (KeyValuePair<string, object?> P in Parameters)
311 {
312 if (!this.NamedArguments.TryGetValue(P.Key, out int i))
313 {
314 if (this.MetaDataArgument.HasValue &&
315 (P.Key == this.Arguments[this.MetaDataArgument.Value].Parameter.Name ||
316 P.Key == "_meta"))
317 {
318 i = this.MetaDataArgument.Value;
319 }
320 else if (P.Key == "_meta")
321 continue;
322 else
323 {
324 Reason = "Invalid parameter name: " + P.Key;
325 Arguments = null;
326 return false;
327 }
328 }
329
330 object? Value = P.Value;
331 ProtectedMethodArgumentInfo ArgumentInfo = this.Arguments[i];
332 Type ExpectedType = ArgumentInfo.Parameter.ParameterType;
333 Type ParameterType = Value?.GetType() ?? typeof(object);
334
335 if (ArgumentInfo.IsMetaDataArgument && !(MetaData is null))
336 {
337 Dictionary<string, object?>? MetaDataValue = Value as Dictionary<string, object?>;
338
339 if (Value is null || !(MetaDataValue is null))
340 {
341 if (MetaDataValue is null)
342 MetaDataValue = MetaData;
343 else
344 {
345 foreach (KeyValuePair<string, object?> P2 in MetaData)
346 {
347 if (!MetaDataValue.ContainsKey(P2.Key))
348 MetaDataValue[P2.Key] = P2.Value;
349 }
350 }
351
352 Value = MetaDataValue;
353 }
354 }
355 else if (ArgumentInfo.IsIdArgument)
356 Value = Id;
357
358 if (ParameterType == ExpectedType)
359 Arguments[i] = Value;
360 else if (Expression.TryConvert(Value, ExpectedType, true, out object Converted))
361 Arguments[i] = Converted;
362 else if (ArgumentInfo.HasDefaultValue &&
363 Value is Dictionary<string, object?> Dictionary &&
364 Dictionary.Count == 0)
365 {
366 Arguments[i] = ArgumentInfo.DefaultValue;
367 }
368 else
369 {
370 Arguments = null;
371 Reason = "Parameter " + P.Key +
372 " has incorrect type: " + ParameterType.FullName +
373 ", Expected: " + ExpectedType.FullName;
374 return false;
375 }
376
377 NrParametersSet++;
378 }
379
380 if (NrParametersSet != c - this.NrSpecialArguments)
381 {
382 foreach (ProtectedMethodArgumentInfo Argument in this.Arguments)
383 {
384 if (!Argument.IsSpecialArgument &&
385 Argument.HasDefaultValue &&
386 !Parameters.ContainsKey(Argument.Parameter.Name))
387 {
388 Arguments[Argument.Parameter.Position] = Argument.DefaultValue;
389 NrParametersSet++;
390 }
391 }
392 }
393
394 if (NrParametersSet < c - this.NrSpecialArguments)
395 {
396 Reason = "Missing required parameters.";
397 Arguments = null;
398 return false;
399 }
400
401 Reason = null;
402
403 return true;
404 }
405
411 {
413 }
414
420 public void UpdateAuthenticationMechanisms(string? ResourceMetaDataUri)
421 {
422 if (this.RequiresAuthentication)
423 {
424 if (string.IsNullOrEmpty(ResourceMetaDataUri))
425 {
426 this.AuthenticationMechanisms = HttpModule.GetAuthenticationSchemes(
427 this.RequiredPrivileges);
428 }
429 else
430 {
431 this.AuthenticationMechanisms = HttpModule.GetAuthenticationSchemes(
432 new Uri(ResourceMetaDataUri), this.RequiredPrivileges);
433 }
434 }
435 }
436 }
437}
The server understood the request, but is refusing to fulfill it. Authorization will not help and the...
static ForbiddenException AccessDenied(string ObjectId, string ActorId)
Returns a ForbiddenException object, and logs a entry in the event log about the event.
Base class for all HTTP authentication schemes, as defined in RFC-7235: https://datatracker....
Represents an HTTP request.
Definition: HttpRequest.cs:22
Represets a response of an HTTP client request.
Definition: HttpResponse.cs:23
Declares an argument as recipient for meta-data, if such is available in the request.
Abstract base class for Web Services based on JSON-RPC v2.0.
Adds documentation to a JSON-RPC method, parameter, property, field, event or return value....
Marks a parameter as the JSON-RPC ID attribute.
Information about an argument in a protected method.
bool IsIdArgument
If the argument represents an ID argument.
bool IsSpecialArgument
If the argument represents a special argument.
bool IsMetaDataArgument
If the argument represents a meta-data argument.
object? DefaultValue
Default value of argument, if defined.
Information about a protected method.
int NrSpecialArguments
Number of special arguments
ProtectedMethodArgumentInfo[] Arguments
Arguments
bool IsAuthorized(IUser? User, out string? MissingPrivilege)
Checks if a user is authorized to call the method.
int? MetaDataArgument
Meta-data argument index
bool IsAuthorized(IUser? User)
Checks if a user is authorized to call the method.
bool TryBuildRequest(object? Id, Dictionary< string, object?> Parameters, Dictionary< string, object?>? MetaData, [NotNullWhen(false)] out string? Reason, [NotNullWhen(true)] out object?[]? Arguments)
Tries to build a request for the method, based on the provided named parameters.
ProtectedMethod(MethodInfo Method, bool CaseSensitive)
Information about a protected method.
bool HasReturnValue
If the method has a return value.
Dictionary< string, int > NamedArguments
Named arguments
void AssertAuthorized(string ObjectId, IUser? User)
Asserts user is authorized to call the method. If not, a ForbiddenException is thrown.
HttpAuthenticationScheme?[] AuthenticationMechanisms
Authentication mechanisms available to authenticate users, if authentication is required.
int? ResponseArgument
Response argument index
KeyValuePair< bool, string >[] ReturnValueDocumentation
Available documentation for the return value. Value represents documentation text,...
bool RequiresAuthentication
If authentication of the user is required.
string[] RequiredPrivileges
Privileges required by the user that calls the method.
string Name
Name to use in documentation.
static KeyValuePair< bool, string >[] GetDocumentation(ICustomAttributeProvider Object)
Gets available documentation for a member. Value represents documentation text, and Key represents if...
KeyValuePair< bool, string >[] Documentation
Available documentation for the method. Value represents documentation text, and Key represents if th...
void UpdateAuthenticationMechanisms(string? ResourceMetaDataUri)
Updates the authentication mechanisms available to authenticate users that want to access the method.
void UpdateAuthenticationMechanisms()
Updates the authentication mechanisms available to authenticate users that want to access the method.
ProtectedMethod(MethodInfo Method, bool CaseSensitive, string[]? RequiredPrivileges)
Information about a protected method.
A chunked list is a linked list of chunks of objects of type T .
Definition: ChunkedList.cs:54
Class managing a script expression.
Definition: Expression.cs:41
static bool IsVoid(Type ResultType)
Checks if a result object type is equal to void (i.e. its type equal to System.Threading....
Definition: Expression.cs:4735
static bool TryConvert(object Value, Type DesiredType, bool AcceptInformationLoss, out object Result)
Tries to convert an object Value to an object of type DesiredType .
Definition: Expression.cs:5530
static HttpAuthenticationScheme[] GetAuthenticationSchemes()
Gets an array of authentication schemes available to authorize access to a web resource.
Definition: HttpModule.cs:108
Basic interface for a user.
Definition: IUser.cs:7
Definition: ImplTypes.g.cs:58