Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
CreateWebForm.cs
1using System;
2using System.Collections.Generic;
3using System.Threading.Tasks;
4using Waher.Content;
14
16{
21 {
26 : base("Account/CreateWebForm")
27 {
28 }
29
35 {
36 base.GetAuthenticationSchemes(Request); // Initialize factory property.
37 return null;
38 }
39
43 public override bool UserSessions => true;
44
48 public virtual bool AllowsPOST => true;
49
56 public async Task POST(HttpRequest Request, HttpResponse Response)
57 {
58 await this.CheckBlocks(Request);
59
60 if (this.Api is null)
61 throw new ServiceUnavailableException("API not available.");
62
63 if (!Request.HasData)
64 throw new BadRequestException("No content is request.");
65
66 object Decoded = await Request.DecodeDataAsync();
67
68 if (!(Decoded is Dictionary<string, string> Form))
69 throw new UnsupportedMediaTypeException("Content does not match specification.");
70
71 if (!Form.TryGetValue("UserName", out string UserName) || string.IsNullOrEmpty(UserName))
72 throw new BadRequestException("Missing user name.");
73
74 if (!Form.TryGetValue("EMail", out string EMail) || string.IsNullOrEmpty(EMail))
75 throw new BadRequestException("Missing e-mail address.");
76
77 if (!Form.TryGetValue("PhoneNr", out string PhoneNr))
78 PhoneNr = string.Empty;
79
80 if (!Form.TryGetValue("Password", out string Password) || string.IsNullOrEmpty(Password))
81 throw new BadRequestException("Missing password.");
82
83 if (!Form.TryGetValue("g-recaptcha-response", out string RecaptchaResponse) || string.IsNullOrEmpty(RecaptchaResponse))
84 throw new BadRequestException("Missing reCaptcha-response.");
85
86 if (!Form.TryGetValue("RedirectionUrl", out string RedirectionUrl) || string.IsNullOrEmpty(RedirectionUrl))
87 throw new BadRequestException("Missing redirection URL.");
88
89 if (!Form.TryGetValue("Seconds", out string s) || !int.TryParse(s, out int Seconds) || Seconds <= 0 || Seconds > 3600)
90 throw new BadRequestException("Invalid number of seconds.");
91
92 if (!string.IsNullOrEmpty(Gateway.Domain) &&
93 !await Feedback.SiteVerify(RecaptchaResponse, Request.RemoteEndPoint))
94 {
95 throw new ForbiddenException("Bot?");
96 }
97
98 if (!string.IsNullOrEmpty(PhoneNr) && !Create.InternationalNumberFormat.IsMatch(PhoneNr))
99 throw new BadRequestException("Phone numbers must be provided using the international number format, starting with +, then the country code, then the number.");
100
101 Create.AssertUserNameValid(UserName);
102
103 ApiKey Key = await this.Api.GetAgentApiApiKey();
104 if (Key is null)
105 {
106 string Msg = "Resource disabled. No Agent API Key configured.";
107 throw new ServiceUnavailableException(Msg);
108 }
109
110 DataStorage.Account Account = await Database.FindFirstIgnoreRest<DataStorage.Account>(
111 new FilterFieldEqualTo("UserName", UserName));
112
113 if (Account is null)
114 {
115 if (Key.NrCreated - Key.NrDeleted >= Key.MaxAccounts)
116 throw new ServiceUnavailableException("API Key account limit reached.");
117
118 Account = new DataStorage.Account()
119 {
120 ApiKey = Key.Key,
121 UserName = UserName,
122 Password = Password,
123 EMail = EMail,
124 EMailVerified = null,
125 PhoneNr = PhoneNr,
126 PhoneNrVerified = null,
127 Enabled = false,
128 CanRelayMessages = false,
129 Created = DateTime.Now
130 };
131
132 await Database.Insert(Account);
133
134 Key.NrCreated++;
135 await Database.Update(Key);
136
137 await PersistenceLayer.AccountCreated(Account, Key, Gateway.Domain, Request.RemoteEndPoint);
138 }
139 else
140 {
141 if (Account.Password != Password)
142 throw new ForbiddenException("Account already exists.", await Create.GetAlterntiveNameSuggestions(UserName));
143
144 bool Updated = false;
145
146 if (Account.EMail != EMail)
147 {
149 throw new ForbiddenException("Not permitted to change e-mail address.");
150
151 Account.EMail = EMail;
152 Account.EMailVerified = null;
153
154 Updated = true;
155 }
156
157 if (Account.PhoneNr != PhoneNr)
158 {
160 throw new ForbiddenException("Not permitted to change phone number.");
161
162 Account.PhoneNr = PhoneNr;
163 Account.PhoneNrVerified = null;
164
165 Updated = true;
166 }
167
168 if (Updated)
169 await Database.Update(Account);
170
171 await Account.LoggedIn(Request.RemoteEndPoint);
172 }
173
174 LoginAuditor.Success("Successful Agent API account created.", UserName, Request.RemoteEndPoint, "HTTPS");
175
176 if (!Account.Enabled)
177 {
178 string OnboardingDomainName = await LegalComponent.GetOnboardingNeuronDomainName();
179
180 if (!(await InternetContent.PostAsync(
181 new Uri("https://" + OnboardingDomainName + "/ID/SendVerificationMessage.ws"),
182 new Dictionary<string, object>()
183 {
184 { "EMail", Account.EMail.Value }
185 },
186 new KeyValuePair<string, string>("Accept", JsonCodec.DefaultContentType))
187 is Dictionary<string, object> EMailResult))
188 {
189 throw new ServiceUnavailableException("Unable to send verification mail. Unexpected result");
190 }
191
192 if (!EMailResult.TryGetValue("Status", out object Obj) || !(Obj is bool Status) || !Status)
193 {
194 if (EMailResult.TryGetValue("Message", out Obj) && Obj is string Message)
195 throw new ServiceUnavailableException("Unable to send verification mail. Error reported: " + Message);
196 else
197 throw new ServiceUnavailableException("Unable to send verification mail. Check e-mail address provided.");
198 }
199
201 {
202 if (!(await InternetContent.PostAsync(
203 new Uri("https://" + OnboardingDomainName + "/ID/SendVerificationMessage.ws"),
204 new Dictionary<string, object>()
205 {
206 { "Nr", Account.PhoneNr.Value }
207 },
208 new KeyValuePair<string, string>("Accept", JsonCodec.DefaultContentType))
209 is Dictionary<string, object> SmsResult))
210 {
211 throw new ServiceUnavailableException("Unable to send verification mail. Unexpected result");
212 }
213
214 if (!SmsResult.TryGetValue("Status", out Obj) || !(Obj is bool Status2) || !Status2)
215 {
216 if (SmsResult.TryGetValue("Message", out Obj) && Obj is string Message)
217 throw new ServiceUnavailableException("Unable to send verification SMS. Error reported: " + Message);
218 else
219 throw new ServiceUnavailableException("Unable to send verification SMS. Check phone number provided.");
220 }
221 }
222 }
223
224 int IssuedAt = (int)Math.Round(DateTime.UtcNow.Subtract(JSON.UnixEpoch).TotalSeconds);
225 int Expires = IssuedAt + (int)Seconds;
226
227 string Token = Factory.Create(
228 new KeyValuePair<string, object>(JwtClaims.JwtId, Convert.ToBase64String(Gateway.NextBytes(32))),
229 new KeyValuePair<string, object>(JwtClaims.Issuer, Gateway.Domain?.Value ?? string.Empty),
230 new KeyValuePair<string, object>(JwtClaims.Subject, Account.UserName.Value + "@" + (Gateway.Domain?.Value ?? string.Empty)),
231 new KeyValuePair<string, object>(JwtClaims.IssueTime, IssuedAt),
232 new KeyValuePair<string, object>(JwtClaims.ExpirationTime, Expires));
233
234 Request.Session[SessionTokenInfoVariableName] = new SessionTokenInfo()
235 {
237 Token = Token,
238 Expires = Expires,
239 Seconds = Seconds
240 };
241
242 throw new TemporaryRedirectException(RedirectionUrl);
243 }
244
248 public const string SessionTokenInfoVariableName = " AgentAPI.SessionTokenInfo ";
249 }
250}
Static class managing encoding and decoding of internet content.
static Task< object > PostAsync(Uri Uri, object Data, params KeyValuePair< string, string >[] Headers)
Posts to a resource, using a Uniform Resource Identifier (or Locator).
Helps with common JSON-related tasks.
Definition: JSON.cs:14
static readonly DateTime UnixEpoch
Unix Date and Time epoch, starting at 1970-01-01T00:00:00Z
Definition: JSON.cs:18
const string DefaultContentType
application/json
Definition: JsonCodec.cs:19
Static class managing the runtime environment of the IoT Gateway.
Definition: Gateway.cs:126
static CaseInsensitiveString Domain
Domain name.
Definition: Gateway.cs:2354
static byte[] NextBytes(int NrBytes)
Generates an array of random bytes.
Definition: Gateway.cs:3534
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...
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
Variables Session
Contains session states, if the resource requires sessions, or null otherwise.
Definition: HttpRequest.cs:164
async Task< object > DecodeDataAsync()
Decodes data sent in request.
Definition: HttpRequest.cs:95
Represets a response of an HTTP client request.
Definition: HttpResponse.cs:21
The server is currently unable to handle the request due to a temporary overloading or maintenance of...
The requested resource resides temporarily under a different URI. Since the redirection MAY be altere...
The server is refusing to service the request because the entity of the request is in a format not su...
Represents a case-insensitive string.
string Value
String-representation of the case-insensitive string. (Representation is case sensitive....
static bool IsNullOrEmpty(CaseInsensitiveString value)
Indicates whether the specified string is null or an CaseInsensitiveString.Empty string.
Static interface for database persistence. In order to work, a database provider has to be assigned t...
Definition: Database.cs:19
static async Task Update(object Object)
Updates an object in the database.
Definition: Database.cs:626
static async Task Insert(object Object)
Inserts an object into the default collection of the database.
Definition: Database.cs:95
This filter selects objects that have a named field equal to a given value.
Static class containing predefined JWT claim names.
Definition: JwtClaims.cs:10
const string Issuer
Issuer of the JWT
Definition: JwtClaims.cs:14
const string IssueTime
Time at which the JWT was issued; can be used to determine age of the JWT
Definition: JwtClaims.cs:39
const string JwtId
Unique identifier; can be used to prevent the JWT from being replayed (allows a token to be used only...
Definition: JwtClaims.cs:44
const string Subject
Subject of the JWT (the user)
Definition: JwtClaims.cs:19
const string ExpirationTime
Time after which the JWT expires
Definition: JwtClaims.cs:29
string Create(params KeyValuePair< string, object >[] Claims)
Creates a new JWT token.
Definition: JwtFactory.cs:248
Class that monitors login events, and help applications determine malicious intent....
Definition: LoginAuditor.cs:26
static async void Success(string Message, string UserName, string RemoteEndpoint, string Protocol, params KeyValuePair< string, object >[] Tags)
Handles a successful login attempt.
Contains information about a broker account.
Definition: Account.cs:28
CaseInsensitiveString EMail
E-mail address associated with account.
Definition: Account.cs:129
CaseInsensitiveString UserName
User Name of account
Definition: Account.cs:99
bool Enabled
If account is enabled
Definition: Account.cs:287
string Password
Password of account
Definition: Account.cs:109
CaseInsensitiveString PhoneNr
Phone number associated with account.
Definition: Account.cs:138
Account()
Contains information about a broker account.
Definition: Account.cs:72
async Task LoggedIn(string RemoteEndpoint)
Registers a log-in event on the account.
Definition: Account.cs:356
static readonly Regex InternationalNumberFormat
International phone number format.
Definition: Create.cs:45
async Task POST(HttpRequest Request, HttpResponse Response)
Executes the POST method on the resource.
CreateWebForm()
Creates an account on the server manually.
const string SessionTokenInfoVariableName
Name of session variable where token information is stored.
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...
virtual bool AllowsPOST
If the POST method is allowed.
async Task< ApiKey > GetAgentApiApiKey()
Gets API Key for the Agent API.
Definition: AgentApi.cs:142
Abstract base class for agent resources
async Task CheckBlocks(HttpRequest Request)
Checks if the client is blocked.
POST Interface for HTTP resources.