using System.Globalization;
using System.Text;
using System.Text.RegularExpressions;
using QRBee.Api.Services.Database;
using QRBee.Core;
using QRBee.Core.Data;
using QRBee.Core.Security;
namespace QRBee.Api.Services
{
///
/// Implementation of
///
public class QRBeeAPIService: IQRBeeAPI
{
private readonly IStorage _storage;
private readonly ISecurityService _securityService;
private readonly IPrivateKeyHandler _privateKeyHandler;
private static readonly object _lock = new ();
private const int MaxNameLength = 512;
private const int MaxEmailLength = 512;
public QRBeeAPIService(IStorage storage, ISecurityService securityService, IPrivateKeyHandler privateKeyHandler)
{
_storage = storage;
_securityService = securityService;
_privateKeyHandler = privateKeyHandler;
Init(_privateKeyHandler);
}
private static void Init(IPrivateKeyHandler privateKeyHandler)
{
lock (_lock)
{
if (!privateKeyHandler.Exists())
{
privateKeyHandler.GeneratePrivateKey();
}
}
}
public async Task Register(RegistrationRequest request)
{
Validate(request);
var info = Convert(request);
var clientId = await _storage.PutUserInfo(info);
var clientCertificate = _securityService.CreateCertificate(clientId,System.Convert.FromBase64String(request.CertificateRequest.RsaPublicKey));
//TODO save certificate to certificate mongoDB collection
return new RegistrationResponse
{
ClientId = clientId,
Certificate = _securityService.Serialize(clientCertificate)
};
}
public Task Update(string clientId, RegistrationRequest request)
{
Validate(request);
var info = Convert(request);
return _storage.UpdateUser(info);
}
public Task InsertTransaction(PaymentRequest value)
{
var info = Convert(value);
return _storage.PutTransactionInfo(info);
}
private void Validate(RegistrationRequest request)
{
if (request == null)
{
throw new NullReferenceException();
}
var name = request.Name;
var email = request.Email;
var dateOfBirth = request.DateOfBirth;
var certificateRequest = request.CertificateRequest;
if (string.IsNullOrEmpty(name) || name.All(char.IsLetter)==false || name.Length>=MaxNameLength)
{
throw new ApplicationException($"Name \"{name}\" isn't valid");
}
var freq = Regex.Matches(email, @"[^@]+@[^@]+").Count;
if (string.IsNullOrEmpty(email) || email.IndexOf('@')<0 || freq>=2 || email.Length >= MaxEmailLength)
{
throw new ApplicationException($"Email \"{email}\" isn't valid");
}
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");
}
//Check digital signature
var verified = _securityService.Verify(
Encoding.UTF8.GetBytes(certificateRequest.AsDataForSignature()),
Encoding.UTF8.GetBytes(certificateRequest.Signature),
_privateKeyHandler.LoadPrivateKey());
if (!verified)
{
throw new ApplicationException($"Digital signature is not valid.");
}
}
private static UserInfo Convert(RegistrationRequest request)
{
return new UserInfo(request.Name, request.Email, request.DateOfBirth);
}
private static TransactionInfo Convert(PaymentRequest request)
{
return new TransactionInfo(request, DateTime.UtcNow);
}
}
}