Moved over to using Identity Server with Asp.Net Core Identity #1456 #865

Still a lot of work to do on the frontend for this.
pull/1488/head
Jamie.Rees 7 years ago
parent 8cf5a4c1fd
commit b04344dd17

@ -10,8 +10,8 @@
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="2.0.1" />
<PackageReference Include="Hangfire" Version="1.6.14" />
<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="Microsoft.EntityFrameworkCore.Sqlite" Version="1.1.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Design" Version="1.1.2" />
<PackageReference Include="MiniProfiler.AspNetCore" Version="4.0.0-alpha6-79" />
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
<PackageReference Include="System.Diagnostics.Process" Version="4.3.0" />

@ -5,7 +5,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="1.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="1.1.2" />
<PackageReference Include="Microsoft.AspNetCore.Authorization" Version="1.1.1" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="1.1.0" />
</ItemGroup>

@ -18,7 +18,7 @@ namespace Ombi.Mapping.Profiles
CreateMap<Claim, ClaimCheckboxes>().ConvertUsing<ClaimsConverter>();
CreateMap<UserDto, UserViewModel>().ForMember(x => x.Password, opt => opt.Ignore());
CreateMap<OmbiUser, UserViewModel>().ForMember(x => x.Password, opt => opt.Ignore());
CreateMap<ClaimCheckboxes, Claim>()
.ConstructUsing(checkbox => checkbox.Enabled ? new Claim(ClaimTypes.Role, checkbox.Value) : new Claim(ClaimTypes.Country, ""));

@ -17,7 +17,7 @@ namespace Ombi.Store.Context
DbSet<PlexContent> PlexContent { get; set; }
DbSet<RadarrCache> RadarrCache { get; set; }
DatabaseFacade Database { get; }
DbSet<User> Users { get; set; }
DbSet<User> OldUsers { get; set; }
EntityEntry<T> Entry<T>(T entry) where T : class;
EntityEntry<TEntity> Attach<TEntity>(TEntity entity) where TEntity : class;
DbSet<TEntity> Set<TEntity>() where TEntity : class;

@ -1,5 +1,6 @@
using System;
using System.Linq;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Ombi.Helpers;
using Ombi.Store.Entities;
@ -7,7 +8,7 @@ using Ombi.Store.Entities.Requests;
namespace Ombi.Store.Context
{
public sealed class OmbiContext : DbContext, IOmbiContext
public sealed class OmbiContext : IdentityDbContext<OmbiUser>, IOmbiContext
{
private static bool _created;
public OmbiContext()
@ -19,10 +20,12 @@ namespace Ombi.Store.Context
// Add the notifcation templates
AddAllTemplates();
}
public DbSet<GlobalSettings> Settings { get; set; }
public DbSet<User> Users { get; set; }
public DbSet<User> OldUsers { get; set; }
public DbSet<PlexContent> PlexContent { get; set; }
public DbSet<RadarrCache> RadarrCache { get; set; }
public DbSet<NotificationTemplates> NotificationTemplates { get; set; }

@ -0,0 +1,14 @@
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
namespace Ombi.Store.Entities
{
public class OmbiUser : IdentityUser
{
public string Alias { get; set; }
public UserType UserType { get; set; }
[NotMapped]
public string UserAlias => string.IsNullOrEmpty(Alias) ? UserName : Alias;
}
}

@ -10,13 +10,142 @@ using Ombi.Store.Entities;
namespace Ombi.Store.Migrations
{
[DbContext(typeof(OmbiContext))]
[Migration("20170703134019_Initial")]
[Migration("20170712080109_Initial")]
partial class Initial
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
modelBuilder
.HasAnnotation("ProductVersion", "1.1.1");
.HasAnnotation("ProductVersion", "1.1.2");
modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityRole", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken();
b.Property<string>("Name")
.HasMaxLength(256);
b.Property<string>("NormalizedName")
.HasMaxLength(256);
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasName("RoleNameIndex");
b.ToTable("AspNetRoles");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityRoleClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ClaimType");
b.Property<string>("ClaimValue");
b.Property<string>("RoleId")
.IsRequired();
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ClaimType");
b.Property<string>("ClaimValue");
b.Property<string>("UserId")
.IsRequired();
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserLogin<string>", b =>
{
b.Property<string>("LoginProvider");
b.Property<string>("ProviderKey");
b.Property<string>("ProviderDisplayName");
b.Property<string>("UserId")
.IsRequired();
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserRole<string>", b =>
{
b.Property<string>("UserId");
b.Property<string>("RoleId");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserToken<string>", b =>
{
b.Property<string>("UserId");
b.Property<string>("LoginProvider");
b.Property<string>("Name");
b.Property<string>("Value");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens");
});
modelBuilder.Entity("Ombi.Store.Entities.EmailTokens", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<DateTime>("DateUsed");
b.Property<Guid>("Token");
b.Property<bool>("Used");
b.Property<int>("UserId");
b.Property<DateTime>("ValidUntil");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("EmailTokens");
});
modelBuilder.Entity("Ombi.Store.Entities.GlobalSettings", b =>
{
@ -52,6 +181,60 @@ namespace Ombi.Store.Migrations
b.ToTable("NotificationTemplates");
});
modelBuilder.Entity("Ombi.Store.Entities.OmbiUser", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("AccessFailedCount");
b.Property<string>("Alias");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken();
b.Property<string>("Email")
.HasMaxLength(256);
b.Property<bool>("EmailConfirmed");
b.Property<bool>("LockoutEnabled");
b.Property<DateTimeOffset?>("LockoutEnd");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256);
b.Property<string>("NormalizedUserName")
.HasMaxLength(256);
b.Property<string>("PasswordHash");
b.Property<string>("PhoneNumber");
b.Property<bool>("PhoneNumberConfirmed");
b.Property<string>("SecurityStamp");
b.Property<bool>("TwoFactorEnabled");
b.Property<string>("UserName")
.HasMaxLength(256);
b.Property<int>("UserType");
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasName("UserNameIndex");
b.ToTable("AspNetUsers");
});
modelBuilder.Entity("Ombi.Store.Entities.PlexContent", b =>
{
b.Property<int>("Id")
@ -131,6 +314,8 @@ namespace Ombi.Store.Migrations
b.Property<int>("RequestedUserId");
b.Property<string>("Title");
b.HasKey("Id");
b.HasIndex("ParentRequestId");
@ -273,7 +458,7 @@ namespace Ombi.Store.Migrations
b.HasKey("Id");
b.ToTable("Users");
b.ToTable("OldUsers");
});
modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b =>
@ -320,6 +505,51 @@ namespace Ombi.Store.Migrations
b.ToTable("SeasonRequests");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityRoleClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityRole")
.WithMany("Claims")
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserClaim<string>", b =>
{
b.HasOne("Ombi.Store.Entities.OmbiUser")
.WithMany("Claims")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserLogin<string>", b =>
{
b.HasOne("Ombi.Store.Entities.OmbiUser")
.WithMany("Logins")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserRole<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityRole")
.WithMany("Users")
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("Ombi.Store.Entities.OmbiUser")
.WithMany("Roles")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Ombi.Store.Entities.EmailTokens", b =>
{
b.HasOne("Ombi.Store.Entities.User", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b =>
{
b.HasOne("Ombi.Store.Entities.PlexContent")

@ -8,6 +8,34 @@ namespace Ombi.Store.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "AspNetRoles",
columns: table => new
{
Id = table.Column<string>(nullable: false),
ConcurrencyStamp = table.Column<string>(nullable: true),
Name = table.Column<string>(maxLength: 256, nullable: true),
NormalizedName = table.Column<string>(maxLength: 256, nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetRoles", x => x.Id);
});
migrationBuilder.CreateTable(
name: "AspNetUserTokens",
columns: table => new
{
UserId = table.Column<string>(nullable: false),
LoginProvider = table.Column<string>(nullable: false),
Name = table.Column<string>(nullable: false),
Value = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name });
});
migrationBuilder.CreateTable(
name: "GlobalSettings",
columns: table => new
@ -39,6 +67,33 @@ namespace Ombi.Store.Migrations
table.PrimaryKey("PK_NotificationTemplates", x => x.Id);
});
migrationBuilder.CreateTable(
name: "AspNetUsers",
columns: table => new
{
Id = table.Column<string>(nullable: false),
AccessFailedCount = table.Column<int>(nullable: false),
Alias = table.Column<string>(nullable: true),
ConcurrencyStamp = table.Column<string>(nullable: true),
Email = table.Column<string>(maxLength: 256, nullable: true),
EmailConfirmed = table.Column<bool>(nullable: false),
LockoutEnabled = table.Column<bool>(nullable: false),
LockoutEnd = table.Column<DateTimeOffset>(nullable: true),
NormalizedEmail = table.Column<string>(maxLength: 256, nullable: true),
NormalizedUserName = table.Column<string>(maxLength: 256, nullable: true),
PasswordHash = table.Column<string>(nullable: true),
PhoneNumber = table.Column<string>(nullable: true),
PhoneNumberConfirmed = table.Column<bool>(nullable: false),
SecurityStamp = table.Column<string>(nullable: true),
TwoFactorEnabled = table.Column<bool>(nullable: false),
UserName = table.Column<string>(maxLength: 256, nullable: true),
UserType = table.Column<int>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUsers", x => x.Id);
});
migrationBuilder.CreateTable(
name: "PlexContent",
columns: table => new
@ -92,7 +147,7 @@ namespace Ombi.Store.Migrations
});
migrationBuilder.CreateTable(
name: "Users",
name: "OldUsers",
columns: table => new
{
Id = table.Column<int>(nullable: false)
@ -107,7 +162,93 @@ namespace Ombi.Store.Migrations
},
constraints: table =>
{
table.PrimaryKey("PK_Users", x => x.Id);
table.PrimaryKey("PK_OldUsers", x => x.Id);
});
migrationBuilder.CreateTable(
name: "AspNetRoleClaims",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("Sqlite:Autoincrement", true),
ClaimType = table.Column<string>(nullable: true),
ClaimValue = table.Column<string>(nullable: true),
RoleId = table.Column<string>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id);
table.ForeignKey(
name: "FK_AspNetRoleClaims_AspNetRoles_RoleId",
column: x => x.RoleId,
principalTable: "AspNetRoles",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "AspNetUserClaims",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("Sqlite:Autoincrement", true),
ClaimType = table.Column<string>(nullable: true),
ClaimValue = table.Column<string>(nullable: true),
UserId = table.Column<string>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUserClaims", x => x.Id);
table.ForeignKey(
name: "FK_AspNetUserClaims_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "AspNetUserLogins",
columns: table => new
{
LoginProvider = table.Column<string>(nullable: false),
ProviderKey = table.Column<string>(nullable: false),
ProviderDisplayName = table.Column<string>(nullable: true),
UserId = table.Column<string>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey });
table.ForeignKey(
name: "FK_AspNetUserLogins_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "AspNetUserRoles",
columns: table => new
{
UserId = table.Column<string>(nullable: false),
RoleId = table.Column<string>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId });
table.ForeignKey(
name: "FK_AspNetUserRoles_AspNetRoles_RoleId",
column: x => x.RoleId,
principalTable: "AspNetRoles",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_AspNetUserRoles_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
@ -132,6 +273,29 @@ namespace Ombi.Store.Migrations
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "EmailTokens",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("Sqlite:Autoincrement", true),
DateUsed = table.Column<DateTime>(nullable: false),
Token = table.Column<Guid>(nullable: false),
Used = table.Column<bool>(nullable: false),
UserId = table.Column<int>(nullable: false),
ValidUntil = table.Column<DateTime>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_EmailTokens", x => x.Id);
table.ForeignKey(
name: "FK_EmailTokens_OldUsers_UserId",
column: x => x.UserId,
principalTable: "OldUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "ChildRequests",
columns: table => new
@ -159,9 +323,9 @@ namespace Ombi.Store.Migrations
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_ChildRequests_Users_RequestedUserId",
name: "FK_ChildRequests_OldUsers_RequestedUserId",
column: x => x.RequestedUserId,
principalTable: "Users",
principalTable: "OldUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
@ -192,9 +356,9 @@ namespace Ombi.Store.Migrations
{
table.PrimaryKey("PK_MovieRequests", x => x.Id);
table.ForeignKey(
name: "FK_MovieRequests_Users_RequestedUserId",
name: "FK_MovieRequests_OldUsers_RequestedUserId",
column: x => x.RequestedUserId,
principalTable: "Users",
principalTable: "OldUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
@ -301,6 +465,48 @@ namespace Ombi.Store.Migrations
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "RoleNameIndex",
table: "AspNetRoles",
column: "NormalizedName",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_AspNetRoleClaims_RoleId",
table: "AspNetRoleClaims",
column: "RoleId");
migrationBuilder.CreateIndex(
name: "IX_AspNetUserClaims_UserId",
table: "AspNetUserClaims",
column: "UserId");
migrationBuilder.CreateIndex(
name: "IX_AspNetUserLogins_UserId",
table: "AspNetUserLogins",
column: "UserId");
migrationBuilder.CreateIndex(
name: "IX_AspNetUserRoles_RoleId",
table: "AspNetUserRoles",
column: "RoleId");
migrationBuilder.CreateIndex(
name: "IX_EmailTokens_UserId",
table: "EmailTokens",
column: "UserId");
migrationBuilder.CreateIndex(
name: "EmailIndex",
table: "AspNetUsers",
column: "NormalizedEmail");
migrationBuilder.CreateIndex(
name: "UserNameIndex",
table: "AspNetUsers",
column: "NormalizedUserName",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_PlexSeasonsContent_PlexContentId",
table: "PlexSeasonsContent",
@ -354,6 +560,24 @@ namespace Ombi.Store.Migrations
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "AspNetRoleClaims");
migrationBuilder.DropTable(
name: "AspNetUserClaims");
migrationBuilder.DropTable(
name: "AspNetUserLogins");
migrationBuilder.DropTable(
name: "AspNetUserRoles");
migrationBuilder.DropTable(
name: "AspNetUserTokens");
migrationBuilder.DropTable(
name: "EmailTokens");
migrationBuilder.DropTable(
name: "GlobalSettings");
@ -375,6 +599,12 @@ namespace Ombi.Store.Migrations
migrationBuilder.DropTable(
name: "EpisodeRequests");
migrationBuilder.DropTable(
name: "AspNetRoles");
migrationBuilder.DropTable(
name: "AspNetUsers");
migrationBuilder.DropTable(
name: "PlexContent");
@ -391,7 +621,7 @@ namespace Ombi.Store.Migrations
name: "TvRequests");
migrationBuilder.DropTable(
name: "Users");
name: "OldUsers");
}
}
}

@ -15,7 +15,136 @@ namespace Ombi.Store.Migrations
protected override void BuildModel(ModelBuilder modelBuilder)
{
modelBuilder
.HasAnnotation("ProductVersion", "1.1.1");
.HasAnnotation("ProductVersion", "1.1.2");
modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityRole", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken();
b.Property<string>("Name")
.HasMaxLength(256);
b.Property<string>("NormalizedName")
.HasMaxLength(256);
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasName("RoleNameIndex");
b.ToTable("AspNetRoles");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityRoleClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ClaimType");
b.Property<string>("ClaimValue");
b.Property<string>("RoleId")
.IsRequired();
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ClaimType");
b.Property<string>("ClaimValue");
b.Property<string>("UserId")
.IsRequired();
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserLogin<string>", b =>
{
b.Property<string>("LoginProvider");
b.Property<string>("ProviderKey");
b.Property<string>("ProviderDisplayName");
b.Property<string>("UserId")
.IsRequired();
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserRole<string>", b =>
{
b.Property<string>("UserId");
b.Property<string>("RoleId");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserToken<string>", b =>
{
b.Property<string>("UserId");
b.Property<string>("LoginProvider");
b.Property<string>("Name");
b.Property<string>("Value");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens");
});
modelBuilder.Entity("Ombi.Store.Entities.EmailTokens", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<DateTime>("DateUsed");
b.Property<Guid>("Token");
b.Property<bool>("Used");
b.Property<int>("UserId");
b.Property<DateTime>("ValidUntil");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("EmailTokens");
});
modelBuilder.Entity("Ombi.Store.Entities.GlobalSettings", b =>
{
@ -51,6 +180,60 @@ namespace Ombi.Store.Migrations
b.ToTable("NotificationTemplates");
});
modelBuilder.Entity("Ombi.Store.Entities.OmbiUser", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("AccessFailedCount");
b.Property<string>("Alias");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken();
b.Property<string>("Email")
.HasMaxLength(256);
b.Property<bool>("EmailConfirmed");
b.Property<bool>("LockoutEnabled");
b.Property<DateTimeOffset?>("LockoutEnd");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256);
b.Property<string>("NormalizedUserName")
.HasMaxLength(256);
b.Property<string>("PasswordHash");
b.Property<string>("PhoneNumber");
b.Property<bool>("PhoneNumberConfirmed");
b.Property<string>("SecurityStamp");
b.Property<bool>("TwoFactorEnabled");
b.Property<string>("UserName")
.HasMaxLength(256);
b.Property<int>("UserType");
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasName("UserNameIndex");
b.ToTable("AspNetUsers");
});
modelBuilder.Entity("Ombi.Store.Entities.PlexContent", b =>
{
b.Property<int>("Id")
@ -130,6 +313,8 @@ namespace Ombi.Store.Migrations
b.Property<int>("RequestedUserId");
b.Property<string>("Title");
b.HasKey("Id");
b.HasIndex("ParentRequestId");
@ -272,7 +457,7 @@ namespace Ombi.Store.Migrations
b.HasKey("Id");
b.ToTable("Users");
b.ToTable("OldUsers");
});
modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b =>
@ -319,6 +504,51 @@ namespace Ombi.Store.Migrations
b.ToTable("SeasonRequests");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityRoleClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityRole")
.WithMany("Claims")
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserClaim<string>", b =>
{
b.HasOne("Ombi.Store.Entities.OmbiUser")
.WithMany("Claims")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserLogin<string>", b =>
{
b.HasOne("Ombi.Store.Entities.OmbiUser")
.WithMany("Logins")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserRole<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityRole")
.WithMany("Users")
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("Ombi.Store.Entities.OmbiUser")
.WithMany("Roles")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Ombi.Store.Entities.EmailTokens", b =>
{
b.HasOne("Ombi.Store.Entities.User", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b =>
{
b.HasOne("Ombi.Store.Entities.PlexContent")

@ -5,10 +5,11 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="1.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" 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="Microsoft.AspNetCore.Authentication.JwtBearer" Version="1.1.2" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="1.1.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="1.1.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="1.1.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Design" Version="1.1.2" />
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
</ItemGroup>
<ItemGroup>

@ -45,32 +45,32 @@ namespace Ombi.Store.Repository
public async Task<User> GetUser(string username)
{
var user = await Db.Users.FirstOrDefaultAsync(x => x.Username.ToLower() == username.ToLower());
var user = await Db.OldUsers.FirstOrDefaultAsync(x => x.Username.ToLower() == username.ToLower());
Db.Entry(user).Reload();
return user;
}
public async Task<User> GetUser(int userId)
{
var user = await Db.Users.FirstOrDefaultAsync(x => x.Id == userId);
var user = await Db.OldUsers.FirstOrDefaultAsync(x => x.Id == userId);
Db.Entry(user).Reload();
return user;
}
public async Task CreateUser(User user)
{
Db.Users.Add(user);
Db.OldUsers.Add(user);
await Db.SaveChangesAsync();
}
public async Task<IEnumerable<User>> GetUsers()
{
return await Db.Users.ToListAsync();
return await Db.OldUsers.ToListAsync();
}
public async Task DeleteUser(User user)
{
Db.Users.Remove(user);
Db.OldUsers.Remove(user);
await Db.SaveChangesAsync();
}

@ -1,72 +0,0 @@
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http.Authentication;
using Microsoft.IdentityModel.Tokens;
namespace Ombi.Auth
{
public class CustomJwtDataFormat : ISecureDataFormat<AuthenticationTicket>
{
private readonly string algorithm;
private readonly TokenValidationParameters validationParameters;
public CustomJwtDataFormat(string algorithm, TokenValidationParameters validationParameters)
{
this.algorithm = algorithm;
this.validationParameters = validationParameters;
}
public AuthenticationTicket Unprotect(string protectedText)
=> Unprotect(protectedText, null);
public AuthenticationTicket Unprotect(string protectedText, string purpose)
{
var handler = new JwtSecurityTokenHandler();
ClaimsPrincipal principal = null;
SecurityToken validToken = null;
try
{
principal = handler.ValidateToken(protectedText, this.validationParameters, out validToken);
var validJwt = validToken as JwtSecurityToken;
if (validJwt == null)
{
throw new ArgumentException("Invalid JWT");
}
if (!validJwt.Header.Alg.Equals(algorithm, StringComparison.Ordinal))
{
throw new ArgumentException($"Algorithm must be '{algorithm}'");
}
// Additional custom validation of JWT claims here (if any)
}
catch (SecurityTokenValidationException)
{
return null;
}
catch (ArgumentException)
{
return null;
}
// Validation passed. Return a valid AuthenticationTicket:
return new AuthenticationTicket(principal, new AuthenticationProperties(), "Cookie");
}
// This ISecureDataFormat implementation is decode-only
public string Protect(AuthenticationTicket data)
{
throw new NotImplementedException();
}
public string Protect(AuthenticationTicket data, string purpose)
{
throw new NotImplementedException();
}
}
}

@ -1,11 +0,0 @@
namespace Ombi.Auth
{
public class TokenAuthenticationOptions
{
public string SecretKey { get; set; }
public string Issuer { get; set; }
public string Audience { get; set; }
public string TokenPath { get; set; }
public string CookieName { get; set; }
}
}

@ -1,161 +0,0 @@
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.IO;
using System.Linq;
using System.Security.Claims;
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
{
public class TokenProviderMiddleware
{
private readonly RequestDelegate _next;
private readonly TokenProviderOptions _options;
private readonly JsonSerializerSettings _serializerSettings;
private readonly IUserIdentityManager _identityManager;
public TokenProviderMiddleware(
RequestDelegate next,
IOptions<TokenProviderOptions> options, IUserIdentityManager manager)
{
_next = next;
_options = options.Value;
ThrowIfInvalidOptions(_options);
_serializerSettings = new JsonSerializerSettings
{
Formatting = Formatting.Indented
};
_identityManager = manager;
}
public Task Invoke(HttpContext context)
{
// If the request path doesn't match, skip
if (!context.Request.Path.Equals(_options.Path, StringComparison.Ordinal))
{
try
{
return _next(context);
}
catch (Exception e)
{
throw;
}
}
// Request must be POST with Content-Type: application/json
if (!context.Request.Method.Equals("POST")
)
{
context.Response.StatusCode = 400;
return context.Response.WriteAsync("Bad request.");
}
return GenerateToken(context);
}
private async Task GenerateToken(HttpContext context)
{
var request = context.Request;
UserAuthModel userInfo;
using (var bodyReader = new StreamReader(request.Body))
{
string body = await bodyReader.ReadToEndAsync();
userInfo = JsonConvert.DeserializeObject<UserAuthModel>(body);
}
var identity = await _options.IdentityResolver(userInfo.Username, userInfo.Password, _identityManager);
if (identity == null)
{
context.Response.StatusCode = 400;
await context.Response.WriteAsync("Invalid username or password.");
return;
}
var now = DateTime.UtcNow;
// Specifically add the jti (nonce), iat (issued timestamp), and sub (subject/user) claims.
// You can add other claims here, if you want:
var jwtClaims = new List<Claim>
{
new Claim(JwtRegisteredClaimNames.Sub, userInfo.Username),
new Claim(JwtRegisteredClaimNames.Jti, await _options.NonceGenerator()),
new Claim(JwtRegisteredClaimNames.Iat, new DateTimeOffset(now).ToUniversalTime().ToUnixTimeSeconds().ToString(), ClaimValueTypes.Integer64)
};
identity.Claims.ToList().AddRange(jwtClaims);
// Create the JWT and write it to a string
var jwt = new JwtSecurityToken(
issuer: _options.Issuer,
audience: _options.Audience,
claims: identity.Claims,
notBefore: now,
expires: now.Add(_options.Expiration),
signingCredentials: _options.SigningCredentials);
var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt);
var response = new
{
access_token = encodedJwt,
expires_in = (int)_options.Expiration.TotalSeconds
};
// Serialize and return the response
context.Response.ContentType = "application/json";
await context.Response.WriteAsync(JsonConvert.SerializeObject(response, _serializerSettings));
}
private static void ThrowIfInvalidOptions(TokenProviderOptions options)
{
if (string.IsNullOrEmpty(options.Path))
{
throw new ArgumentNullException(nameof(TokenProviderOptions.Path));
}
if (string.IsNullOrEmpty(options.Issuer))
{
throw new ArgumentNullException(nameof(TokenProviderOptions.Issuer));
}
if (string.IsNullOrEmpty(options.Audience))
{
throw new ArgumentNullException(nameof(TokenProviderOptions.Audience));
}
if (options.Expiration == TimeSpan.Zero)
{
throw new ArgumentException("Must be a non-zero TimeSpan.", nameof(TokenProviderOptions.Expiration));
}
if (options.IdentityResolver == null)
{
throw new ArgumentNullException(nameof(TokenProviderOptions.IdentityResolver));
}
if (options.SigningCredentials == null)
{
throw new ArgumentNullException(nameof(TokenProviderOptions.SigningCredentials));
}
if (options.NonceGenerator == null)
{
throw new ArgumentNullException(nameof(TokenProviderOptions.NonceGenerator));
}
}
}
}

@ -1,53 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Threading.Tasks;
using Microsoft.IdentityModel.Tokens;
using Ombi.Core.IdentityResolver;
namespace Ombi.Auth
{
public class TokenProviderOptions
{
/// <summary>
/// The relative request path to listen on.
/// </summary>
/// <remarks>The default path is <c>/token</c>.</remarks>
public string Path { get; set; } = "/token/";
/// <summary>
/// The Issuer (iss) claim for generated tokens.
/// </summary>
public string Issuer { get; set; }
/// <summary>
/// The Audience (aud) claim for the generated tokens.
/// </summary>
public string Audience { get; set; }
/// <summary>
/// The expiration time for the generated tokens.
/// </summary>
/// <remarks>The default is 1 Days.</remarks>
public TimeSpan Expiration { get; set; } = TimeSpan.FromDays(1);
/// <summary>
/// The signing key to use when generating tokens.
/// </summary>
public SigningCredentials SigningCredentials { get; set; }
/// <summary>
/// Resolves a user identity given a username and password.
/// </summary>
public Func<string, string, IUserIdentityManager, Task<ClaimsIdentity>> IdentityResolver { get; set; }
/// <summary>
/// Generates a random value (nonce) for each generated token.
/// </summary>
/// <remarks>The default nonce is a random GUID.</remarks>
public Func<Task<string>> NonceGenerator { get; set; }
= () => Task.FromResult(Guid.NewGuid().ToString());
}
}

@ -1,5 +1,4 @@

import { Injectable } from '@angular/core';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Rx';
import { ServiceHelpers } from '../services/service.helpers';
@ -8,18 +7,28 @@ import { IUserLogin, ILocalUser } from './IUserLogin';
import { tokenNotExpired, JwtHelper } from 'angular2-jwt';
import { Http } from '@angular/http';
import { Http, Headers, URLSearchParams } from '@angular/http';
@Injectable()
export class AuthService extends ServiceHelpers {
constructor(http: Http) {
super(http, '/api/v1/token');
super(http, '/connect/token');
}
jwtHelper: JwtHelper = new JwtHelper();
login(login:IUserLogin) : Observable<any> {
return this.http.post(`${this.url}/`, JSON.stringify(login), { headers: this.headers })
login(login: IUserLogin): Observable<any> {
this.headers = new Headers();
this.headers.append('Content-Type', 'application/x-www-form-urlencoded');
let data = new URLSearchParams();
data.append('client_id', 'frontend');
data.append('scope', 'api');
data.append('client_secret', 'secret');
data.append('grant_type', 'password');
data.append('username', login.username);
data.append('password', login.password);
return this.http.post(`${this.url}/`, data.toString(), { headers: this.headers })
.map(this.extractData);
}

@ -1,9 +1,7 @@
<div *ngIf="childRequests">
<div *ngFor="let child of childRequests">
<div class="col-md-12">
</div>
</div>
</div>

@ -0,0 +1,8 @@
namespace Ombi.Config
{
public class UserSettings
{
public string WebsiteUrl { get; set; }
public bool UseHttps { get; set; }
}
}

@ -1,18 +1,18 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Security.Claims;
using System.Threading.Tasks;
using AutoMapper;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Ombi.Attributes;
using Ombi.Core.Claims;
using Ombi.Core.IdentityResolver;
using Ombi.Core.Models;
using Ombi.Core.Models.UI;
using Ombi.Models;
using Ombi.Store.Entities;
namespace Ombi.Controllers
{
@ -23,13 +23,15 @@ namespace Ombi.Controllers
[PowerUser]
public class IdentityController : BaseV1ApiController
{
public IdentityController(IUserIdentityManager identity, IMapper mapper)
public IdentityController(UserManager<OmbiUser> user, IMapper mapper, RoleManager<IdentityRole> rm)
{
IdentityManager = identity;
UserManager = user;
Mapper = mapper;
RoleManager = rm;
}
private IUserIdentityManager IdentityManager { get; }
private UserManager<OmbiUser> UserManager { get; }
private RoleManager<IdentityRole> RoleManager { get; }
private IMapper Mapper { get; }
/// <summary>
@ -39,10 +41,9 @@ namespace Ombi.Controllers
[HttpGet]
public async Task<UserViewModel> GetUser()
{
return Mapper.Map<UserViewModel>(await IdentityManager.GetUser(this.HttpContext.User.Identity.Name));
return Mapper.Map<UserViewModel>(await UserManager.GetUserAsync(User));
}
/// <summary>
/// This is what the Wizard will call when creating the user for the very first time.
/// This should never be called after this.
@ -57,20 +58,39 @@ namespace Ombi.Controllers
[AllowAnonymous]
public async Task<bool> CreateWizardUser([FromBody] UserAuthModel user)
{
var users = await IdentityManager.GetUsers();
var users = UserManager.Users;
if (users.Any())
{
// No one should be calling this. Only the wizard
return false;
}
await IdentityManager.CreateUser(new UserDto
var userToCreate = new OmbiUser
{
UserName = user.Username,
};
var result = await UserManager.CreateAsync(userToCreate, user.Password);
if (result.Succeeded)
{
Username = user.Username,
UserType = UserType.LocalUser,
Claims = new List<Claim>() { new Claim(ClaimTypes.Role, OmbiClaims.Admin) },
Password = user.Password,
});
if (!(await RoleManager.RoleExistsAsync("Admin")))
{
var r = await RoleManager.CreateAsync(new IdentityRole("Admin"));
}
var re = await UserManager.AddToRoleAsync(userToCreate, "Admin");
var v = User.IsInRole("Admin");
}
//await UserManager.CreateUser(new UserDto
//{
// Username = user.Username,
// UserType = UserType.LocalUser,
// Claims = new List<Claim>() { new Claim(ClaimTypes.Role, OmbiClaims.Admin) },
// Password = user.Password,
//});
return true;
}
@ -88,7 +108,7 @@ namespace Ombi.Controllers
var fields = fieldInfos.Where(fi => fi.IsLiteral && !fi.IsInitOnly).ToList();
var allClaims = fields.Select(x => x.Name).ToList();
var users = Mapper.Map<IEnumerable<UserViewModel>>(await IdentityManager.GetUsers()).ToList();
var users = Mapper.Map<IEnumerable<UserViewModel>>(UserManager.Users).ToList();
foreach (var user in users)
{
@ -121,7 +141,7 @@ namespace Ombi.Controllers
var fields = fieldInfos.Where(fi => fi.IsLiteral && !fi.IsInitOnly).ToList();
var allClaims = fields.Select(x => x.Name).ToList();
var user = Mapper.Map<UserViewModel>(await IdentityManager.GetUser(id));
var user = Mapper.Map<UserViewModel>(await UserManager.Users.FirstOrDefaultAsync(x => x.Id == id.ToString()));
var userClaims = user.Claims.Select(x => x.Value);
@ -145,37 +165,37 @@ namespace Ombi.Controllers
/// </summary>
/// <param name="user">The user.</param>
/// <returns></returns>
[HttpPost]
public async Task<UserViewModel> CreateUser([FromBody] UserViewModel user)
{
user.Id = null;
var userResult = await IdentityManager.CreateUser(Mapper.Map<UserDto>(user));
return Mapper.Map<UserViewModel>(userResult);
}
/// <summary>
/// Updates the user.
/// </summary>
/// <param name="user">The user.</param>
/// <returns></returns>
[HttpPut]
public async Task<UserViewModel> UpdateUser([FromBody] UserViewModel user)
{
var userResult = await IdentityManager.UpdateUser(Mapper.Map<UserDto>(user));
return Mapper.Map<UserViewModel>(userResult);
}
//[HttpPost]
//public async Task<UserViewModel> CreateUser([FromBody] UserViewModel user)
//{
// user.Id = null;
// var userResult = await UserManager.CreateUser(Mapper.Map<UserDto>(user));
// return Mapper.Map<UserViewModel>(userResult);
//}
/// <summary>
/// Deletes the user.
/// Updates the user.
/// </summary>
/// <param name="user">The user.</param>
/// <returns></returns>
[HttpDelete]
public async Task<StatusCodeResult> DeleteUser([FromBody] UserViewModel user)
{
await IdentityManager.DeleteUser(Mapper.Map<UserDto>(user));
return Ok();
}
//[HttpPut]
//public async Task<UserViewModel> UpdateUser([FromBody] UserViewModel user)
//{
// var userResult = await UserManager.UpdateUser(Mapper.Map<UserDto>(user));
// return Mapper.Map<UserViewModel>(userResult);
//}
///// <summary>
///// Deletes the user.
///// </summary>
///// <param name="user">The user.</param>
///// <returns></returns>
//[HttpDelete]
//public async Task<StatusCodeResult> DeleteUser([FromBody] UserViewModel user)
//{
// await UserManager.DeleteUser(Mapper.Map<UserDto>(user));
// return Ok();
//}
/// <summary>
/// Gets all available claims in the system.

@ -0,0 +1,58 @@
using System.Collections.Generic;
using IdentityServer4.Models;
namespace Ombi
{
public class IdentityConfig
{
// scopes define the resources in your system
public static IEnumerable<IdentityResource> GetIdentityResources()
{
return new List<IdentityResource>
{
new IdentityResources.OpenId(),
new IdentityResources.Profile(),
new IdentityResource {
Name = "role",
UserClaims = new List<string> {"role"}
}
};
}
public static IEnumerable<ApiResource> GetApiResources()
{
return new List<ApiResource>
{
new ApiResource("api", "API")
{
UserClaims = {"role", "name"},
}
};
}
// clients want to access resources (aka scopes)
public static IEnumerable<Client> GetClients()
{
// client credentials client
return new List<Client>
{
new Client
{
ClientId = "frontend",
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
ClientSecrets =
{
new Secret("secret".Sha256()) // TODO read up on what this actually is
},
AllowedScopes =
{
"api",
},
AccessTokenType = AccessTokenType.Jwt
}
};
}
}
}

@ -45,18 +45,21 @@
<PackageReference Include="Hangfire.MemoryStorage.Core" Version="1.4.0" />
<PackageReference Include="Hangfire.RecurringJobExtensions" Version="1.1.6" />
<PackageReference Include="Hangfire.SQLite.Core" Version="1.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" />
<PackageReference Include="IdentityServer4" Version="1.5.2" />
<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="1.2.1" />
<PackageReference Include="IdentityServer4.AspNetIdentity" Version="1.0.1" />
<PackageReference Include="Microsoft.AspNetCore" Version="1.1.2" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="1.1.2" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="1.1.3" />
<PackageReference Include="Microsoft.AspNetCore.SpaServices" Version="1.1.1" />
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="1.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" 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="Microsoft.EntityFrameworkCore.SqlServer" Version="1.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer.Design" Version="1.1.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="1.1.1" />
<PackageReference Include="Microsoft.VisualStudio.Web.BrowserLink" Version="1.1.0" />
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="1.1.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="1.1.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="1.1.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Design" Version="1.1.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="1.1.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer.Design" Version="1.1.2" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="1.1.2" />
<PackageReference Include="Microsoft.VisualStudio.Web.BrowserLink" Version="1.1.2" />
<PackageReference Include="MiniProfiler.AspNetCore.Mvc" Version="4.0.0-alpha6-79" />
<PackageReference Include="Serilog" Version="2.4.0" />
<PackageReference Include="Serilog.Extensions.Logging" Version="1.4.0" />

@ -1,75 +0,0 @@
using System;
using System.Security.Claims;
using System.Security.Principal;
using System.Text;
using System.Threading.Tasks;
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
{
/// <summary>
/// A key...
/// </summary>
public SymmetricSecurityKey SigningKey;
private void ConfigureAuth(IApplicationBuilder app, IOptions<TokenAuthenticationOptions> options)
{
var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(options.Value.SecretKey));
var tokenProviderOptions = new TokenProviderOptions
{
Path = options.Value.TokenPath,
Audience = options.Value.Audience,
Issuer = options.Value.Issuer,
SigningCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256),
IdentityResolver = GetIdentity
};
var tokenValidationParameters = new TokenValidationParameters
{
// The signing key must match!
ValidateIssuerSigningKey = true,
IssuerSigningKey = signingKey,
// Validate the JWT Issuer (iss) claim
ValidateIssuer = true,
ValidIssuer = options.Value.Issuer,
// Validate the JWT Audience (aud) claim
ValidateAudience = true,
ValidAudience = options.Value.Audience,
// Validate the token expiry
ValidateLifetime = true,
// If you want to allow a certain amount of clock drift, set that here:
ClockSkew = TimeSpan.Zero,
};
app.UseJwtBearerAuthentication(new JwtBearerOptions
{
AutomaticAuthenticate = true,
AutomaticChallenge = true,
TokenValidationParameters = tokenValidationParameters,
});
app.UseMiddleware<TokenProviderMiddleware>(Options.Create(tokenProviderOptions));
}
private async Task<ClaimsIdentity> GetIdentity(string username, string password, IUserIdentityManager userIdentityManager)
{
var validLogin = await userIdentityManager.CredentialsValid(username, password);
if (!validLogin)
{
return null;
}
var user = await userIdentityManager.GetUser(username);
var claim = new ClaimsIdentity(new GenericIdentity(user.Username, "Token"), user.Claims);
return claim;
}
}
}

@ -9,19 +9,23 @@ using Hangfire.SQLite;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.AspNetCore.SpaServices.Webpack;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.PlatformAbstractions;
using Ombi.Auth;
using Microsoft.IdentityModel.Tokens;
using Ombi.Config;
using Ombi.DependencyInjection;
using Ombi.Mapping;
using Ombi.Schedule;
using Ombi.Store.Context;
using Ombi.Store.Entities;
using Serilog;
using Serilog.Events;
using StackExchange.Profiling;
@ -66,9 +70,35 @@ namespace Ombi
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddDbContext<OmbiContext>(options =>
options.UseSqlite("Data Source=Ombi.db"));
services.AddIdentity<OmbiUser, IdentityRole>()
.AddEntityFrameworkStores<OmbiContext>()
.AddDefaultTokenProviders();
services.AddIdentityServer()
.AddTemporarySigningCredential()
.AddInMemoryPersistedGrants()
.AddInMemoryIdentityResources(IdentityConfig.GetIdentityResources())
.AddInMemoryApiResources(IdentityConfig.GetApiResources())
.AddInMemoryClients(IdentityConfig.GetClients())
.AddAspNetIdentity<OmbiUser>();
services.Configure<IdentityOptions>(options =>
{
options.Password.RequireDigit = false;
options.Password.RequiredLength = 1;
options.Password.RequireLowercase = false;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireUppercase = false;
});
services.AddMemoryCache();
services.AddMvc()
.AddJsonOptions(x => x.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);
services.AddOmbiMappingProfile();
services.AddAutoMapper(expression =>
{
@ -114,8 +144,9 @@ namespace Ombi
services.AddScoped<IPrincipal>(sp => sp.GetService<IHttpContextAccessor>().HttpContext.User);
services.Configure<TokenAuthenticationOptions>(Configuration.GetSection("TokenAuthentication"));
//services.Configure<TokenAuthenticationOptions>(Configuration.GetSection("TokenAuthentication"));
services.Configure<ApplicationSettings>(Configuration.GetSection("ApplicationSettings"));
services.Configure<UserSettings>(Configuration.GetSection("UserSettings"));
services.AddHangfire(x =>
{
@ -140,6 +171,24 @@ namespace Ombi
{
//loggerFactory.AddConsole(Configuration.GetSection("Logging"));
//loggerFactory.AddDebug();
var options = (IOptions<UserSettings>) app.ApplicationServices.GetService(
typeof(IOptions<UserSettings>));
app.UseIdentity();
app.UseIdentityServer();
app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
{
Authority = options.Value.WebsiteUrl,
ApiName = "api",
ApiSecret = "secret",
EnableCaching = true,
CacheDuration = TimeSpan.FromMinutes(10), // that's the default
RequireHttpsMetadata = options.Value.UseHttps, // FOR DEV set to false
AutomaticAuthenticate = true,
AutomaticChallenge = true
});
loggerFactory.AddSerilog();
@ -180,7 +229,7 @@ namespace Ombi
var jobSetup = (IJobSetup)app.ApplicationServices.GetService(typeof(IJobSetup));
jobSetup.Setup();
ConfigureAuth(app, (IOptions<TokenAuthenticationOptions>)app.ApplicationServices.GetService(typeof(IOptions<TokenAuthenticationOptions>)));
//ConfigureAuth(app, (IOptions<TokenAuthenticationOptions>)app.ApplicationServices.GetService(typeof(IOptions<TokenAuthenticationOptions>)));
var provider = new FileExtensionContentTypeProvider();
provider.Mappings[".map"] = "application/octet-stream";

@ -8,6 +8,10 @@
"ApplicationSettings": {
"Verison": "{{VERSIONNUMBER}}"
},
"UserSettings": {
"WebsiteUrl": "http://localhost:52038",
"UseHttps": false
},
"TokenAuthentication": {
"SecretKey": "secretkey_secretkey123!",
"Issuer": "OmbiIssuer",

@ -1,9 +0,0 @@
{
"name": "ombi",
"private": true,
"dependencies": {
"PACE": "pace#^1.0.2",
"font-awesome": "^4.7.0"
}
}
Loading…
Cancel
Save