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."); } } } }