mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-07-08 06:00:50 -07:00
Hooked up most of #49 Just the validation messages need to be done.
***NOTE: Due to we are now hashing the passwords and reading from a new table, We will need to create a new administrator account.***
This commit is contained in:
parent
da8b6340ae
commit
8f7098d048
13 changed files with 250 additions and 21 deletions
|
@ -26,11 +26,13 @@
|
|||
#endregion
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Security;
|
||||
|
||||
using Nancy;
|
||||
using Nancy.Authentication.Forms;
|
||||
using Nancy.Security;
|
||||
|
||||
using PlexRequests.Helpers;
|
||||
using PlexRequests.Store;
|
||||
|
||||
namespace PlexRequests.Core
|
||||
|
@ -44,7 +46,7 @@ namespace PlexRequests.Core
|
|||
private static ISqliteConfiguration Db { get; set; }
|
||||
public IUserIdentity GetUserFromIdentifier(Guid identifier, NancyContext context)
|
||||
{
|
||||
var repo = new UserRepository<UserModel>(Db);
|
||||
var repo = new UserRepository<UsersModel>(Db);
|
||||
|
||||
var user = repo.Get(identifier.ToString());
|
||||
|
||||
|
@ -61,35 +63,65 @@ namespace PlexRequests.Core
|
|||
|
||||
public static Guid? ValidateUser(string username, string password)
|
||||
{
|
||||
var repo = new UserRepository<UserModel>(Db);
|
||||
var repo = new UserRepository<UsersModel>(Db);
|
||||
var users = repo.GetAll();
|
||||
var userRecord = users.FirstOrDefault(u => u.UserName.Equals(username, StringComparison.InvariantCultureIgnoreCase) && u.Password.Equals(password)); // TODO hashing
|
||||
|
||||
if (userRecord == null)
|
||||
foreach (var u in users)
|
||||
{
|
||||
return null;
|
||||
if (username == u.UserName)
|
||||
{
|
||||
var passwordMatch = PasswordHasher.VerifyPassword(password, u.Salt, u.Hash);
|
||||
if (passwordMatch)
|
||||
{
|
||||
return new Guid(u.User);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new Guid(userRecord.User);
|
||||
return null;
|
||||
}
|
||||
|
||||
public static bool DoUsersExist()
|
||||
{
|
||||
var repo = new UserRepository<UserModel>(Db);
|
||||
var repo = new UserRepository<UsersModel>(Db);
|
||||
var users = repo.GetAll();
|
||||
|
||||
return users.Any();
|
||||
}
|
||||
|
||||
public static Guid? CreateUser(string username, string password)
|
||||
{
|
||||
var repo = new UserRepository<UserModel>(Db);
|
||||
var repo = new UserRepository<UsersModel>(Db);
|
||||
var salt = PasswordHasher.GenerateSalt();
|
||||
|
||||
var userModel = new UserModel { UserName = username, User = Guid.NewGuid().ToString(), Password = password };
|
||||
var userModel = new UsersModel { UserName = username, User = Guid.NewGuid().ToString(), Salt = salt, Hash = PasswordHasher.ComputeHash(password, salt)};
|
||||
repo.Insert(userModel);
|
||||
|
||||
var userRecord = repo.Get(userModel.User);
|
||||
|
||||
return new Guid(userRecord.User);
|
||||
}
|
||||
|
||||
public static bool UpdateUser(string username, string oldPassword, string newPassword)
|
||||
{
|
||||
var repo = new UserRepository<UsersModel>(Db);
|
||||
var users = repo.GetAll();
|
||||
var userToChange = users.FirstOrDefault(x => x.UserName == username);
|
||||
if (userToChange == null)
|
||||
return false;
|
||||
|
||||
var passwordMatch = PasswordHasher.VerifyPassword(oldPassword, userToChange.Salt, userToChange.Hash);
|
||||
if (!passwordMatch)
|
||||
{
|
||||
throw new SecurityException("Password does not match");
|
||||
}
|
||||
|
||||
var newSalt = PasswordHasher.GenerateSalt();
|
||||
var newHash = PasswordHasher.ComputeHash(newPassword, newSalt);
|
||||
|
||||
userToChange.Hash = newHash;
|
||||
userToChange.Salt = newSalt;
|
||||
|
||||
return repo.Update(userToChange);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
50
PlexRequests.Helpers.Tests/PasswordHasherTests.cs
Normal file
50
PlexRequests.Helpers.Tests/PasswordHasherTests.cs
Normal file
|
@ -0,0 +1,50 @@
|
|||
#region Copyright
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2016 Jamie Rees
|
||||
// File: AssemblyHelperTests.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
using System.Diagnostics;
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace PlexRequests.Helpers.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class PasswordHasherTests
|
||||
{
|
||||
[Test]
|
||||
public void TestHash()
|
||||
{
|
||||
var password = "abcdef";
|
||||
var salt = PasswordHasher.GenerateSalt();
|
||||
var hash = PasswordHasher.ComputeHash(password, salt);
|
||||
|
||||
Assert.That(hash, Is.Not.EqualTo(password));
|
||||
|
||||
var match = PasswordHasher.VerifyPassword(password, salt, hash);
|
||||
|
||||
Assert.That(match, Is.True);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -70,6 +70,7 @@
|
|||
</Otherwise>
|
||||
</Choose>
|
||||
<ItemGroup>
|
||||
<Compile Include="PasswordHasherTests.cs" />
|
||||
<Compile Include="HtmlRemoverTests.cs" />
|
||||
<Compile Include="AssemblyHelperTests.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
|
|
69
PlexRequests.Helpers/PasswordHasher.cs
Normal file
69
PlexRequests.Helpers/PasswordHasher.cs
Normal file
|
@ -0,0 +1,69 @@
|
|||
#region Copyright
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2016 Jamie Rees
|
||||
// File: PasswordHasher.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Cryptography;
|
||||
namespace PlexRequests.Helpers
|
||||
{
|
||||
|
||||
public static class PasswordHasher
|
||||
{
|
||||
// 24 = 192 bits
|
||||
private const int SaltByteSize = 24;
|
||||
private const int HashByteSize = 24;
|
||||
private const int HasingIterationsCount = 10101;
|
||||
|
||||
public static byte[] ComputeHash(string password, byte[] salt, int iterations = HasingIterationsCount, int hashByteSize = HashByteSize)
|
||||
{
|
||||
var hashGenerator = new Rfc2898DeriveBytes(password, salt) { IterationCount = iterations };
|
||||
return hashGenerator.GetBytes(hashByteSize);
|
||||
}
|
||||
|
||||
public static byte[] GenerateSalt(int saltByteSize = SaltByteSize)
|
||||
{
|
||||
var saltGenerator = new RNGCryptoServiceProvider();
|
||||
var salt = new byte[saltByteSize];
|
||||
saltGenerator.GetBytes(salt);
|
||||
return salt;
|
||||
}
|
||||
|
||||
public static bool VerifyPassword(string password, byte[] passwordSalt, byte[] passwordHash)
|
||||
{
|
||||
var computedHash = ComputeHash(password, passwordSalt);
|
||||
return AreHashesEqual(computedHash, passwordHash);
|
||||
}
|
||||
|
||||
//Length constant verification - prevents timing attack
|
||||
private static bool AreHashesEqual(IReadOnlyList<byte> firstHash, IReadOnlyList<byte> secondHash)
|
||||
{
|
||||
var minHashLength = firstHash.Count <= secondHash.Count ? firstHash.Count : secondHash.Count;
|
||||
var xor = firstHash.Count ^ secondHash.Count;
|
||||
for (var i = 0; i < minHashLength; i++)
|
||||
xor |= firstHash[i] ^ secondHash[i];
|
||||
return 0 == xor;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -53,6 +53,7 @@
|
|||
<Compile Include="LoggingHelper.cs" />
|
||||
<Compile Include="MemoryCacheProvider.cs" />
|
||||
<Compile Include="ObjectCopier.cs" />
|
||||
<Compile Include="PasswordHasher.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="SerializerSettings.cs" />
|
||||
<Compile Include="StringCipher.cs" />
|
||||
|
|
|
@ -70,6 +70,8 @@
|
|||
<Compile Include="Repository\RequestJsonRepository.cs" />
|
||||
<Compile Include="GenericRepository.cs" />
|
||||
<Compile Include="RequestedModel.cs" />
|
||||
<Compile Include="UserEntity.cs" />
|
||||
<Compile Include="UsersModel.cs" />
|
||||
<Compile Include="UserRepository.cs" />
|
||||
<Compile Include="Sql.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
|
@ -77,7 +79,6 @@
|
|||
<DependentUpon>Sql.resx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="TableCreation.cs" />
|
||||
<Compile Include="UserModel.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="sqlite3.dll">
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
--Any DB changes need to be made in this file.
|
||||
|
||||
CREATE TABLE IF NOT EXISTS User
|
||||
CREATE TABLE IF NOT EXISTS Users
|
||||
(
|
||||
Id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
User varchar(50) NOT NULL ,
|
||||
UserGuid varchar(50) NOT NULL ,
|
||||
UserName varchar(50) NOT NULL,
|
||||
Password varchar(100) NOT NULL
|
||||
Salt BLOB NOT NULL,
|
||||
Hash BLOB NOT NULL
|
||||
);
|
||||
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#region Copyright
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2016 Jamie Rees
|
||||
// File: UserModel.cs
|
||||
// File: UserEntity.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
|
@ -28,11 +28,11 @@ using Dapper.Contrib.Extensions;
|
|||
|
||||
namespace PlexRequests.Store
|
||||
{
|
||||
[Table("User")]
|
||||
public class UserModel : Entity
|
||||
public class UserEntity
|
||||
{
|
||||
[Key]
|
||||
public int Id { get; set; }
|
||||
public string User { get; set; }
|
||||
public string UserName { get; set; }
|
||||
public string Password { get; set; }
|
||||
}
|
||||
}
|
|
@ -32,7 +32,7 @@ using Dapper.Contrib.Extensions;
|
|||
|
||||
namespace PlexRequests.Store
|
||||
{
|
||||
public class UserRepository<T> : IRepository<T> where T : UserModel
|
||||
public class UserRepository<T> : IRepository<T> where T : UserEntity
|
||||
{
|
||||
public UserRepository(ISqliteConfiguration config)
|
||||
{
|
||||
|
|
37
PlexRequests.Store/UsersModel.cs
Normal file
37
PlexRequests.Store/UsersModel.cs
Normal file
|
@ -0,0 +1,37 @@
|
|||
#region Copyright
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2016 Jamie Rees
|
||||
// File: UserModel.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
using Dapper.Contrib.Extensions;
|
||||
|
||||
namespace PlexRequests.Store
|
||||
{
|
||||
[Table("Users")]
|
||||
public class UsersModel : UserEntity
|
||||
{
|
||||
public byte[] Hash { get; set; }
|
||||
public byte[] Salt { get; set; }
|
||||
}
|
||||
}
|
|
@ -30,6 +30,8 @@ using System.Dynamic;
|
|||
using Nancy;
|
||||
using Nancy.Authentication.Forms;
|
||||
using Nancy.Extensions;
|
||||
using Nancy.Responses.Negotiation;
|
||||
using Nancy.Security;
|
||||
|
||||
using PlexRequests.Core;
|
||||
using PlexRequests.UI.Models;
|
||||
|
@ -81,7 +83,6 @@ namespace PlexRequests.UI.Modules
|
|||
|
||||
return View["Login/Register", model];
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
Post["/register"] = x =>
|
||||
|
@ -96,6 +97,30 @@ namespace PlexRequests.UI.Modules
|
|||
Session[SessionKeys.UsernameKey] = username;
|
||||
return this.LoginAndRedirect((Guid)userId);
|
||||
};
|
||||
|
||||
Get["/changepassword"] = _ => ChangePassword();
|
||||
Post["/changepassword"] = _ => ChangePasswordPost();
|
||||
}
|
||||
|
||||
private Negotiator ChangePassword()
|
||||
{
|
||||
this.RequiresAuthentication();
|
||||
return View["ChangePassword"];
|
||||
}
|
||||
|
||||
private Negotiator ChangePasswordPost()
|
||||
{
|
||||
var username = Context.CurrentUser.UserName;
|
||||
var oldPass = Request.Form.OldPassword;
|
||||
var newPassword = Request.Form.NewPassword;
|
||||
var newPasswordAgain = Request.Form.NewPasswordAgain;
|
||||
if (!newPassword.Equals(newPasswordAgain))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
var result = UserMapper.UpdateUser(username, oldPass, newPassword);
|
||||
return View["ChangePassword"];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -328,6 +328,9 @@
|
|||
<Content Include="Views\Admin\Sickrage.cshtml">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Views\Login\ChangePassword.cshtml">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<None Include="Web.Debug.config">
|
||||
<DependentUpon>web.config</DependentUpon>
|
||||
</None>
|
||||
|
|
9
PlexRequests.UI/Views/Login/ChangePassword.cshtml
Normal file
9
PlexRequests.UI/Views/Login/ChangePassword.cshtml
Normal file
|
@ -0,0 +1,9 @@
|
|||
<form method="POST">
|
||||
<br />
|
||||
Old Password <input class="form-control" name="OldPassword" type="password" />
|
||||
New Password <input class="form-control" name="NewPassword" type="password" />
|
||||
New Password again <input class="form-control" name="NewPasswordAgain" type="password" />
|
||||
<br />
|
||||
<br />
|
||||
<input class="btn btn-success-outline" type="submit" value="Change Password" />
|
||||
</form>
|
Loading…
Add table
Add a link
Reference in a new issue