using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Authentication;
using System.Text;
using System.Threading.Tasks;
using Tango.BL;
using Tango.BL.Entities;
using Tango.BL.Enumerations;
using Tango.FSE.BL.CacheEntities;
using Tango.FSE.BL.Web;
using Tango.FSE.Web.Messages;
using Tango.MachineService.Gateway;
namespace Tango.FSE.BL.Services
{
///
/// Represents authentication services used for authenticating FSE users.
///
///
public class AuthenticationService : FSEServiceBase
{
private const string LOGIN_RESPONSES_COLLECTION = "LoginResponses";
///
/// Performs authentication using the specified .
///
/// The request.
///
public Task Authenticate(LoginRequest request)
{
LogManager.Log($"Authenticating user '{request.Email}'...");
return DataResolver.Builder.New()
.ConfigureCascade(DataResolverNode.Online, DataResolverNode.DiskCache)
.Online((context) =>
{
try
{
var response = WebClient.Login(request).Result;
LogManager.Log("Online authentication was successful. Caching login response...");
try
{
using (var cache = DiskCache.CreateContext())
{
cache.Database.GetCollection(LOGIN_RESPONSES_COLLECTION).Upsert(new CachedLoginResponse()
{
Response = response,
Email = request.Email,
Password = request.Password,
EnvironmentId = Authentication.CurrentEnvironment.ID
});
}
}
catch (Exception ex)
{
LogManager.Log(ex, "Error caching online logging response.");
}
return response;
}
catch (Exception ex)
{
if (ex is AggregateException)
{
if ((ex as AggregateException).InnerExceptions.ToList().Exists(x => x.GetType() == typeof(AuthenticationException)))
{
//Delete cache and abort cascade if web request was successful but authentication failed.
LogManager.Log(ex, "Online request was successful but authentication failed. Removing cached response and aborting cascade.");
DropCachedResponse(request.Email);
context.AbortCascade();
}
}
throw ex;
}
})
.DiskCache((context) =>
{
try
{
using (var cache = DiskCache.CreateContext())
{
String currentEnvironmentID = Authentication.CurrentEnvironment.ID;
var cachedResponse = cache.Database.GetCollection(LOGIN_RESPONSES_COLLECTION).FindOne(x => x.Email.ToLower() == request.Email.ToLower() && x.Password == request.Password && x.EnvironmentId == currentEnvironmentID);
if (cachedResponse == null)
{
throw context.LastError;
}
cachedResponse.Response.PasswordChangeRequired = false;
return cachedResponse.Response;
}
}
catch (Exception ex)
{
throw new AuthenticationException("Could not login user online or via cache.", ex);
}
})
.BuildExecuteAsync();
}
///
/// Changes the specified user password.
///
/// The email.
/// The old password.
/// The new password.
///
public Task ChangePassword(String email, String oldPassword, String newPassword)
{
return Task.Factory.StartNew(() =>
{
using (ObservablesContext db = ObservablesContext.CreateDefault())
{
var oldPasswordHash = User.GetPasswordHash(oldPassword);
var newPasswordHash = User.GetPasswordHash(newPassword);
var user = db.Users.SingleOrDefault(x => x.Email.ToLower() == email.ToLower() && x.Password == oldPasswordHash);
if (user == null)
{
throw new AuthenticationException("Current email and password do not match.");
}
user.Password = newPasswordHash;
user.PasswordChangeRequired = false;
db.SaveChanges();
}
});
}
///
/// Sends the specified email a password reset information.
///
/// The email.
///
public Task SendForgotPasswordEmail(String email, EnvironmentConfiguration environment)
{
return Task.Factory.StartNew(() =>
{
FSEWebClient client = new FSEWebClient(environment.MachineServiceAddress);
var response = client.SendForgotPasswordEmail(new ForgotPasswordRequest()
{
Email = email,
MachineServiceAddress = environment.MachineServiceAddress,
Build = BuildProvider.Build
}).Result;
});
}
///
/// Drops the cached login response for the specified user.
///
/// The email.
public void DropCachedResponse(String email)
{
LogManager.Log($"Dropping cached response for user '{email}'...");
try
{
using (var cache = DiskCache.CreateContext())
{
cache.Database.GetCollection(LOGIN_RESPONSES_COLLECTION).DeleteMany(x => x.Email.ToLower() == email.ToLower());
}
}
catch (Exception ex)
{
LogManager.Log(ex, "Error dropping cached response.");
}
}
}
}