MongoDB connected. User registration implemented.

This commit is contained in:
Andrey Shabarshov 2021-12-28 13:37:10 +00:00
parent 2525e37f83
commit 96411f70f7
8 changed files with 96 additions and 68 deletions

View File

@ -18,7 +18,7 @@ namespace QRBee.Api.Controllers
_service = service;
}
[HttpPost]
[HttpPost("Register")]
public Task<RegistrationResponse> Register([FromBody] RegistrationRequest value)
{
return _service.Register(value);

View File

@ -5,6 +5,7 @@ namespace QRBee.Api
public class DatabaseSettings
{
public string? ConnectionString { get; set;}
public string? DatabaseName { get; set; }
public MongoClientSettings ToMongoDbSettings()
{

View File

@ -1,3 +1,4 @@
using Microsoft.Extensions.Options;
using MongoDB.Driver;
using QRBee.Api;
using QRBee.Api.Services;
@ -12,10 +13,12 @@ builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddSingleton<IQRBeeAPI,QRBeeAPI>();
builder.Services.AddSingleton<IStorage, Storage>();
builder.Services.Configure<DatabaseSettings>(builder.Configuration.GetSection("QRBeeDatabase"));
builder.Services.AddSingleton<IMongoClient>( cfg => new MongoClient(cfg.GetRequiredService<DatabaseSettings>().ToMongoDbSettings()));
builder.Services
.AddSingleton<IQRBeeAPI,QRBeeAPI>()
.AddSingleton<IStorage, Storage>()
.Configure<DatabaseSettings>(builder.Configuration.GetSection("QRBeeDatabase"))
.AddSingleton<IMongoClient>( cfg => new MongoClient(cfg.GetRequiredService<IOptions<DatabaseSettings>>().Value.ToMongoDbSettings()))
;
var app = builder.Build();

View File

@ -1,12 +1,10 @@
using Microsoft.AspNetCore.Identity;
namespace QRBee.Api.Services
namespace QRBee.Api.Services.Database
{
public interface IStorage
{
void PutUserInfo(UserInfo info);
UserInfo GetUserInfo(string email);
Task<string> PutUserInfo(UserInfo info);
Task<UserInfo> GetUserInfo(string email);
}
}

View File

@ -1,17 +1,56 @@
namespace QRBee.Api.Services.Database
using Microsoft.Extensions.Options;
using MongoDB.Driver;
namespace QRBee.Api.Services.Database
{
public class Storage: IStorage
{
public void PutUserInfo(UserInfo info)
private readonly IMongoDatabase _database;
public Storage(IMongoClient client, IOptions<DatabaseSettings> settings)
{
throw new NotImplementedException();
var name = settings.Value.DatabaseName;
_database = client.GetDatabase(name);
}
public UserInfo GetUserInfo(string email)
public async Task<string> PutUserInfo(UserInfo info)
{
throw new NotImplementedException();
var collection = _database.GetCollection<UserInfo>("Users");
var user = await TryGetUserInfo(info.Email);
// Ignore re-register
// Potential vulnerability if user registers with other email
if (user == null)
{
await collection.InsertOneAsync(info);
return info.ClientId ?? throw new ApplicationException($"ClientId is null while adding user {info.Email}");
}
return user.ClientId ?? throw new ApplicationException($"ClientId is null while adding user {info.Email}");
}
internal async Task<UserInfo?> TryGetUserInfo(string email)
{
var collection = _database.GetCollection<UserInfo>("Users");
using var cursor = await collection.FindAsync($"{{ Email: \"{email}\" }}");
if (!await cursor.MoveNextAsync())
{
return null;
}
return cursor.Current.FirstOrDefault();
}
public async Task<UserInfo> GetUserInfo(string email)
{
var user = await TryGetUserInfo(email);
return user ?? throw new ApplicationException($"User {email} not found.");
}
}
}

View File

@ -1,40 +1,32 @@
namespace QRBee.Api.Services;
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
namespace QRBee.Api.Services.Database;
public record UserInfo
{
#pragma warning disable CS8618
public UserInfo()
#pragma warning restore CS8618
{
}
public UserInfo(string name, string email, string dateOfBirth)
{
Name = name;
Email = email;
Name = name;
Email = email;
DateOfBirth = dateOfBirth;
}
public string? Name
{
get;
set;
}
/// <summary>
/// Never use directly. Use <see cref="ClientId"/> instead.
/// </summary>
[BsonId] public ObjectId Id { get; set; }
public string? Email
{
get;
set;
}
public string? DateOfBirth
{
get;
set;
}
public bool RegisterAsMerchant
{
get;
set;
}
[BsonIgnore] public string? ClientId => Id == ObjectId.Empty ? null : Id.ToString();
public string Name { get; set; }
public string Email { get; set; }
public string DateOfBirth { get; set; }
public bool RegisterAsMerchant { get; set; }
}

View File

@ -1,4 +1,6 @@
using System.Text.RegularExpressions;
using System.Globalization;
using System.Text.RegularExpressions;
using QRBee.Api.Services.Database;
using QRBee.Core;
using QRBee.Core.Data;
@ -7,32 +9,27 @@ namespace QRBee.Api.Services
public class QRBeeAPI: IQRBeeAPI
{
private readonly IStorage _storage;
private const int MaxNameLength = 512;
private const int MaxEmailLength = 512;
public QRBeeAPI(IStorage storage)
{
_storage = storage;
}
public Task<RegistrationResponse> Register(RegistrationRequest request)
public async Task<RegistrationResponse> Register(RegistrationRequest request)
{
throw new NotImplementedException();
Validate(request);
var info = Convert(request);
if (UserExists(info))
{
// throw error
}
else
{
_storage.PutUserInfo(info);
}
var clientId = await _storage.PutUserInfo(info);
return new RegistrationResponse{ClientId = clientId};
}
private void Validate(RegistrationRequest request)
private static void Validate(RegistrationRequest request)
{
if (request == null)
{
@ -43,19 +40,25 @@ namespace QRBee.Api.Services
var email = request.Email;
var dateOfBirth = request.DateOfBirth;
if (string.IsNullOrEmpty(name) || name.All(char.IsLetter)==false || name.Length>=30)
if (string.IsNullOrEmpty(name) || name.All(char.IsLetter)==false || name.Length>=MaxNameLength)
{
// throw exception
throw new ApplicationException($"Name \"{name}\" isn't valid");
}
var freq = Regex.Matches(email, '@'.ToString()).Count;
var freq = Regex.Matches(email, @"[^@]+@[^@]+").Count;
if (string.IsNullOrEmpty(email) || email.Contains('@')==false || freq>=2 || email.Length >= 30)
if (string.IsNullOrEmpty(email) || email.IndexOf('@')<0 || freq>=2 || email.Length >= MaxEmailLength)
{
// throw exception
throw new ApplicationException($"Email \"{email}\" isn't valid");
}
// DateOfBirth check
if (!DateTime.TryParseExact(dateOfBirth, "yyyy-MM-dd", null, DateTimeStyles.AssumeUniversal, out var check)
|| check > DateTime.UtcNow - TimeSpan.FromDays(365 * 8)
|| check < DateTime.UtcNow - TimeSpan.FromDays(365 * 100)
)
{
throw new ApplicationException($"DateOfBirth \"{dateOfBirth}\" isn't valid");
}
}
@ -64,11 +67,5 @@ namespace QRBee.Api.Services
return new UserInfo(request.Name, request.Email, request.DateOfBirth);
}
private bool UserExists(UserInfo info)
{
var user = _storage.GetUserInfo(info.Email);
return user == info;
}
}
}

View File

@ -2,9 +2,7 @@
"QRBeeDatabase": {
"ConnectionString": "mongodb://localhost:27017",
"DatabaseName": "QRBee",
"User": "",
"Password": ""
"DatabaseName": "QRBee"
},
"Logging": {