Addressed the unsecure login HTTP endpoint and user the Performers property instead of the deprecated Artists property from the TagLib library. #38

This commit is contained in:
amazing-username
2019-05-11 16:42:29 -04:00
parent c0ac3cdd30
commit 9bfb43e46a
13 changed files with 267 additions and 60 deletions
+36 -11
View File
@@ -6,6 +6,7 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Icarus.Controllers.Managers; using Icarus.Controllers.Managers;
using Icarus.Controllers.Utilities; using Icarus.Controllers.Utilities;
@@ -20,6 +21,7 @@ namespace Icarus.Controllers
{ {
#region Fields #region Fields
private IConfiguration _config; private IConfiguration _config;
private ILogger<LoginController> _logger;
#endregion #endregion
@@ -28,9 +30,10 @@ namespace Icarus.Controllers
#region Contructors #region Contructors
public LoginController(IConfiguration config) public LoginController(IConfiguration config, ILogger<LoginController> logger)
{ {
_config = config; _config = config;
_logger = logger;
} }
#endregion #endregion
@@ -38,27 +41,49 @@ namespace Icarus.Controllers
#region HTTP endpoints #region HTTP endpoints
public IActionResult Post([FromBody] User user) public IActionResult Post([FromBody] User user)
{ {
// TODO: Secure this HTTP endpoint. #38
// Currently there is no check done to determine whether or not the
// user's password sent with the request matches the password stored
// in the database. In fact there is not check if the user credentials
// sent with this request even exist in the database. I knowingly left
// this bug in here for the sole purpose of making it easier to test
// but it should now be addressed.
UserStoreContext context = HttpContext UserStoreContext context = HttpContext
.RequestServices .RequestServices
.GetService(typeof(UserStoreContext)) as UserStoreContext; .GetService(typeof(UserStoreContext)) as UserStoreContext;
_logger.LogInformation("Starting process of validating credentials");
var message = "Invalid credentials";
var password = user.Password;
var loginRes = new LoginResult
{
Username = user.Username
};
if (context.DoesUserExist(user))
{
user = context.RetrieveUser(user); user = context.RetrieveUser(user);
Console.WriteLine($"Username: {user.Username}");
var validatePass = new PasswordEncryption();
var validated = validatePass.VerifyPassword(user, password);
if (!validated)
{
loginRes.Message = message;
_logger.LogInformation(message);
return Ok(loginRes);
}
_logger.LogInformation("Successfully validated user credentials");
TokenManager tk = new TokenManager(_config); TokenManager tk = new TokenManager(_config);
LoginResult loginRes = tk.RetrieveLoginResult(user); loginRes = tk.RetrieveLoginResult(user);
return Ok(loginRes); return Ok(loginRes);
} }
else
{
loginRes.Message = message;
return NotFound(loginRes);
}
}
#endregion #endregion
} }
} }
+2 -1
View File
@@ -59,7 +59,8 @@ namespace Icarus.Controllers.Managers
Username = user.Username, Username = user.Username,
Token = tokenResult.AccessToken, Token = tokenResult.AccessToken,
TokenType = tokenResult.TokenType, TokenType = tokenResult.TokenType,
Expiration = tokenResult.Expiration Expiration = tokenResult.Expiration,
Message = "Successfully retrieved token"
}; };
} }
+21 -5
View File
@@ -35,21 +35,37 @@ namespace Icarus.Controllers
#endregion #endregion
[HttpPost] [HttpPost]
public void Post([FromBody] User user) public IActionResult Post([FromBody] User user)
{ {
Console.WriteLine($"Username: {user.Username}");
Console.WriteLine($"Password: {user.Password}");
PasswordEncryption pe = new PasswordEncryption(); PasswordEncryption pe = new PasswordEncryption();
user.Password = pe.HashPassword(user); user.Password = pe.HashPassword(user);
user.EmailVerified = false; user.EmailVerified = false;
Console.WriteLine($"Hashed Password: {user.Password}");
UserStoreContext context = HttpContext UserStoreContext context = HttpContext
.RequestServices .RequestServices
.GetService(typeof(UserStoreContext)) as UserStoreContext; .GetService(typeof(UserStoreContext)) as UserStoreContext;
context.SaveUser(user); context.SaveUser(user);
var registerResult = new RegisterResult
{
Username = user.Username
};
if (context.DoesUserExist(user))
{
registerResult.Message = "Successful registration";
registerResult.SuccessfullyRegistered = true;
return Ok(registerResult);
}
else
{
registerResult.Message = "Registration failed";
registerResult.SuccessfullyRegistered = false;
return Ok(registerResult);
}
} }
} }
} }
+2 -2
View File
@@ -51,7 +51,7 @@ namespace Icarus.Controllers.Utilities
{ {
TagLib.File fileTag = TagLib.File.Create(filePath); TagLib.File fileTag = TagLib.File.Create(filePath);
_title = fileTag.Tag.Title; _title = fileTag.Tag.Title;
_artist = string.Join("", fileTag.Tag.Artists); _artist = string.Join("", fileTag.Tag.Performers);
_album = fileTag.Tag.Album; _album = fileTag.Tag.Album;
_genre = string.Join("", fileTag.Tag.Genres); _genre = string.Join("", fileTag.Tag.Genres);
_year = (int)fileTag.Tag.Year; _year = (int)fileTag.Tag.Year;
@@ -141,7 +141,7 @@ namespace Icarus.Controllers.Utilities
break; break;
case "artists": case "artists":
_updatedSong.Artist = artist; _updatedSong.Artist = artist;
fileTag.Tag.Artists = new []{artist}; fileTag.Tag.Performers = new []{artist};
break; break;
case "album": case "album":
_updatedSong.AlbumTitle = album; _updatedSong.AlbumTitle = album;
@@ -3,6 +3,7 @@ using System.Security.Cryptography;
using BCrypt.Net; using BCrypt.Net;
using Microsoft.AspNetCore.Cryptography.KeyDerivation; using Microsoft.AspNetCore.Cryptography.KeyDerivation;
using NLog;
using Icarus.Models; using Icarus.Models;
@@ -11,6 +12,7 @@ namespace Icarus.Controllers.Utilities
public class PasswordEncryption public class PasswordEncryption
{ {
#region Fields #region Fields
private static Logger _logger = NLog.LogManager.GetCurrentClassLogger();
#endregion #endregion
@@ -23,6 +25,23 @@ namespace Icarus.Controllers.Utilities
#region Methods #region Methods
public bool VerifyPassword(User user, string password)
{
try
{
var result = BCrypt.Net.BCrypt.Verify(password, user.Password);
return result;
}
catch (Exception ex)
{
var msg = ex.Message;
_logger.Error(msg, "An error occurred");
}
return false;
}
public string HashPassword(User user) public string HashPassword(User user)
{ {
try try
@@ -30,12 +49,15 @@ namespace Icarus.Controllers.Utilities
string hashedPassword = string.Empty; string hashedPassword = string.Empty;
hashedPassword = BCrypt.Net.BCrypt.HashPassword(user.Password); hashedPassword = BCrypt.Net.BCrypt.HashPassword(user.Password);
_logger.Info("Successfully hashed password");
return hashedPassword; return hashedPassword;
} }
catch (Exception ex) catch (Exception ex)
{ {
var exMsg = ex.Message; var exMsg = ex.Message;
_logger.Error(exMsg, "An error occurred");
} }
return null; return null;
+12
View File
@@ -0,0 +1,12 @@
using System;
using Newtonsoft.Json;
namespace Icarus.Models
{
public class BaseResult
{
[JsonProperty("message")]
public string Message { get; set; }
}
}
+2 -2
View File
@@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using MySql.Data.MySqlClient; using MySql.Data.MySqlClient;
using NLog; //using NLog;
using Icarus.Models; using Icarus.Models;
@@ -12,7 +12,7 @@ namespace Icarus.Models.Context
public class AlbumStoreContext : BaseStoreContext public class AlbumStoreContext : BaseStoreContext
{ {
#region Fields #region Fields
private static Logger _logger = NLog.LogManager.GetCurrentClassLogger(); //private static Logger _logger = NLog.LogManager.GetCurrentClassLogger();
#endregion #endregion
+2 -2
View File
@@ -4,7 +4,7 @@ using System.Globalization;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using MySql.Data.MySqlClient; using MySql.Data.MySqlClient;
using NLog; //using NLog;
using Icarus.Models; using Icarus.Models;
@@ -13,7 +13,7 @@ namespace Icarus.Models.Context
public class ArtistStoreContext : BaseStoreContext public class ArtistStoreContext : BaseStoreContext
{ {
#region Fields #region Fields
private static Logger _logger = NLog.LogManager.GetCurrentClassLogger(); //private static Logger _logger = NLog.LogManager.GetCurrentClassLogger();
#endregion #endregion
+2
View File
@@ -1,6 +1,7 @@
using System; using System;
using MySql.Data.MySqlClient; using MySql.Data.MySqlClient;
using NLog;
namespace Icarus.Models.Context namespace Icarus.Models.Context
{ {
@@ -8,6 +9,7 @@ namespace Icarus.Models.Context
{ {
#region Fields #region Fields
protected string _connectionString; protected string _connectionString;
protected static Logger _logger = NLog.LogManager.GetCurrentClassLogger();
#endregion #endregion
+2 -2
View File
@@ -2,14 +2,14 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using MySql.Data.MySqlClient; using MySql.Data.MySqlClient;
using NLog; //using NLog;
namespace Icarus.Models.Context namespace Icarus.Models.Context
{ {
public class MusicStoreContext : BaseStoreContext public class MusicStoreContext : BaseStoreContext
{ {
#region Fields #region Fields
private static Logger _logger = NLog.LogManager.GetCurrentClassLogger(); //private static Logger _logger = NLog.LogManager.GetCurrentClassLogger();
#endregion #endregion
+143 -28
View File
@@ -31,15 +31,15 @@ namespace Icarus.Models.Context
{ {
try try
{ {
_logger.Info("Saving user");
using (MySqlConnection conn = GetConnection()) using (MySqlConnection conn = GetConnection())
{ {
conn.Open(); conn.Open();
string query = "INSERT INTO Users(Username, Password, Nickname," + string query = "INSERT INTO User(Username, Password, Nickname, Email" +
"Email, PhoneNumber, Firstname, Lastname, " + ", PhoneNumber, Firstname, Lastname, EmailVerified) " +
"EmailVerified, DateCreated, LastLogin) " + "VALUES(@Username, @Password, @Nickname, @Email, @PhoneNumber," +
"VALUES(@Username, @Password, @Nickname, " + " @Firstname, @Lastname, @EmailVerified)";
"@Email, @PhoneNumber, @Firstname, @Lastname," +
" @EmailVerified, @DateCreated, @LastLogin)";
using (MySqlCommand cmd = new MySqlCommand(query, conn)) using (MySqlCommand cmd = new MySqlCommand(query, conn))
{ {
cmd.Parameters.AddWithValue("@Username", user.Username); cmd.Parameters.AddWithValue("@Username", user.Username);
@@ -50,18 +50,19 @@ namespace Icarus.Models.Context
cmd.Parameters.AddWithValue("@Firstname", user.Firstname); cmd.Parameters.AddWithValue("@Firstname", user.Firstname);
cmd.Parameters.AddWithValue("@Lastname", user.Lastname); cmd.Parameters.AddWithValue("@Lastname", user.Lastname);
cmd.Parameters.AddWithValue("@EmailVerified", user.EmailVerified); cmd.Parameters.AddWithValue("@EmailVerified", user.EmailVerified);
cmd.Parameters.AddWithValue("@DateCreated", DateTime.Now);
cmd.Parameters.AddWithValue("@LastLogin", DateTime.Now);
cmd.ExecuteNonQuery(); cmd.ExecuteNonQuery();
} }
} }
_logger.Info("Successfully saved user");
} }
catch (Exception ex) catch (Exception ex)
{ {
var exMsg = ex.Message; var exMsg = ex.Message;
Console.WriteLine($"An error occurred:\n{exMsg}"); Console.WriteLine($"An error occurred:\n{exMsg}");
_logger.Error(exMsg, "An error occurred");
} }
} }
@@ -69,10 +70,12 @@ namespace Icarus.Models.Context
{ {
try try
{ {
_logger.Info("Retrieving user");
using (MySqlConnection conn = GetConnection()) using (MySqlConnection conn = GetConnection())
{ {
conn.Open(); conn.Open();
var query = "SELECT * FROM Users WHERE Username=@Username"; var query = "SELECT * FROM User WHERE Username=@Username";
using (MySqlCommand cmd = new MySqlCommand(query, conn)) using (MySqlCommand cmd = new MySqlCommand(query, conn))
{ {
@@ -80,36 +83,148 @@ namespace Icarus.Models.Context
using (var reader = cmd.ExecuteReader()) using (var reader = cmd.ExecuteReader())
{ {
while (reader.Read()) user = ParseSingleData(reader);
{ }
var dateCreated = reader["DateCreated"].ToString(); }
var lastLogin = reader["LastLogin"].ToString(); }
var parsedC = DateTime.Parse(dateCreated);
var parsedL = DateTime.Parse(lastLogin); _logger.Info("Successfully retrieved user");
user.Id = Convert.ToInt32(reader["Id"]);
user.Nickname = reader["Nickname"].ToString();
user.Email = reader["Email"].ToString();
user.PhoneNumber = reader["PhoneNumber"].ToString();
user.EmailVerified = (reader["EmailVerified"].ToString()) == "1";
user.Firstname = reader["Firstname"].ToString();
user.Lastname = reader["Lastname"].ToString();
user.DateCreated = DateTime.Parse(parsedC.ToString("yyyy-MM-dd HH:mm:ss"));
user.LastLogin = DateTime.Parse(parsedL.ToString("yyyy-MM-dd HH:mm:ss"));
}
}
}
}
return user; return user;
} }
catch (Exception ex) catch (Exception ex)
{ {
var exMsg = ex.Message; var exMsg = ex.Message;
Console.WriteLine($"An error occurred:\n{exMsg}"); Console.WriteLine($"An error occurred:\n{exMsg}");
_logger.Error(exMsg, "An error occurred");
} }
return null; return null;
} }
public bool DoesUserExist(User user)
{
var username = user.Username;
try
{
_logger.Info($"Checking to see if {user.Username} exists");
using (var conn = GetConnection())
{
conn.Open();
var query = "SELECT * FROM User WHERE Username=@Username";
using (var cmd = new MySqlCommand(query, conn))
{
cmd.Parameters.AddWithValue("@Username", user.Username);
using (var reader = cmd.ExecuteReader())
{
user = ParseSingleData(reader, true);
username = user.Username;
if (!string.IsNullOrEmpty(username))
{
_logger.Info($"The user {username} exists");
return true;
}
}
}
}
}
catch (Exception ex)
{
var msg = ex.Message;
_logger.Error(msg, "An error occurred");
}
_logger.Info($"The user {username} does not exists");
return false;
}
private User ParseSingleData(MySqlDataReader reader)
{
var user = new User();
while (reader.Read())
{
var id = Convert.ToInt32(reader["Id"].ToString());
var username = reader["Username"].ToString();
var password = reader["Password"].ToString();
var nickname = reader["Nickname"].ToString();
var email = reader["Email"].ToString();
var phoneNumber = reader["PhoneNumber"].ToString();
var emailVerified = reader["EmailVerified"].ToString() == "1";
var firstname = reader["Firstname"].ToString();
var lastname = reader["Lastname"].ToString();
var rawLastLogin = reader["LastLogin"].ToString();
var parsedDateCreated = DateTime.Parse(reader["DateCreated"].ToString());
var dateCreated = DateTime.Parse(parsedDateCreated.ToString("yyyy-MM-dd HH:mm:ss"));
if (!string.IsNullOrEmpty(rawLastLogin))
{
var parsedLastLogin = DateTime.Parse(rawLastLogin);
var lastLogin = DateTime.Parse(parsedLastLogin.ToString("yyyy-MM-dd HH:mm:ss"));
user.LastLogin = lastLogin;
}
user.Id = id;
user.Username = username;
user.Password = password;
user.Nickname = nickname;
user.Email = email;
user.PhoneNumber = phoneNumber;
user.EmailVerified = emailVerified;
user.Firstname = firstname;
user.Lastname = lastname;
user.DateCreated = dateCreated;
}
return user;
}
private User ParseSingleData(MySqlDataReader reader, bool ignoreLastLogin)
{
var user = new User();
while (reader.Read())
{
var id = Convert.ToInt32(reader["Id"].ToString());
var username = reader["Username"].ToString();
var nickname = reader["Nickname"].ToString();
var email = reader["Email"].ToString();
var phoneNumber = reader["PhoneNumber"].ToString();
var emailVerified = reader["EmailVerified"].ToString() == "1";
var firstname = reader["Firstname"].ToString();
var lastname = reader["Lastname"].ToString();
var parsedDateCreated = DateTime.Parse(reader["DateCreated"].ToString());
var dateCreated = DateTime.Parse(parsedDateCreated.ToString("yyyy-MM-dd HH:mm:ss"));
if (!ignoreLastLogin)
{
var parsedLastLogin = DateTime.Parse(reader["LastLogin"].ToString());
var lastLogin = DateTime.Parse(parsedLastLogin.ToString("yyyy-MM-dd HH:mm:ss"));
user.LastLogin = lastLogin;
}
user.Id = id;
user.Username = username;
user.Nickname = nickname;
user.Email = email;
user.PhoneNumber = phoneNumber;
user.EmailVerified = emailVerified;
user.Firstname = firstname;
user.Lastname = lastname;
user.DateCreated = dateCreated;
}
return user;
}
#endregion #endregion
} }
} }
+1 -1
View File
@@ -4,7 +4,7 @@ using Newtonsoft.Json;
namespace Icarus.Models namespace Icarus.Models
{ {
public class LoginResult public class LoginResult : BaseResult
{ {
[JsonProperty("id")] [JsonProperty("id")]
public int UserId { get; set; } public int UserId { get; set; }
+14
View File
@@ -0,0 +1,14 @@
using System;
using Newtonsoft.Json;
namespace Icarus.Models
{
public class RegisterResult : BaseResult
{
[JsonProperty("username")]
public string Username { get; set; }
[JsonProperty("successfully_registered")]
public bool SuccessfullyRegistered { get; set; }
}
}