#865 more for the authentication

pull/1389/head
tidusjar 8 years ago
parent 98fb15c263
commit cf6b5c5138

@ -0,0 +1,14 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Ombi.Store.Entities;
namespace Ombi.Core.IdentityResolver
{
public interface IUserIdentityManager
{
Task CreateUser(User user);
Task<bool> CredentialsValid(string username, string password);
Task<User> GetUser(string username);
Task<IEnumerable<User>> GetUsers();
}
}

@ -0,0 +1,90 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2017 Jamie Rees
// File: UserIdentityManager.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;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Cryptography.KeyDerivation;
using Ombi.Store.Entities;
using Ombi.Store.Repository;
namespace Ombi.Core.IdentityResolver
{
public class UserIdentityManager : IUserIdentityManager
{
public UserIdentityManager(IUserRepository userRepository)
{
UserRepository = userRepository;
}
private IUserRepository UserRepository { get; }
public async Task<bool> CredentialsValid(string username, string password)
{
var user = await UserRepository.GetUser(username);
var hashedPass = HashPassword(password);
return hashedPass.Equals(user.Password);
}
public async Task<User> GetUser(string username)
{
return await UserRepository.GetUser(username);
}
public async Task<IEnumerable<User>> GetUsers()
{
return await UserRepository.GetUsers();
}
public async Task CreateUser(User user)
{
user.Password = HashPassword(user.Password);
await UserRepository.CreateUser(user);
}
private string HashPassword(string password)
{
// generate a 128-bit salt using a secure PRNG
byte[] salt = new byte[128 / 8];
using (var rng = RandomNumberGenerator.Create())
{
rng.GetBytes(salt);
}
// derive a 256-bit subkey (use HMACSHA1 with 10,000 iterations)
var hashed = Convert.ToBase64String(KeyDerivation.Pbkdf2(
password: password,
salt: salt,
prf: KeyDerivationPrf.HMACSHA1,
iterationCount: 10000,
numBytesRequested: 256 / 8));
return hashed;
}
}
}

@ -6,6 +6,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="1.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="1.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Design" Version="1.1.1" />
<PackageReference Include="Newtonsoft.Json" Version="10.0.2" />

@ -6,6 +6,7 @@ using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Ombi.Core;
using Ombi.Core.Engine;
using Ombi.Core.IdentityResolver;
using Ombi.Core.Models.Requests;
using Ombi.Core.Requests.Models;
using Ombi.Core.Settings;
@ -48,6 +49,7 @@ namespace Ombi.DependencyInjection
services.AddTransient<IOmbiContext, OmbiContext>();
services.AddTransient<IRequestRepository, RequestJsonRepository>();
services.AddTransient<ISettingsRepository, SettingsJsonRepository>();
services.AddTransient<IUserRepository, UserRepository>();
services.AddTransient(typeof(ISettingsService<>), typeof(SettingsServiceV2<>));
return services;
}
@ -59,6 +61,7 @@ namespace Ombi.DependencyInjection
public static IServiceCollection RegisterIdentity(this IServiceCollection services)
{
services.AddTransient<IUserIdentityManager, UserIdentityManager>();
services.AddAuthorization(auth =>
{
auth.AddPolicy("Bearer", new AuthorizationPolicyBuilder()

@ -12,5 +12,6 @@ namespace Ombi.Store.Context
Task<int> SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken));
DbSet<RequestBlobs> Requests { get; set; }
DbSet<GlobalSettings> Settings { get; set; }
DbSet<User> Users { get; set; }
}
}

@ -16,6 +16,7 @@ namespace Ombi.Store.Context
}
public DbSet<RequestBlobs> Requests { get; set; }
public DbSet<GlobalSettings> Settings { get; set; }
public DbSet<User> Users { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{

@ -0,0 +1,48 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2017 Jamie Rees
// File: Users.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.Security.Claims;
namespace Ombi.Store.Entities
{
public class User : Entity
{
public string Username { get; set; }
public string Alias { get; set; }
public Claim[] Claims { get; set; }
public string EmailAddress { get; set; }
public string Password { get; set; }
public UserType UserType { get; set; }
}
public enum UserType
{
LocalUser = 1,
PlexUser = 2,
EmbyUser = 3,
}
}

@ -0,0 +1,13 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Ombi.Store.Entities;
namespace Ombi.Store.Repository
{
public interface IUserRepository
{
Task CreateUser(User user);
Task<User> GetUser(string username);
Task<IEnumerable<User>> GetUsers();
}
}

@ -0,0 +1,62 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2017 Jamie Rees
// File: UserRepository.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.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Ombi.Store.Context;
using Ombi.Store.Entities;
namespace Ombi.Store.Repository
{
public class UserRepository : IUserRepository
{
public UserRepository(IOmbiContext ctx)
{
Db = ctx;
}
private IOmbiContext Db { get; }
public async Task<User> GetUser(string username)
{
return await Db.Users.FirstOrDefaultAsync(x => x.Username.ToLower() == username.ToLower());
}
public async Task CreateUser(User user)
{
Db.Users.Add(user);
await Db.SaveChangesAsync();
}
public async Task<IEnumerable<User>> GetUsers()
{
return await Db.Users.ToListAsync();
}
}
}

@ -8,7 +8,10 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using Ombi.Core.IdentityResolver;
using Ombi.Models;
using Ombi.Store.Context;
using Ombi.Store.Repository;
namespace Ombi.Auth
{
@ -23,7 +26,6 @@ namespace Ombi.Auth
IOptions<TokenProviderOptions> options)
{
_next = next;
_options = options.Value;
ThrowIfInvalidOptions(_options);
@ -64,7 +66,7 @@ namespace Ombi.Auth
userInfo = JsonConvert.DeserializeObject<UserAuthModel>(body);
}
var identity = await _options.IdentityResolver(userInfo.Username, userInfo.Password);
var identity = await _options.IdentityResolver(userInfo.Username, userInfo.Password, new UserIdentityManager(new UserRepository(new OmbiContext())));
if (identity == null)
{
context.Response.StatusCode = 400;

@ -5,6 +5,7 @@ using System.Security.Claims;
using System.Security.Cryptography;
using System.Threading.Tasks;
using Microsoft.IdentityModel.Tokens;
using Ombi.Core.IdentityResolver;
namespace Ombi.Auth
{
@ -40,7 +41,7 @@ namespace Ombi.Auth
/// <summary>
/// Resolves a user identity given a username and password.
/// </summary>
public Func<string, string, Task<ClaimsIdentity>> IdentityResolver { get; set; }
public Func<string, string, IUserIdentityManager, Task<ClaimsIdentity>> IdentityResolver { get; set; }
/// <summary>
/// Generates a random value (nonce) for each generated token.

@ -7,11 +7,13 @@ using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using Ombi.Auth;
using Ombi.Core.IdentityResolver;
namespace Ombi
{
public partial class Startup
{
public SymmetricSecurityKey signingKey;
private void ConfigureAuth(IApplicationBuilder app)
{
@ -66,32 +68,17 @@ namespace Ombi
}
private Task<ClaimsIdentity> GetIdentity(string username, string password)
private async Task<ClaimsIdentity> GetIdentity(string username, string password, IUserIdentityManager userIdentityManager)
{
// DEMO CODE, DON NOT USE IN PRODUCTION!!!
if (username == "TEST" && password == "TEST123")
{
var claim = new ClaimsIdentity(new GenericIdentity(username, "Token"),
new[]
{
//new Claim(ClaimTypes.Role, "Admin"),
new Claim(ClaimTypes.Name, "Test"),
});
claim.AddClaim(new Claim(ClaimsIdentity.DefaultRoleClaimType, "Admin", ClaimValueTypes.String));
return Task.FromResult(claim);
}
if (username == "TEST2" && password == "TEST123")
var validLogin = await userIdentityManager.CredentialsValid(username, password);
if (!validLogin)
{
return Task.FromResult(new ClaimsIdentity(new GenericIdentity(username, "Token"), new Claim[] {
new Claim(ClaimTypes.Role, "User"),
new Claim(ClaimTypes.Name, "Test2"), }));
return null;
}
// Account doesn't exists
return Task.FromResult<ClaimsIdentity>(null);
var user = await userIdentityManager.GetUser(username);
var claim = new ClaimsIdentity(new GenericIdentity(user.Username, "Token"), user.Claims);
return claim;
}
}
}
Loading…
Cancel
Save