Fixed some issues around the identity

pull/1389/head
Jamie.Rees 7 years ago
parent cf6b5c5138
commit 8e04061e4e

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

@ -29,7 +29,9 @@ using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Threading.Tasks;
using AutoMapper;
using Microsoft.AspNetCore.Cryptography.KeyDerivation;
using Ombi.Core.Models;
using Ombi.Store.Entities;
using Ombi.Store.Repository;
@ -37,38 +39,43 @@ namespace Ombi.Core.IdentityResolver
{
public class UserIdentityManager : IUserIdentityManager
{
public UserIdentityManager(IUserRepository userRepository)
public UserIdentityManager(IUserRepository userRepository, IMapper mapper)
{
UserRepository = userRepository;
Mapper = mapper;
}
private IMapper Mapper { get; }
private IUserRepository UserRepository { get; }
public async Task<bool> CredentialsValid(string username, string password)
{
var user = await UserRepository.GetUser(username);
var hashedPass = HashPassword(password);
var hash = HashPassword(password, user.Salt);
return hashedPass.Equals(user.Password);
return hash.HashedPass.Equals(user.Password);
}
public async Task<User> GetUser(string username)
public async Task<UserDto> GetUser(string username)
{
return await UserRepository.GetUser(username);
return Mapper.Map<UserDto>(await UserRepository.GetUser(username));
}
public async Task<IEnumerable<User>> GetUsers()
public async Task<IEnumerable<UserDto>> GetUsers()
{
return await UserRepository.GetUsers();
return Mapper.Map<List<UserDto>>(await UserRepository.GetUsers());
}
public async Task CreateUser(User user)
public async Task CreateUser(UserDto userDto)
{
user.Password = HashPassword(user.Password);
var user = Mapper.Map<User>(userDto);
var result = HashPassword(user.Password);
user.Password = result.HashedPass;
user.Salt = result.Salt;
await UserRepository.CreateUser(user);
}
private string HashPassword(string password)
private UserHash HashPassword(string password)
{
// generate a 128-bit salt using a secure PRNG
byte[] salt = new byte[128 / 8];
@ -76,6 +83,12 @@ namespace Ombi.Core.IdentityResolver
{
rng.GetBytes(salt);
}
return HashPassword(password, salt);
}
private UserHash HashPassword(string password, byte[] salt)
{
// derive a 256-bit subkey (use HMACSHA1 with 10,000 iterations)
var hashed = Convert.ToBase64String(KeyDerivation.Pbkdf2(
password: password,
@ -84,7 +97,13 @@ namespace Ombi.Core.IdentityResolver
iterationCount: 10000,
numBytesRequested: 256 / 8));
return hashed;
return new UserHash { HashedPass = hashed, Salt = salt };
}
private class UserHash
{
public string HashedPass { get; set; }
public byte[] Salt { get; set; }
}
}
}

@ -0,0 +1,23 @@
using System.Collections.Generic;
using System.Security.Claims;
namespace Ombi.Core.Models
{
public class UserDto
{
public int Id { get; set; }
public string Username { get; set; }
public string Alias { get; set; }
public List<Claim> Claims { get; set; }
public string EmailAddress { get; set; }
public string Password { get; set; }
public byte[] Salt { get; set; }
public UserType UserType { get; set; }
}
public enum UserType
{
LocalUser = 1,
PlexUser = 2,
EmbyUser = 3,
}
}

@ -6,6 +6,8 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AutoMapper" Version="6.0.2" />
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="2.0.1" />
<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" />

@ -25,6 +25,7 @@ namespace Ombi.DependencyInjection
services.RegisterApi();
services.RegisterServices();
services.RegisterStore();
services.RegisterIdentity();
return services;
}
@ -56,6 +57,7 @@ namespace Ombi.DependencyInjection
public static IServiceCollection RegisterServices(this IServiceCollection services)
{
services.AddTransient<IRequestService, JsonRequestService>();
return services;
}

@ -5,8 +5,6 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="IdentityServer4" Version="1.4.2" />
<PackageReference Include="IdentityServer4.EntityFramework" Version="1.0.1" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="1.1.1" />
</ItemGroup>

@ -0,0 +1,33 @@
using System;
using System.Security.Claims;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Ombi.Helpers
{
public class ClaimConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(Claim));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JObject jo = JObject.Load(reader);
string type = (string)jo["Type"];
string value = (string)jo["Value"];
string valueType = (string)jo["ValueType"];
string issuer = (string)jo["Issuer"];
string originalIssuer = (string)jo["OriginalIssuer"];
return new Claim(type, value, valueType, issuer, originalIssuer);
}
public override bool CanWrite => false;
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
}

@ -8,4 +8,13 @@
<PackageReference Include="Newtonsoft.Json" Version="10.0.2" />
</ItemGroup>
<ItemGroup>
<Reference Include="System.IdentityModel">
<HintPath>..\..\..\..\..\..\..\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.6.1\System.IdentityModel.dll</HintPath>
</Reference>
<Reference Include="System.Security.Claims">
<HintPath>..\..\..\..\..\.nuget\packages\system.security.claims\4.3.0\ref\netstandard1.3\System.Security.Claims.dll</HintPath>
</Reference>
</ItemGroup>
</Project>

@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.6</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AutoMapper" Version="6.0.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Ombi.Api\Ombi.Api.csproj" />
<ProjectReference Include="..\Ombi.Core\Ombi.Core.csproj" />
<ProjectReference Include="..\Ombi.Helpers\Ombi.Helpers.csproj" />
<ProjectReference Include="..\Ombi.Store\Ombi.Store.csproj" />
<ProjectReference Include="..\Ombi\Ombi.csproj" />
</ItemGroup>
</Project>

@ -0,0 +1,15 @@
using System;
using AutoMapper;
namespace Ombi.Mapping.Maps
{
public class OmbiProfile : Profile
{
public OmbiProfile()
{
// Add as many of these lines as you need to map your objects
}
}
}

@ -0,0 +1,20 @@
using Microsoft.Extensions.DependencyInjection;
namespace Ombi.Mapping
{
public static class AutoMapperProfile
{
public static IServiceCollection AddOmbiMappingProfile(this IServiceCollection services)
{
var config = new AutoMapper.MapperConfiguration(cfg =>
{
cfg.AddProfile(new OmbiProfile());
});
var mapper = config.CreateMapper();
services.AddSingleton(mapper);
return services;
}
}
}

@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.6</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AutoMapper" Version="6.0.2" />
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="2.0.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Ombi.Api\Ombi.Api.csproj" />
<ProjectReference Include="..\Ombi.Core\Ombi.Core.csproj" />
<ProjectReference Include="..\Ombi.Helpers\Ombi.Helpers.csproj" />
<ProjectReference Include="..\Ombi.Store\Ombi.Store.csproj" />
</ItemGroup>
</Project>

@ -0,0 +1,15 @@
using System;
using AutoMapper;
using Ombi.Core.Models;
using Ombi.Store.Entities;
namespace Ombi.Mapping
{
public class OmbiProfile : Profile
{
public OmbiProfile()
{
CreateMap<User, UserDto>().ReverseMap();
}
}
}

@ -25,7 +25,12 @@
// ************************************************************************/
#endregion
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Security.Claims;
using Newtonsoft.Json;
using Ombi.Helpers;
namespace Ombi.Store.Entities
{
@ -33,10 +38,17 @@ namespace Ombi.Store.Entities
{
public string Username { get; set; }
public string Alias { get; set; }
public Claim[] Claims { get; set; }
public string ClaimsSerialized { get; set; }
public string EmailAddress { get; set; }
public string Password { get; set; }
public byte[] Salt { get; set; }
public UserType UserType { get; set; }
[NotMapped]
public List<Claim> Claims {
get => JsonConvert.DeserializeObject<List<Claim>>(ClaimsSerialized, new ClaimConverter());
set => ClaimsSerialized = JsonConvert.SerializeObject(value, new ClaimConverter());
}
}
public enum UserType

@ -6,7 +6,6 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="IdentityServer4.EntityFramework" Version="1.0.1" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="1.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="1.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Design" Version="1.1.1" />
@ -15,4 +14,7 @@
<ItemGroup>
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="1.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Ombi.Helpers\Ombi.Helpers.csproj" />
</ItemGroup>
</Project>

@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26228.10
VisualStudioVersion = 15.0.26403.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ombi", "Ombi\Ombi.csproj", "{C987AA67-AFE1-468F-ACD3-EAD5A48E1F6A}"
EndProject
@ -26,6 +26,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ombi.Store", "Ombi.Store\Om
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.DependencyInjection", "Ombi.DependencyInjection\Ombi.DependencyInjection.csproj", "{B39E4558-C557-48E7-AA74-19C5CD809617}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Mapping", "Ombi.Mapping\Ombi.Mapping.csproj", "{63E63511-1C7F-4162-8F92-8F7391B3C8A3}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Mappers", "Mappers", "{025FB189-2FFB-4F43-A64B-6F1B5A0D2065}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DI", "DI", "{410F36CF-9C60-428A-B191-6FD90610991A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -60,6 +66,10 @@ Global
{B39E4558-C557-48E7-AA74-19C5CD809617}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B39E4558-C557-48E7-AA74-19C5CD809617}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B39E4558-C557-48E7-AA74-19C5CD809617}.Release|Any CPU.Build.0 = Release|Any CPU
{63E63511-1C7F-4162-8F92-8F7391B3C8A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{63E63511-1C7F-4162-8F92-8F7391B3C8A3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{63E63511-1C7F-4162-8F92-8F7391B3C8A3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{63E63511-1C7F-4162-8F92-8F7391B3C8A3}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -67,5 +77,7 @@ Global
GlobalSection(NestedProjects) = preSolution
{132DA282-5894-4570-8916-D8C18ED2CE84} = {9293CA11-360A-4C20-A674-B9E794431BF5}
{EA31F915-31F9-4318-B521-1500CDF40DDF} = {9293CA11-360A-4C20-A674-B9E794431BF5}
{B39E4558-C557-48E7-AA74-19C5CD809617} = {410F36CF-9C60-428A-B191-6FD90610991A}
{63E63511-1C7F-4162-8F92-8F7391B3C8A3} = {025FB189-2FFB-4F43-A64B-6F1B5A0D2065}
EndGlobalSection
EndGlobal

@ -20,10 +20,11 @@ namespace Ombi.Auth
private readonly RequestDelegate _next;
private readonly TokenProviderOptions _options;
private readonly JsonSerializerSettings _serializerSettings;
private readonly IUserIdentityManager _identityManager;
public TokenProviderMiddleware(
RequestDelegate next,
IOptions<TokenProviderOptions> options)
IOptions<TokenProviderOptions> options, IUserIdentityManager manager)
{
_next = next;
_options = options.Value;
@ -33,6 +34,7 @@ namespace Ombi.Auth
{
Formatting = Formatting.Indented
};
_identityManager = manager;
}
public Task Invoke(HttpContext context)
@ -66,7 +68,7 @@ namespace Ombi.Auth
userInfo = JsonConvert.DeserializeObject<UserAuthModel>(body);
}
var identity = await _options.IdentityResolver(userInfo.Username, userInfo.Password, new UserIdentityManager(new UserRepository(new OmbiContext())));
var identity = await _options.IdentityResolver(userInfo.Username, userInfo.Password, _identityManager);
if (identity == null)
{
context.Response.StatusCode = 400;

@ -0,0 +1,26 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Ombi.Core.IdentityResolver;
using Ombi.Core.Models;
namespace Ombi.Controllers
{
[Authorize]
public class IdentityController : BaseV1ApiController
{
public IdentityController(IUserIdentityManager identity)
{
IdentityManager = identity;
}
private IUserIdentityManager IdentityManager { get; }
[HttpGet]
public async Task<UserDto> GetUser()
{
return await IdentityManager.GetUser(this.HttpContext.User.Identity.Name);
}
}
}

@ -8,16 +8,11 @@
</PropertyGroup>
<ItemGroup>
<Content Include="wwwroot\app\auth\auth - Copy.module.js.map">
<DependentUpon>auth - Copy.module.js</DependentUpon>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Include="wwwroot\app\auth\auth.guard.ts">
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Include="wwwroot\app\auth\auth.module.ts" />
<Content Include="wwwroot\app\auth\auth.service.ts" />
<Content Include="wwwroot\app\auth\AuthModule.ts" />
<Content Include="wwwroot\app\auth\IUserLogin.ts" />
<Content Include="wwwroot\app\login\login.component.html" />
<Content Include="wwwroot\app\login\login.component.ts" />
@ -33,8 +28,7 @@
<ItemGroup>
<PackageReference Include="IdentityServer4" Version="1.4.2" />
<PackageReference Include="IdentityServer4.EntityFramework" Version="1.0.1" />
<PackageReference Include="AutoMapper" Version="6.0.2" />
<PackageReference Include="Microsoft.AspNetCore" Version="1.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="1.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="1.1.2" />
@ -52,6 +46,7 @@
<ItemGroup>
<ProjectReference Include="..\Ombi.Core\Ombi.Core.csproj" />
<ProjectReference Include="..\Ombi.DependencyInjection\Ombi.DependencyInjection.csproj" />
<ProjectReference Include="..\Ombi.Mapping\Ombi.Mapping.csproj" />
<ProjectReference Include="..\Ombi.TheMovieDbApi\Ombi.TheMovieDbApi.csproj" />
</ItemGroup>

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Security.Claims;
using System.Security.Principal;
using System.Text;
@ -8,6 +9,7 @@ using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using Ombi.Auth;
using Ombi.Core.IdentityResolver;
using Ombi.Core.Models;
namespace Ombi
{
@ -53,23 +55,20 @@ namespace Ombi
TokenValidationParameters = tokenValidationParameters
});
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AutomaticAuthenticate = true,
AutomaticChallenge = true,
AuthenticationScheme = "Cookie",
CookieName = Configuration.GetSection("TokenAuthentication:CookieName").Value,
TicketDataFormat = new CustomJwtDataFormat(
SecurityAlgorithms.HmacSha256,
tokenValidationParameters)
});
app.UseMiddleware<TokenProviderMiddleware>(Options.Create(tokenProviderOptions));
}
private async Task<ClaimsIdentity> GetIdentity(string username, string password, IUserIdentityManager userIdentityManager)
{
//await userIdentityManager.CreateUser(new UserDto
//{
// Username = "a",
// Password = "a",
// Claims = new List<Claim>() { new Claim(ClaimTypes.Role, "Admin")},
// UserType = UserType.LocalUser,
//});
var validLogin = await userIdentityManager.CredentialsValid(username, password);
if (!validLogin)
{

@ -1,24 +1,12 @@
using System;
using System.IO;
using System.Linq;
using System.Text;
using IdentityServer4.EntityFramework.DbContexts;
using AutoMapper;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json;
using Ombi.Auth;
using Ombi.DependencyInjection;
using Ombi.Models;
using Ombi.Store.Context;
using Ombi.Mapping;
namespace Ombi
{
@ -42,6 +30,8 @@ namespace Ombi
{
// Add framework services.
services.AddMvc();
services.AddOmbiMappingProfile();
services.AddAutoMapper();
services.RegisterDependencies(); // Ioc and EF
}

@ -3,7 +3,7 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@ViewData["Title"] - Ombi</title>
<title>Ombi</title>
<base href="@Url.Content("~/")" />
<script src="@Url.Content("~/lib/pace.js")?v=@ViewBag.AssemblyVersion"></script>
<link href="@Url.Content("~/css/lib/pace-theme-minimal.css")" rel="stylesheet" />

@ -9,7 +9,7 @@
"SecretKey": "secretkey_secretkey123!",
"Issuer": "DemoIssuer",
"Audience": "DemoAudience",
"TokenPath": "/api/token/",
"TokenPath": "/api/v1/token/",
"CookieName": "access_token"
}
}

@ -13,7 +13,7 @@ import { Http } from '@angular/http';
@Injectable()
export class AuthService extends ServiceHelpers {
constructor(http: Http) {
super(http, '/api/token');
super(http, '/api/v1/token');
}
login(login:IUserLogin) : Observable<any> {

Loading…
Cancel
Save