From 1eb18b3187f48c89fc1a5f15920bfe032afa9a5e Mon Sep 17 00:00:00 2001 From: "Jamie.Rees" Date: Tue, 1 Aug 2017 16:14:47 +0100 Subject: [PATCH] A lot of clean up and added a new Image api #865 --- src/Ombi.Api.FanartTv/FanartTvApi.cs | 35 ++++++++ src/Ombi.Api.FanartTv/IFanartTvApi.cs | 11 +++ src/Ombi.Api.FanartTv/Models/MovieResult.cs | 17 ++++ src/Ombi.Api.FanartTv/Models/TvResult.cs | 28 ++++++ .../Ombi.Api.FanartTv.csproj | 11 +++ src/Ombi.Core/Engine/Interfaces/BaseEngine.cs | 11 +-- src/Ombi.Core/Engine/MovieRequestEngine.cs | 2 +- src/Ombi.Core/Engine/TvRequestEngine.cs | 17 ++-- src/Ombi.Core/Helpers/TvShowRequestBuilder.cs | 19 ++-- .../ResourceOwnerPasswordValidator.cs | 88 +++++++++++++++---- src/Ombi.Core/Senders/TvSender.cs | 14 +-- src/Ombi.DependencyInjection/IocExtensions.cs | 5 ++ .../Ombi.DependencyInjection.csproj | 1 + src/Ombi.Helpers/LoggingEvents.cs | 2 + .../Settings/Models/OmbiSettings.cs | 9 +- src/Ombi.Store/Context/IOmbiContext.cs | 2 +- src/Ombi.Store/Context/OmbiContext.cs | 26 ++++++ .../Entities/ApplicationConfiguration.cs | 2 + src/Ombi.Store/Entities/Audit.cs | 38 ++++++++ ...r.cs => 20170801143617_Inital.Designer.cs} | 22 ++++- ...851_Inital.cs => 20170801143617_Inital.cs} | 20 +++++ .../Migrations/OmbiContextModelSnapshot.cs | 20 +++++ .../Repository/ApplicationConfigRepository.cs | 23 +++++ src/Ombi.Store/Repository/AuditRepository.cs | 39 ++++++++ .../IApplicationConfigRepository.cs | 10 +++ src/Ombi.Store/Repository/IAuditRepository.cs | 11 +++ src/Ombi.sln | 7 ++ .../ClientApp/app/interfaces/ISettings.ts | 3 +- .../app/settings/ombi/ombi.component.html | 12 ++- .../app/settings/ombi/ombi.component.ts | 5 +- src/Ombi/Controllers/StatusController.cs | 1 + src/Ombi/Startup.cs | 2 - 32 files changed, 453 insertions(+), 60 deletions(-) create mode 100644 src/Ombi.Api.FanartTv/FanartTvApi.cs create mode 100644 src/Ombi.Api.FanartTv/IFanartTvApi.cs create mode 100644 src/Ombi.Api.FanartTv/Models/MovieResult.cs create mode 100644 src/Ombi.Api.FanartTv/Models/TvResult.cs create mode 100644 src/Ombi.Api.FanartTv/Ombi.Api.FanartTv.csproj create mode 100644 src/Ombi.Store/Entities/Audit.cs rename src/Ombi.Store/Migrations/{20170728131851_Inital.Designer.cs => 20170801143617_Inital.Designer.cs} (96%) rename src/Ombi.Store/Migrations/{20170728131851_Inital.cs => 20170801143617_Inital.cs} (96%) create mode 100644 src/Ombi.Store/Repository/ApplicationConfigRepository.cs create mode 100644 src/Ombi.Store/Repository/AuditRepository.cs create mode 100644 src/Ombi.Store/Repository/IApplicationConfigRepository.cs create mode 100644 src/Ombi.Store/Repository/IAuditRepository.cs diff --git a/src/Ombi.Api.FanartTv/FanartTvApi.cs b/src/Ombi.Api.FanartTv/FanartTvApi.cs new file mode 100644 index 000000000..f7e2c4711 --- /dev/null +++ b/src/Ombi.Api.FanartTv/FanartTvApi.cs @@ -0,0 +1,35 @@ +using System; +using System.Net.Http; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; +using Ombi.Api.FanartTv.Models; + +namespace Ombi.Api.FanartTv +{ + public class FanartTvApi : IFanartTvApi + { + public FanartTvApi(IApi api) + { + Api = api; + } + + private string Endpoint => "https://webservice.fanart.tv/v3"; + private IApi Api { get; } + + public async Task GetTvImages(int tvdbId, string token) + { + var request = new Request($"tv/{tvdbId}", Endpoint, HttpMethod.Get); + request.AddHeader("api-key", token); + + return await Api.Request(request); + } + + public async Task GetMovieImages(int theMovieDbId, string token) + { + var request = new Request($"movies/{theMovieDbId}", Endpoint, HttpMethod.Get); + request.AddHeader("api-key", token); + + return await Api.Request(request); + } + } +} diff --git a/src/Ombi.Api.FanartTv/IFanartTvApi.cs b/src/Ombi.Api.FanartTv/IFanartTvApi.cs new file mode 100644 index 000000000..58213eeaf --- /dev/null +++ b/src/Ombi.Api.FanartTv/IFanartTvApi.cs @@ -0,0 +1,11 @@ +using System.Threading.Tasks; +using Ombi.Api.FanartTv.Models; + +namespace Ombi.Api.FanartTv +{ + public interface IFanartTvApi + { + Task GetMovieImages(int theMovieDbId, string token); + Task GetTvImages(int tvdbId, string token); + } +} \ No newline at end of file diff --git a/src/Ombi.Api.FanartTv/Models/MovieResult.cs b/src/Ombi.Api.FanartTv/Models/MovieResult.cs new file mode 100644 index 000000000..0fe1f83a6 --- /dev/null +++ b/src/Ombi.Api.FanartTv/Models/MovieResult.cs @@ -0,0 +1,17 @@ +namespace Ombi.Api.FanartTv.Models +{ + + public class MovieResult + { + public string name { get; set; } + public string tmdb_id { get; set; } + public string imdb_id { get; set; } + public Content[] hdmovieclearart { get; set; } + public Content[] hdmovielogo { get; set; } + public Content[] moviebackground { get; set; } + public Content[] movieposter { get; set; } + public Content[] moviedisc { get; set; } + public Content[] moviebanner { get; set; } + public Content[] moviethumb { get; set; } + } +} diff --git a/src/Ombi.Api.FanartTv/Models/TvResult.cs b/src/Ombi.Api.FanartTv/Models/TvResult.cs new file mode 100644 index 000000000..d0fa2d278 --- /dev/null +++ b/src/Ombi.Api.FanartTv/Models/TvResult.cs @@ -0,0 +1,28 @@ +namespace Ombi.Api.FanartTv.Models +{ + public class TvResult + { + public string name { get; set; } + public string thetvdb_id { get; set; } + public Content[] hdtvlogo { get; set; } + public Content[] seasonposter { get; set; } + public Content[] seasonthumb { get; set; } + public Content[] characterart { get; set; } + public Content[] clearlogo { get; set; } + public Content[] hdclearart { get; set; } + public Content[] tvposter { get; set; } + public Content[] showbackground { get; set; } + public Content[] tvthumb { get; set; } + public Content[] clearart { get; set; } + public Content[] tvbanner { get; set; } + public Content[] seasonbanner { get; set; } + } + + public class Content + { + public string id { get; set; } + public string url { get; set; } + public string lang { get; set; } + public string likes { get; set; } + } +} diff --git a/src/Ombi.Api.FanartTv/Ombi.Api.FanartTv.csproj b/src/Ombi.Api.FanartTv/Ombi.Api.FanartTv.csproj new file mode 100644 index 000000000..a8c3e7a4c --- /dev/null +++ b/src/Ombi.Api.FanartTv/Ombi.Api.FanartTv.csproj @@ -0,0 +1,11 @@ + + + + netstandard1.6 + + + + + + + \ No newline at end of file diff --git a/src/Ombi.Core/Engine/Interfaces/BaseEngine.cs b/src/Ombi.Core/Engine/Interfaces/BaseEngine.cs index 7e759c549..6a67b951f 100644 --- a/src/Ombi.Core/Engine/Interfaces/BaseEngine.cs +++ b/src/Ombi.Core/Engine/Interfaces/BaseEngine.cs @@ -27,22 +27,19 @@ namespace Ombi.Core.Engine.Interfaces protected string Username => UserPrinciple.Identity.Name; private OmbiUser _user; - protected async Task User() + protected async Task GetUser() { - if(_user == null) - _user = await UserManager.Users.FirstOrDefaultAsync(x => x.UserName == Username); - - return _user; + return _user ?? (_user = await UserManager.Users.FirstOrDefaultAsync(x => x.UserName == Username)); } protected async Task UserAlias() { - return (await User()).UserAlias; + return (await GetUser()).UserAlias; } protected async Task IsInRole(string roleName) { - return await UserManager.IsInRoleAsync(await User(), roleName); + return await UserManager.IsInRoleAsync(await GetUser(), roleName); } public async Task> RunRequestRules(BaseRequest model) diff --git a/src/Ombi.Core/Engine/MovieRequestEngine.cs b/src/Ombi.Core/Engine/MovieRequestEngine.cs index b03a88d6f..69e04fea8 100644 --- a/src/Ombi.Core/Engine/MovieRequestEngine.cs +++ b/src/Ombi.Core/Engine/MovieRequestEngine.cs @@ -57,7 +57,7 @@ namespace Ombi.Core.Engine var fullMovieName = $"{movieInfo.Title}{(!string.IsNullOrEmpty(movieInfo.ReleaseDate) ? $" ({DateTime.Parse(movieInfo.ReleaseDate).Year})" : string.Empty)}"; - var userDetails = await User(); + var userDetails = await GetUser(); var requestModel = new MovieRequests { diff --git a/src/Ombi.Core/Engine/TvRequestEngine.cs b/src/Ombi.Core/Engine/TvRequestEngine.cs index 41275fff3..d36ef78bb 100644 --- a/src/Ombi.Core/Engine/TvRequestEngine.cs +++ b/src/Ombi.Core/Engine/TvRequestEngine.cs @@ -4,22 +4,19 @@ using Ombi.Core.Models.Requests; using Ombi.Core.Models.Search; using Ombi.Helpers; using Ombi.Store.Entities; -using System; using System.Collections.Generic; using System.Globalization; using System.Linq; -using System.Security.Claims; using System.Security.Principal; using System.Threading.Tasks; using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; using Ombi.Core.Engine.Interfaces; using Ombi.Core.Helpers; -using Ombi.Core.IdentityResolver; using Ombi.Core.Rule; using Ombi.Core.Rule.Interfaces; using Ombi.Store.Entities.Requests; -using Ombi.Store.Repository.Requests; +using Ombi.Store.Repository; namespace Ombi.Core.Engine { @@ -28,22 +25,24 @@ namespace Ombi.Core.Engine public TvRequestEngine(ITvMazeApi tvApi, IRequestServiceMain requestService, IPrincipal user, INotificationHelper helper, IMapper map, IRuleEvaluator rule, UserManager manager, - ITvSender sender) : base(user, requestService, rule, manager) + ITvSender sender, IAuditRepository audit) : base(user, requestService, rule, manager) { TvApi = tvApi; NotificationHelper = helper; Mapper = map; TvSender = sender; + Audit = audit; } private INotificationHelper NotificationHelper { get; } private ITvMazeApi TvApi { get; } private IMapper Mapper { get; } private ITvSender TvSender {get;} + private IAuditRepository Audit { get; } public async Task RequestTvShow(SearchTvShowViewModel tv) { - var user = await User(); + var user = await GetUser(); var tvBuilder = new TvShowRequestBuilder(TvApi); (await tvBuilder @@ -63,6 +62,8 @@ namespace Ombi.Core.Engine }; } + await Audit.Record(AuditType.Added, AuditArea.TvRequest, $"Added Request {tv.Title}", Username); + var existingRequest = await TvRepository.Get().FirstOrDefaultAsync(x => x.TvDbId == tv.Id); if (existingRequest != null) { @@ -128,6 +129,7 @@ namespace Ombi.Core.Engine public async Task UpdateTvRequest(TvRequests request) { + await Audit.Record(AuditType.Updated, AuditArea.TvRequest, $"Updated Request {request.Title}", Username); var allRequests = TvRepository.Get(); var results = await allRequests.FirstOrDefaultAsync(x => x.Id == request.Id); @@ -138,6 +140,7 @@ namespace Ombi.Core.Engine public async Task UpdateChildRequest(ChildRequests request) { + await Audit.Record(AuditType.Updated, AuditArea.TvRequest, $"Updated Request {request.Title}", Username); var allRequests = TvRepository.GetChild(); var results = await allRequests.FirstOrDefaultAsync(x => x.Id == request.Id); @@ -149,12 +152,14 @@ namespace Ombi.Core.Engine public async Task RemoveTvChild(int requestId) { var request = await TvRepository.GetChild().FirstOrDefaultAsync(x => x.Id == requestId); + await Audit.Record(AuditType.Deleted, AuditArea.TvRequest, $"Deleting Request {request.Title}", Username); await TvRepository.DeleteChild(request); } public async Task RemoveTvRequest(int requestId) { var request = await TvRepository.Get().FirstOrDefaultAsync(x => x.Id == requestId); + await Audit.Record(AuditType.Deleted, AuditArea.TvRequest, $"Deleting Request {request.Title}", Username); await TvRepository.Delete(request); } diff --git a/src/Ombi.Core/Helpers/TvShowRequestBuilder.cs b/src/Ombi.Core/Helpers/TvShowRequestBuilder.cs index 3dfe3a097..b86c7bf34 100644 --- a/src/Ombi.Core/Helpers/TvShowRequestBuilder.cs +++ b/src/Ombi.Core/Helpers/TvShowRequestBuilder.cs @@ -123,17 +123,20 @@ namespace Ombi.Core.Helpers else if (tv.LatestSeason) { var episodes = await TvApi.EpisodeLookup(ShowInfo.id); - var latest = episodes.OrderBy(x => x.season).FirstOrDefault(); + var latest = episodes.OrderByDescending(x => x.season).FirstOrDefault(); var episodesRequests = new List(); foreach (var ep in episodes) { - episodesRequests.Add(new EpisodeRequests + if (ep.season == latest.season) { - EpisodeNumber = ep.number, - AirDate = DateTime.Parse(ep.airdate), - Title = ep.name, - Url = ep.url - }); + episodesRequests.Add(new EpisodeRequests + { + EpisodeNumber = ep.number, + AirDate = DateTime.Parse(ep.airdate), + Title = ep.name, + Url = ep.url + }); + } } ChildRequest.SeasonRequests.Add(new SeasonRequests { @@ -144,7 +147,7 @@ namespace Ombi.Core.Helpers else if (tv.FirstSeason) { var episodes = await TvApi.EpisodeLookup(ShowInfo.id); - var first = episodes.OrderByDescending(x => x.season).FirstOrDefault(); + var first = episodes.OrderBy(x => x.season).FirstOrDefault(); var episodesRequests = new List(); foreach (var ep in episodes) { diff --git a/src/Ombi.Core/IdentityResolver/ResourceOwnerPasswordValidator.cs b/src/Ombi.Core/IdentityResolver/ResourceOwnerPasswordValidator.cs index 7285aace8..6c2903491 100644 --- a/src/Ombi.Core/IdentityResolver/ResourceOwnerPasswordValidator.cs +++ b/src/Ombi.Core/IdentityResolver/ResourceOwnerPasswordValidator.cs @@ -7,63 +7,116 @@ using IdentityServer4.Models; using IdentityServer4.Validation; using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using Ombi.Api.Emby; using Ombi.Api.Emby.Models; using Ombi.Api.Plex; using Ombi.Api.Plex.Models; using Ombi.Core.Settings; using Ombi.Core.Settings.Models.External; +using Ombi.Helpers; +using Ombi.Settings.Settings.Models; using Ombi.Store.Entities; +using Ombi.Store.Repository; namespace Ombi.Core.IdentityResolver { public class OmbiOwnerPasswordValidator : IResourceOwnerPasswordValidator { - public OmbiOwnerPasswordValidator(UserManager um, IPlexApi api, - ISettingsService settings) + public OmbiOwnerPasswordValidator(UserManager um, IPlexApi plexApi, IEmbyApi embyApi, + ISettingsService settings, ISettingsService ombiSettings, + ISettingsService embySettings, IAuditRepository log) { UserManager = um; - Api = api; + PlexApi = plexApi; PlexSettings = settings; + OmbiSettings = ombiSettings; + EmbyApi = embyApi; + EmbySettings = embySettings; + Audit = log; } private UserManager UserManager { get; } - private IPlexApi Api { get; } + private IPlexApi PlexApi { get; } + private IEmbyApi EmbyApi{ get; } private ISettingsService PlexSettings { get; } + private ISettingsService EmbySettings { get; } + private ISettingsService OmbiSettings { get; } + private IAuditRepository Audit { get; } public async Task ValidateAsync(ResourceOwnerPasswordValidationContext context) { + await Audit.Record(AuditType.None, AuditArea.Authentication, $"User {context.UserName} attempted to login", context.UserName); var users = UserManager.Users; if (await LocalUser(context, users)) { return; } - if (await PlexUser(context, users)) + var ombi = await OmbiSettings.GetSettingsAsync(); + if (ombi.AllowExternalUsersToAuthenticate) { - return; - } - if (await EmbyUser(context, users)) - { - return; + if (await PlexUser(context, users)) + { + return; + } + if (await EmbyUser(context, users)) + { + return; + } } + + await Audit.Record(AuditType.Fail, AuditArea.Authentication, $"User {context.UserName} failed to login", context.UserName); + context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, "Username or password is incorrect"); } private async Task PlexUser(ResourceOwnerPasswordValidationContext context, IQueryable users) { - var signInResult = await Api.SignIn(new UserRequest {login = context.UserName, password = context.Password}); - if (signInResult.user == null) + var signInResult = await PlexApi.SignIn(new UserRequest {login = context.UserName, password = context.Password}); + if (signInResult?.user == null) { return false; } // Do we have a local user? - var user = await users.FirstOrDefaultAsync(x => x.UserName == context.UserName && x.UserType == UserType.PlexUser); + return await GetUserDetails(context, users, UserType.PlexUser); + } + + private async Task EmbyUser(ResourceOwnerPasswordValidationContext context, IQueryable users) + { + var embySettings = await EmbySettings.GetSettingsAsync(); + var signInResult = await EmbyApi.LogIn(context.UserName, context.Password, embySettings.ApiKey, + embySettings.FullUri); + + if (string.IsNullOrEmpty(signInResult?.Name)) + { + return false; + } - throw new NotImplementedException(); // TODO finish + return await GetUserDetails(context, users, UserType.EmbyUser); } - private Task EmbyUser(ResourceOwnerPasswordValidationContext context, IQueryable users) + private async Task GetUserDetails(ResourceOwnerPasswordValidationContext context, IQueryable users, UserType userType) { - throw new NotImplementedException(); + var user = await users.FirstOrDefaultAsync(x => x.UserName == context.UserName && x.UserType == userType); + if (user != null) + { + var roles = await UserManager.GetRolesAsync(user); + var claims = new List + { + new Claim(ClaimTypes.Name, user.UserName) + }; + + foreach (var role in roles) + { + claims.Add(new Claim(ClaimTypes.Role, role)); + } + context.Result = new GrantValidationResult(user.UserName, "password", claims); + + return true; + } + + // Create the user? + return true; } public async Task LocalUser(ResourceOwnerPasswordValidationContext context, IQueryable users) @@ -72,13 +125,13 @@ namespace Ombi.Core.IdentityResolver if (user == null) { - //context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, "Username or password is incorrect"); return false; } var passwordValid = await UserManager.CheckPasswordAsync(user, context.Password); if (!passwordValid) { + await Audit.Record(AuditType.Fail, AuditArea.Authentication, $"User {context.UserName} failed to login", context.UserName); context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, "Username or password is incorrect"); return true; @@ -96,6 +149,7 @@ namespace Ombi.Core.IdentityResolver } context.Result = new GrantValidationResult(user.UserName, "password", claims); + await Audit.Record(AuditType.Success, AuditArea.Authentication, $"User {context.UserName} has logged in", context.UserName); return true; } } diff --git a/src/Ombi.Core/Senders/TvSender.cs b/src/Ombi.Core/Senders/TvSender.cs index 217e076e8..e20e8c54a 100644 --- a/src/Ombi.Core/Senders/TvSender.cs +++ b/src/Ombi.Core/Senders/TvSender.cs @@ -1,16 +1,16 @@ -using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; using Ombi.Api.Sonarr; using Ombi.Api.Sonarr.Models; using Ombi.Core.Settings; using Ombi.Core.Settings.Models.External; using Ombi.Helpers; using Ombi.Store.Entities.Requests; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -namespace Ombi.Core +namespace Ombi.Core.Senders { public class TvSender : ITvSender { @@ -99,7 +99,7 @@ namespace Ombi.Core // Ok, now let's sort out the episodes. var sonarrEpisodes = await SonarrApi.GetEpisodes(result.id, s.ApiKey, s.FullUri); - while (sonarrEpisodes.Count() == 0) + while (!sonarrEpisodes.Any()) { // It could be that the series metadata is not ready yet. So wait sonarrEpisodes = await SonarrApi.GetEpisodes(result.id, s.ApiKey, s.FullUri); diff --git a/src/Ombi.DependencyInjection/IocExtensions.cs b/src/Ombi.DependencyInjection/IocExtensions.cs index 589e21373..7f94f8705 100644 --- a/src/Ombi.DependencyInjection/IocExtensions.cs +++ b/src/Ombi.DependencyInjection/IocExtensions.cs @@ -28,8 +28,10 @@ using Ombi.Store.Repository; using Ombi.Notifications.Agents; using Ombi.Schedule.Jobs.Radarr; using Ombi.Api; +using Ombi.Api.FanartTv; using Ombi.Api.Service; using Ombi.Core.Rule.Interfaces; +using Ombi.Core.Senders; using Ombi.Schedule.Ombi; using Ombi.Store.Repository.Requests; @@ -71,6 +73,7 @@ namespace Ombi.DependencyInjection services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); } public static void RegisterStore(this IServiceCollection services) @@ -86,6 +89,8 @@ namespace Ombi.DependencyInjection services.AddTransient(); services.AddTransient(); + services.AddTransient(); + services.AddTransient(); //services.AddTransient(); services.AddTransient(typeof(ISettingsService<>), typeof(SettingsService<>)); } diff --git a/src/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj b/src/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj index 1c26342a6..f130ec11b 100644 --- a/src/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj +++ b/src/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj @@ -13,6 +13,7 @@ + diff --git a/src/Ombi.Helpers/LoggingEvents.cs b/src/Ombi.Helpers/LoggingEvents.cs index 85fa25b31..ae48d6746 100644 --- a/src/Ombi.Helpers/LoggingEvents.cs +++ b/src/Ombi.Helpers/LoggingEvents.cs @@ -4,6 +4,8 @@ namespace Ombi.Helpers { public class LoggingEvents { + public static EventId Authentication => new EventId(500); + public static EventId ApiException => new EventId(1000); public static EventId RadarrApiException => new EventId(1001); diff --git a/src/Ombi.Settings/Settings/Models/OmbiSettings.cs b/src/Ombi.Settings/Settings/Models/OmbiSettings.cs index 15263ff3a..3c989dc2e 100644 --- a/src/Ombi.Settings/Settings/Models/OmbiSettings.cs +++ b/src/Ombi.Settings/Settings/Models/OmbiSettings.cs @@ -1,13 +1,12 @@ -namespace Ombi.Core.Settings.Models +namespace Ombi.Settings.Settings.Models { - public class OmbiSettings : Ombi.Settings.Settings.Models.Settings + public class OmbiSettings : Models.Settings { - public int Port { get; set; } //public string BaseUrl { get; set; } public bool CollectAnalyticData { get; set; } public bool Wizard { get; set; } - - public string ExternalUrl { get; set; } public string ApiKey { get; set; } + + public bool AllowExternalUsersToAuthenticate { get; set; } } } \ No newline at end of file diff --git a/src/Ombi.Store/Context/IOmbiContext.cs b/src/Ombi.Store/Context/IOmbiContext.cs index 046211f89..95f78a54a 100644 --- a/src/Ombi.Store/Context/IOmbiContext.cs +++ b/src/Ombi.Store/Context/IOmbiContext.cs @@ -23,7 +23,7 @@ namespace Ombi.Store.Context DbSet NotificationTemplates { get; set; } DbSet ApplicationConfigurations { get; set; } void Seed(); - + DbSet Audit { get; set; } DbSet MovieRequests { get; set; } DbSet TvRequests { get; set; } DbSet ChildRequests { get; set; } diff --git a/src/Ombi.Store/Context/OmbiContext.cs b/src/Ombi.Store/Context/OmbiContext.cs index 209c3e6e1..a87c41b28 100644 --- a/src/Ombi.Store/Context/OmbiContext.cs +++ b/src/Ombi.Store/Context/OmbiContext.cs @@ -34,6 +34,8 @@ namespace Ombi.Store.Context public DbSet MovieIssues { get; set; } public DbSet TvIssues { get; set; } + public DbSet Audit { get; set; } + public DbSet ApplicationConfigurations { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) @@ -44,6 +46,30 @@ namespace Ombi.Store.Context public void Seed() { + + // Add the tokens + var fanArt = ApplicationConfigurations.FirstOrDefault(x => x.Type == ConfigurationTypes.FanartTv); + if (fanArt == null) + { + ApplicationConfigurations.Add(new ApplicationConfiguration + { + Type = ConfigurationTypes.FanartTv, + Value = "4b6d983efa54d8f45c68432521335f15" + }); + SaveChanges(); + } + var movieDb = ApplicationConfigurations.FirstOrDefault(x => x.Type == ConfigurationTypes.FanartTv); + if (movieDb == null) + { + ApplicationConfigurations.Add(new ApplicationConfiguration + { + Type = ConfigurationTypes.TheMovieDb, + Value = "b8eabaf5608b88d0298aa189dd90bf00" + }); + SaveChanges(); + } + + // Check if templates exist var templates = NotificationTemplates.ToList(); if (templates.Any()) diff --git a/src/Ombi.Store/Entities/ApplicationConfiguration.cs b/src/Ombi.Store/Entities/ApplicationConfiguration.cs index cdd103aea..adf7e3342 100644 --- a/src/Ombi.Store/Entities/ApplicationConfiguration.cs +++ b/src/Ombi.Store/Entities/ApplicationConfiguration.cs @@ -13,5 +13,7 @@ namespace Ombi.Store.Entities { Url, Port, + FanartTv, + TheMovieDb } } \ No newline at end of file diff --git a/src/Ombi.Store/Entities/Audit.cs b/src/Ombi.Store/Entities/Audit.cs new file mode 100644 index 000000000..4f1f8c39a --- /dev/null +++ b/src/Ombi.Store/Entities/Audit.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; +using System.Text; + +namespace Ombi.Store.Entities +{ + [Table("Audit")] + public class Audit : Entity + { + public DateTime DateTime { get; set; } + public string Description { get; set; } + public AuditType AuditType { get; set; } + public AuditArea AuditArea { get; set; } + public string User { get; set; } + } + + public enum AuditType + { + None, + Created, + Updated, + Deleted, + Approved, + Denied, + Success, + Fail, + Added + } + + public enum AuditArea + { + Authentication, + User, + TvRequest, + MovieRequest, + } +} diff --git a/src/Ombi.Store/Migrations/20170728131851_Inital.Designer.cs b/src/Ombi.Store/Migrations/20170801143617_Inital.Designer.cs similarity index 96% rename from src/Ombi.Store/Migrations/20170728131851_Inital.Designer.cs rename to src/Ombi.Store/Migrations/20170801143617_Inital.Designer.cs index 3731625aa..8c3ab43d2 100644 --- a/src/Ombi.Store/Migrations/20170728131851_Inital.Designer.cs +++ b/src/Ombi.Store/Migrations/20170801143617_Inital.Designer.cs @@ -10,7 +10,7 @@ using Ombi.Helpers; namespace Ombi.Store.Migrations { [DbContext(typeof(OmbiContext))] - [Migration("20170728131851_Inital")] + [Migration("20170801143617_Inital")] partial class Inital { protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -139,6 +139,26 @@ namespace Ombi.Store.Migrations b.ToTable("ApplicationConfiguration"); }); + modelBuilder.Entity("Ombi.Store.Entities.Audit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AuditArea"); + + b.Property("AuditType"); + + b.Property("DateTime"); + + b.Property("Description"); + + b.Property("User"); + + b.HasKey("Id"); + + b.ToTable("Audit"); + }); + modelBuilder.Entity("Ombi.Store.Entities.GlobalSettings", b => { b.Property("Id") diff --git a/src/Ombi.Store/Migrations/20170728131851_Inital.cs b/src/Ombi.Store/Migrations/20170801143617_Inital.cs similarity index 96% rename from src/Ombi.Store/Migrations/20170728131851_Inital.cs rename to src/Ombi.Store/Migrations/20170801143617_Inital.cs index bc195c6b2..eff9e7734 100644 --- a/src/Ombi.Store/Migrations/20170728131851_Inital.cs +++ b/src/Ombi.Store/Migrations/20170801143617_Inital.cs @@ -50,6 +50,23 @@ namespace Ombi.Store.Migrations table.PrimaryKey("PK_ApplicationConfiguration", x => x.Id); }); + migrationBuilder.CreateTable( + name: "Audit", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + AuditArea = table.Column(nullable: false), + AuditType = table.Column(nullable: false), + DateTime = table.Column(nullable: false), + Description = table.Column(nullable: true), + User = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Audit", x => x.Id); + }); + migrationBuilder.CreateTable( name: "GlobalSettings", columns: table => new @@ -545,6 +562,9 @@ namespace Ombi.Store.Migrations migrationBuilder.DropTable( name: "ApplicationConfiguration"); + migrationBuilder.DropTable( + name: "Audit"); + migrationBuilder.DropTable( name: "GlobalSettings"); diff --git a/src/Ombi.Store/Migrations/OmbiContextModelSnapshot.cs b/src/Ombi.Store/Migrations/OmbiContextModelSnapshot.cs index b18f5ccaa..b67c5df06 100644 --- a/src/Ombi.Store/Migrations/OmbiContextModelSnapshot.cs +++ b/src/Ombi.Store/Migrations/OmbiContextModelSnapshot.cs @@ -138,6 +138,26 @@ namespace Ombi.Store.Migrations b.ToTable("ApplicationConfiguration"); }); + modelBuilder.Entity("Ombi.Store.Entities.Audit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AuditArea"); + + b.Property("AuditType"); + + b.Property("DateTime"); + + b.Property("Description"); + + b.Property("User"); + + b.HasKey("Id"); + + b.ToTable("Audit"); + }); + modelBuilder.Entity("Ombi.Store.Entities.GlobalSettings", b => { b.Property("Id") diff --git a/src/Ombi.Store/Repository/ApplicationConfigRepository.cs b/src/Ombi.Store/Repository/ApplicationConfigRepository.cs new file mode 100644 index 000000000..3b4476454 --- /dev/null +++ b/src/Ombi.Store/Repository/ApplicationConfigRepository.cs @@ -0,0 +1,23 @@ +using System.Linq; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Ombi.Store.Context; +using Ombi.Store.Entities; + +namespace Ombi.Store.Repository +{ + public class ApplicationConfigRepository : IApplicationConfigRepository + { + public ApplicationConfigRepository(IOmbiContext ctx) + { + Ctx = ctx; + } + + private IOmbiContext Ctx { get; } + + public async Task Get(ConfigurationTypes type) + { + return await Ctx.ApplicationConfigurations.FirstOrDefaultAsync(x => x.Type == type); + } + } +} \ No newline at end of file diff --git a/src/Ombi.Store/Repository/AuditRepository.cs b/src/Ombi.Store/Repository/AuditRepository.cs new file mode 100644 index 000000000..a6fc42c3f --- /dev/null +++ b/src/Ombi.Store/Repository/AuditRepository.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using Ombi.Store.Context; +using Ombi.Store.Entities; + +namespace Ombi.Store.Repository +{ + public class AuditRepository : IAuditRepository + { + public AuditRepository(IOmbiContext ctx) + { + Ctx = ctx; + } + + private IOmbiContext Ctx { get; } + + + public async Task Record(AuditType type, AuditArea area, string description) + { + await Record(type, area, description, string.Empty); + } + + public async Task Record(AuditType type, AuditArea area, string description, string user) + { + await Ctx.Audit.AddAsync(new Audit + { + User = user, + AuditArea = area, + AuditType = type, + DateTime = DateTime.UtcNow, + Description = description + }); + + await Ctx.SaveChangesAsync(); + } + } +} diff --git a/src/Ombi.Store/Repository/IApplicationConfigRepository.cs b/src/Ombi.Store/Repository/IApplicationConfigRepository.cs new file mode 100644 index 000000000..9e35bde7e --- /dev/null +++ b/src/Ombi.Store/Repository/IApplicationConfigRepository.cs @@ -0,0 +1,10 @@ +using System.Threading.Tasks; +using Ombi.Store.Entities; + +namespace Ombi.Store.Repository +{ + public interface IApplicationConfigRepository + { + Task Get(ConfigurationTypes type); + } +} \ No newline at end of file diff --git a/src/Ombi.Store/Repository/IAuditRepository.cs b/src/Ombi.Store/Repository/IAuditRepository.cs new file mode 100644 index 000000000..2fbc4b486 --- /dev/null +++ b/src/Ombi.Store/Repository/IAuditRepository.cs @@ -0,0 +1,11 @@ +using System.Threading.Tasks; +using Ombi.Store.Entities; + +namespace Ombi.Store.Repository +{ + public interface IAuditRepository + { + Task Record(AuditType type, AuditArea area, string description); + Task Record(AuditType type, AuditArea area, string description, string user); + } +} \ No newline at end of file diff --git a/src/Ombi.sln b/src/Ombi.sln index 0e48bdb1d..4866b2be0 100644 --- a/src/Ombi.sln +++ b/src/Ombi.sln @@ -67,6 +67,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ombi.Notifications.Tests", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ombi.Api.Service", "Ombi.Api.Service\Ombi.Api.Service.csproj", "{A0892896-F5BD-47E2-823E-DFCE82514EEC}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Api.FanartTv", "Ombi.Api.FanartTv\Ombi.Api.FanartTv.csproj", "{FD947E63-A0D2-4878-8378-2005D5E9AB8A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -165,6 +167,10 @@ Global {A0892896-F5BD-47E2-823E-DFCE82514EEC}.Debug|Any CPU.Build.0 = Debug|Any CPU {A0892896-F5BD-47E2-823E-DFCE82514EEC}.Release|Any CPU.ActiveCfg = Release|Any CPU {A0892896-F5BD-47E2-823E-DFCE82514EEC}.Release|Any CPU.Build.0 = Release|Any CPU + {FD947E63-A0D2-4878-8378-2005D5E9AB8A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FD947E63-A0D2-4878-8378-2005D5E9AB8A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FD947E63-A0D2-4878-8378-2005D5E9AB8A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FD947E63-A0D2-4878-8378-2005D5E9AB8A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -187,5 +193,6 @@ Global {6294A82D-4915-4FC3-B301-8F985716F34C} = {D11FE57E-1E57-491D-A1D4-01AEF4BE5CB6} {2C7836E7-B120-40A6-B641-DDAA02FBAE23} = {6F42AB98-9196-44C4-B888-D5E409F415A1} {A0892896-F5BD-47E2-823E-DFCE82514EEC} = {9293CA11-360A-4C20-A674-B9E794431BF5} + {FD947E63-A0D2-4878-8378-2005D5E9AB8A} = {9293CA11-360A-4C20-A674-B9E794431BF5} EndGlobalSection EndGlobal diff --git a/src/Ombi/ClientApp/app/interfaces/ISettings.ts b/src/Ombi/ClientApp/app/interfaces/ISettings.ts index f00822267..d5e1486b1 100644 --- a/src/Ombi/ClientApp/app/interfaces/ISettings.ts +++ b/src/Ombi/ClientApp/app/interfaces/ISettings.ts @@ -15,7 +15,8 @@ export interface IOmbiSettings extends ISettings { collectAnalyticData: boolean, wizard: boolean, apiKey: string, - externalUrl:string, + externalUrl: string, + allowExternalUsersToAuthenticate:boolean, } export interface IEmbySettings extends IExternalSettings { diff --git a/src/Ombi/ClientApp/app/settings/ombi/ombi.component.html b/src/Ombi/ClientApp/app/settings/ombi/ombi.component.html index 87a90acbe..9f3896e2b 100644 --- a/src/Ombi/ClientApp/app/settings/ombi/ombi.component.html +++ b/src/Ombi/ClientApp/app/settings/ombi/ombi.component.html @@ -20,8 +20,16 @@ - +
+
+ + +
+
+ +
diff --git a/src/Ombi/ClientApp/app/settings/ombi/ombi.component.ts b/src/Ombi/ClientApp/app/settings/ombi/ombi.component.ts index da7d45d71..5afe186e4 100644 --- a/src/Ombi/ClientApp/app/settings/ombi/ombi.component.ts +++ b/src/Ombi/ClientApp/app/settings/ombi/ombi.component.ts @@ -22,13 +22,14 @@ export class OmbiComponent implements OnInit { collectAnalyticData: [x.collectAnalyticData], apiKey: [{ value: x.apiKey, disabled: true }], externalUrl: [x.externalUrl], - }); + allowExternalUsersToAuthenticate: [x.allowExternalUsersToAuthenticate] + }); }); } refreshApiKey() { - + } onSubmit(form: FormGroup) { diff --git a/src/Ombi/Controllers/StatusController.cs b/src/Ombi/Controllers/StatusController.cs index 761216ddf..98f6dadbf 100644 --- a/src/Ombi/Controllers/StatusController.cs +++ b/src/Ombi/Controllers/StatusController.cs @@ -32,6 +32,7 @@ using Microsoft.AspNetCore.Mvc; using Ombi.Core.Settings; using Ombi.Core.Settings.Models; using Ombi.Core.Update; +using Ombi.Settings.Settings.Models; namespace Ombi.Controllers { diff --git a/src/Ombi/Startup.cs b/src/Ombi/Startup.cs index b615b0e8d..788525c7a 100644 --- a/src/Ombi/Startup.cs +++ b/src/Ombi/Startup.cs @@ -178,8 +178,6 @@ namespace Ombi // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IMemoryCache cache) { - //loggerFactory.AddConsole(Configuration.GetSection("Logging")); - //loggerFactory.AddDebug(); var options = (IOptions) app.ApplicationServices.GetService( typeof(IOptions));