source: TFP-WebServer/WebServer/src/WebAPI/APIs/Permissions/RegisterUser.cs@ 496

Last change on this file since 496 was 486, checked in by alloc, 9 months ago

22.0.1.1 WebServer release

  • Moved API error codes from string literals to enum
File size: 5.0 KB
Line 
1using System;
2using System.Collections.Generic;
3using System.Net;
4using System.Text;
5using System.Text.RegularExpressions;
6using JetBrains.Annotations;
7using Utf8Json;
8using Webserver.Permissions;
9using Webserver.UrlHandlers;
10
11namespace Webserver.WebAPI.APIs.Permissions {
12 [UsedImplicitly]
13 public class RegisterUser : AbsRestApi {
14 private static readonly byte[] jsonPlayerNameKey = JsonWriter.GetEncodedPropertyNameWithBeginObject ("playerName");
15 private static readonly byte[] jsonExpirationKey = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("expirationSeconds");
16
17 // TODO: Rate-limiting
18
19 private static readonly Regex userValidationRegex = new Regex ("^\\w{4,16}$", RegexOptions.ECMAScript | RegexOptions.Compiled);
20 private static readonly Regex passValidationRegex = new Regex ("^\\w{4,16}$", RegexOptions.ECMAScript | RegexOptions.Compiled);
21
22 public RegisterUser (Web _parentWeb) : base (_parentWeb) {
23 }
24
25 protected override void HandleRestGet (RequestContext _context) {
26 string token = _context.RequestPath;
27
28 if (string.IsNullOrEmpty (token)) {
29 SendEmptyResponse (_context, HttpStatusCode.BadRequest, null, EApiErrorCode.MISSING_TOKEN);
30 return;
31 }
32
33 if (!UserRegistrationTokens.TryValidate (token, out UserRegistrationTokens.RegistrationData regData)) {
34 SendEmptyResponse (_context, HttpStatusCode.NotFound, null, EApiErrorCode.INVALID_OR_EXPIRED_TOKEN);
35 return;
36 }
37
38 PrepareEnvelopedResult (out JsonWriter writer);
39
40 writer.WriteRaw (jsonPlayerNameKey);
41 writer.WriteString (regData.PlayerName);
42
43 writer.WriteRaw (jsonExpirationKey);
44 writer.WriteDouble ((regData.ExpiryTime - DateTime.Now).TotalSeconds);
45
46 writer.WriteEndObject ();
47
48 SendEnvelopedResult (_context, ref writer);
49 }
50
51 protected override void HandleRestPost (RequestContext _context, IDictionary<string, object> _jsonInput, byte[] _jsonInputData) {
52 if (!JsonCommons.TryGetJsonField (_jsonInput, "token", out string token)) {
53 SendEmptyResponse (_context, HttpStatusCode.BadRequest, _jsonInputData, EApiErrorCode.MISSING_TOKEN);
54 return;
55 }
56
57 if (!JsonCommons.TryGetJsonField (_jsonInput, "username", out string username)) {
58 SendEmptyResponse (_context, HttpStatusCode.BadRequest, _jsonInputData, EApiErrorCode.MISSING_USERNAME);
59 return;
60 }
61
62 if (!JsonCommons.TryGetJsonField (_jsonInput, "password", out string password)) {
63 SendEmptyResponse (_context, HttpStatusCode.BadRequest, _jsonInputData, EApiErrorCode.MISSING_PASSWORD);
64 return;
65 }
66
67 if (!UserRegistrationTokens.TryValidate (token, out UserRegistrationTokens.RegistrationData regData)) {
68 SendEmptyResponse (_context, HttpStatusCode.Unauthorized, null, EApiErrorCode.INVALID_OR_EXPIRED_TOKEN);
69 return;
70 }
71
72 if (!userValidationRegex.IsMatch (username)) {
73 SendEmptyResponse (_context, HttpStatusCode.Unauthorized, _jsonInputData, EApiErrorCode.INVALID_USERNAME);
74 return;
75 }
76
77 if (!passValidationRegex.IsMatch (password)) {
78 SendEmptyResponse (_context, HttpStatusCode.Unauthorized, _jsonInputData, EApiErrorCode.INVALID_PASSWORD);
79 return;
80 }
81
82 if (AdminWebUsers.Instance.GetUsers ().TryGetValue (username, out AdminWebUsers.WebUser existingMapping)) {
83 // Username already exists
84
85 if (!Equals (existingMapping.PlatformUser, regData.PlatformUserId) ||
86 !Equals (existingMapping.CrossPlatformUser, regData.CrossPlatformUserId)) {
87 // Username already in use by another player
88 SendEmptyResponse (_context, HttpStatusCode.Unauthorized, _jsonInputData, EApiErrorCode.DUPLICATE_USERNAME);
89 return;
90 }
91
92 // Username used by the same player, allow overwriting his existing login
93 }
94
95 // Log info
96 string crossplatformidString = regData.CrossPlatformUserId == null ? "" : $", crossplatform ID {regData.CrossPlatformUserId.CombinedString}";
97 Log.Out ($"[Web] User registered: Username '{username}' for platform ID {regData.PlatformUserId.CombinedString}{crossplatformidString}");
98
99 if (AdminWebUsers.Instance.HasUser (regData.PlatformUserId, regData.CrossPlatformUserId, out AdminWebUsers.WebUser existingUser)) {
100 // Remove existing username of player, only allowing one user per player
101
102 Log.Out ($"[Web] Re-registration, replacing existing username '{existingUser.Name}'");
103 AdminWebUsers.Instance.RemoveUser (existingUser.Name);
104 }
105
106 // Add new user
107 AdminWebUsers.Instance.AddUser (username, password, regData.PlatformUserId, regData.CrossPlatformUserId);
108
109 // Login with new user and return response
110 string remoteEndpointString = _context.Request.RemoteEndPoint!.ToString ();
111 SessionHandler.HandleUserIdLogin (ParentWeb.ConnectionHandler, _context, remoteEndpointString, SessionHandler.userPassLoginName,
112 username, regData.PlatformUserId, regData.CrossPlatformUserId);
113
114 _context.Response.StatusCode = (int)HttpStatusCode.Created;
115 _context.Response.ContentType = WebUtils.MimePlain;
116 _context.Response.ContentEncoding = Encoding.UTF8;
117 _context.Response.ContentLength64 = 0;
118 }
119
120 public override int DefaultPermissionLevel () => AdminWebModules.PermissionLevelGuest;
121 }
122}
Note: See TracBrowser for help on using the repository browser.