diff --git a/MediaBrowser.Api/BaseApiService.cs b/MediaBrowser.Api/BaseApiService.cs index f8379a8103..297a131557 100644 --- a/MediaBrowser.Api/BaseApiService.cs +++ b/MediaBrowser.Api/BaseApiService.cs @@ -84,7 +84,6 @@ namespace MediaBrowser.Api /// Gets the session. /// /// SessionInfo. - /// Session not found. protected SessionInfo GetSession() { var session = SessionContext.GetSession(Request); diff --git a/MediaBrowser.Api/ConfigurationService.cs b/MediaBrowser.Api/ConfigurationService.cs index f6edea86e2..5fe606e16e 100644 --- a/MediaBrowser.Api/ConfigurationService.cs +++ b/MediaBrowser.Api/ConfigurationService.cs @@ -9,7 +9,6 @@ using MediaBrowser.Model.Serialization; using ServiceStack; using ServiceStack.Text.Controller; using ServiceStack.Web; -using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -33,18 +32,18 @@ namespace MediaBrowser.Api [ApiMember(Name = "Key", Description = "Key", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] public string Key { get; set; } } - + /// /// Class UpdateConfiguration /// [Route("/System/Configuration", "POST", Summary = "Updates application configuration")] - [Authenticated] + [Authenticated(Roles = "Admin")] public class UpdateConfiguration : ServerConfiguration, IReturnVoid { } [Route("/System/Configuration/{Key}", "POST", Summary = "Updates named configuration")] - [Authenticated] + [Authenticated(Roles = "Admin")] public class UpdateNamedConfiguration : IReturnVoid, IRequiresRequestStream { [ApiMember(Name = "Key", Description = "Key", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] @@ -52,23 +51,23 @@ namespace MediaBrowser.Api public Stream RequestStream { get; set; } } - + [Route("/System/Configuration/MetadataOptions/Default", "GET", Summary = "Gets a default MetadataOptions object")] - [Authenticated] + [Authenticated(Roles = "Admin")] public class GetDefaultMetadataOptions : IReturn { } [Route("/System/Configuration/MetadataPlugins", "GET", Summary = "Gets all available metadata plugins")] - [Authenticated] + [Authenticated(Roles = "Admin")] public class GetMetadataPlugins : IReturn> { } [Route("/System/Configuration/MetadataPlugins/Autoset", "POST")] - [Authenticated] + [Authenticated(Roles = "Admin", AllowBeforeStartupWizard = true)] public class AutoSetMetadataOptions : IReturnVoid { @@ -149,7 +148,7 @@ namespace MediaBrowser.Api var configurationType = _configurationManager.GetConfigurationType(key); var configuration = _jsonSerializer.DeserializeFromStream(request.RequestStream, configurationType); - + _configurationManager.SaveConfiguration(key, configuration); } diff --git a/MediaBrowser.Api/Devices/DeviceService.cs b/MediaBrowser.Api/Devices/DeviceService.cs index b873c6a712..0d86d6a5c0 100644 --- a/MediaBrowser.Api/Devices/DeviceService.cs +++ b/MediaBrowser.Api/Devices/DeviceService.cs @@ -12,6 +12,7 @@ using System.Threading.Tasks; namespace MediaBrowser.Api.Devices { [Route("/Devices", "GET", Summary = "Gets all devices")] + [Authenticated(Roles = "Admin")] public class GetDevices : IReturn> { [ApiMember(Name = "SupportsContentUploading", Description = "SupportsContentUploading", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] @@ -26,6 +27,7 @@ namespace MediaBrowser.Api.Devices } [Route("/Devices/CameraUploads", "GET", Summary = "Gets camera upload history for a device")] + [Authenticated] public class GetCameraUploads : IReturn { [ApiMember(Name = "Id", Description = "Device Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] @@ -33,6 +35,7 @@ namespace MediaBrowser.Api.Devices } [Route("/Devices/CameraUploads", "POST", Summary = "Uploads content")] + [Authenticated] public class PostCameraUpload : IRequiresRequestStream, IReturnVoid { [ApiMember(Name = "DeviceId", Description = "Device Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] @@ -51,6 +54,7 @@ namespace MediaBrowser.Api.Devices } [Route("/Devices/Info", "GET", Summary = "Gets device info")] + [Authenticated] public class GetDeviceInfo : IReturn { [ApiMember(Name = "Id", Description = "Device Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "DELETE")] @@ -58,6 +62,7 @@ namespace MediaBrowser.Api.Devices } [Route("/Devices/Capabilities", "GET", Summary = "Gets device capabilities")] + [Authenticated] public class GetDeviceCapabilities : IReturn { [ApiMember(Name = "Id", Description = "Device Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "DELETE")] @@ -65,13 +70,13 @@ namespace MediaBrowser.Api.Devices } [Route("/Devices/Options", "POST", Summary = "Updates device options")] + [Authenticated(Roles = "Admin")] public class PostDeviceOptions : DeviceOptions, IReturnVoid { [ApiMember(Name = "Id", Description = "Device Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "DELETE")] public string Id { get; set; } } - - [Authenticated] + public class DeviceService : BaseApiService { private readonly IDeviceManager _deviceManager; diff --git a/MediaBrowser.Api/DisplayPreferencesService.cs b/MediaBrowser.Api/DisplayPreferencesService.cs index 9f3a6134e9..72b12020d0 100644 --- a/MediaBrowser.Api/DisplayPreferencesService.cs +++ b/MediaBrowser.Api/DisplayPreferencesService.cs @@ -24,9 +24,6 @@ namespace MediaBrowser.Api [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")] public Guid UserId { get; set; } - - [ApiMember(Name = "Client", Description = "Client", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")] - public string Client { get; set; } } [Route("/DisplayPreferences/{Id}", "GET", Summary = "Gets a user's display preferences for an item")] diff --git a/MediaBrowser.Api/Dlna/DlnaService.cs b/MediaBrowser.Api/Dlna/DlnaService.cs index fec6d698ae..b6c4f5dfba 100644 --- a/MediaBrowser.Api/Dlna/DlnaService.cs +++ b/MediaBrowser.Api/Dlna/DlnaService.cs @@ -43,7 +43,7 @@ namespace MediaBrowser.Api.Dlna { } - [Authenticated] + [Authenticated(Roles = "Admin")] public class DlnaService : BaseApiService { private readonly IDlnaManager _dlnaManager; diff --git a/MediaBrowser.Api/EnvironmentService.cs b/MediaBrowser.Api/EnvironmentService.cs index 590deff5a7..4265d0f9fc 100644 --- a/MediaBrowser.Api/EnvironmentService.cs +++ b/MediaBrowser.Api/EnvironmentService.cs @@ -87,7 +87,7 @@ namespace MediaBrowser.Api /// /// Class EnvironmentService /// - [Authenticated] + [Authenticated(Roles = "Admin")] public class EnvironmentService : BaseApiService { const char UncSeparator = '\\'; diff --git a/MediaBrowser.Api/Images/ImageByNameService.cs b/MediaBrowser.Api/Images/ImageByNameService.cs index 99d2f144b1..b4d5a99497 100644 --- a/MediaBrowser.Api/Images/ImageByNameService.cs +++ b/MediaBrowser.Api/Images/ImageByNameService.cs @@ -2,6 +2,7 @@ using MediaBrowser.Common.IO; using MediaBrowser.Controller; using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Net; using MediaBrowser.Model.Dto; using ServiceStack; using System; @@ -14,8 +15,7 @@ namespace MediaBrowser.Api.Images /// /// Class GetGeneralImage /// - [Route("/Images/General/{Name}/{Type}", "GET")] - [Api(Description = "Gets a general image by name")] + [Route("/Images/General/{Name}/{Type}", "GET", Summary = "Gets a general image by name")] public class GetGeneralImage { /// @@ -32,8 +32,7 @@ namespace MediaBrowser.Api.Images /// /// Class GetRatingImage /// - [Route("/Images/Ratings/{Theme}/{Name}", "GET")] - [Api(Description = "Gets a rating image by name")] + [Route("/Images/Ratings/{Theme}/{Name}", "GET", Summary = "Gets a rating image by name")] public class GetRatingImage { /// @@ -54,8 +53,7 @@ namespace MediaBrowser.Api.Images /// /// Class GetMediaInfoImage /// - [Route("/Images/MediaInfo/{Theme}/{Name}", "GET")] - [Api(Description = "Gets a media info image by name")] + [Route("/Images/MediaInfo/{Theme}/{Name}", "GET", Summary = "Gets a media info image by name")] public class GetMediaInfoImage { /// @@ -73,20 +71,20 @@ namespace MediaBrowser.Api.Images public string Theme { get; set; } } - [Route("/Images/MediaInfo", "GET")] - [Api(Description = "Gets all media info image by name")] + [Route("/Images/MediaInfo", "GET", Summary = "Gets all media info image by name")] + [Authenticated] public class GetMediaInfoImages : IReturn> { } - [Route("/Images/Ratings", "GET")] - [Api(Description = "Gets all rating images by name")] + [Route("/Images/Ratings", "GET", Summary = "Gets all rating images by name")] + [Authenticated] public class GetRatingImages : IReturn> { } - [Route("/Images/General", "GET")] - [Api(Description = "Gets all general images by name")] + [Route("/Images/General", "GET", Summary = "Gets all general images by name")] + [Authenticated] public class GetGeneralImages : IReturn> { } diff --git a/MediaBrowser.Api/Images/ImageService.cs b/MediaBrowser.Api/Images/ImageService.cs index 3262537957..561ce4a5cd 100644 --- a/MediaBrowser.Api/Images/ImageService.cs +++ b/MediaBrowser.Api/Images/ImageService.cs @@ -24,8 +24,7 @@ namespace MediaBrowser.Api.Images /// /// Class GetItemImage /// - [Route("/Items/{Id}/Images", "GET")] - [Api(Description = "Gets information about an item's images")] + [Route("/Items/{Id}/Images", "GET", Summary = "Gets information about an item's images")] [Authenticated] public class GetItemImageInfos : IReturn> { @@ -43,7 +42,6 @@ namespace MediaBrowser.Api.Images [Route("/Items/{Id}/Images/{Type}/{Index}", "HEAD")] [Route("/Items/{Id}/Images/{Type}/{Index}/{Tag}/{Format}/{MaxWidth}/{MaxHeight}/{PercentPlayed}", "GET")] [Route("/Items/{Id}/Images/{Type}/{Index}/{Tag}/{Format}/{MaxWidth}/{MaxHeight}/{PercentPlayed}", "HEAD")] - [Api(Description = "Gets an item image")] public class GetItemImage : ImageRequest { /// @@ -57,8 +55,7 @@ namespace MediaBrowser.Api.Images /// /// Class UpdateItemImageIndex /// - [Route("/Items/{Id}/Images/{Type}/{Index}/Index", "POST")] - [Api(Description = "Updates the index for an item image")] + [Route("/Items/{Id}/Images/{Type}/{Index}/Index", "POST", Summary = "Updates the index for an item image")] [Authenticated] public class UpdateItemImageIndex : IReturnVoid { @@ -122,7 +119,6 @@ namespace MediaBrowser.Api.Images [Route("/Studios/{Name}/Images/{Type}/{Index}", "HEAD")] [Route("/Years/{Year}/Images/{Type}", "HEAD")] [Route("/Years/{Year}/Images/{Type}/{Index}", "HEAD")] - [Api(Description = "Gets an item by name image")] public class GetItemByNameImage : ImageRequest { /// @@ -140,7 +136,6 @@ namespace MediaBrowser.Api.Images [Route("/Users/{Id}/Images/{Type}/{Index}", "GET")] [Route("/Users/{Id}/Images/{Type}", "HEAD")] [Route("/Users/{Id}/Images/{Type}/{Index}", "HEAD")] - [Api(Description = "Gets a user image")] public class GetUserImage : ImageRequest { /// @@ -156,7 +151,6 @@ namespace MediaBrowser.Api.Images /// [Route("/Items/{Id}/Images/{Type}", "DELETE")] [Route("/Items/{Id}/Images/{Type}/{Index}", "DELETE")] - [Api(Description = "Deletes an item image")] [Authenticated] public class DeleteItemImage : DeleteImageRequest, IReturnVoid { @@ -173,7 +167,6 @@ namespace MediaBrowser.Api.Images /// [Route("/Users/{Id}/Images/{Type}", "DELETE")] [Route("/Users/{Id}/Images/{Type}/{Index}", "DELETE")] - [Api(Description = "Deletes a user image")] [Authenticated] public class DeleteUserImage : DeleteImageRequest, IReturnVoid { @@ -190,7 +183,6 @@ namespace MediaBrowser.Api.Images /// [Route("/Users/{Id}/Images/{Type}", "POST")] [Route("/Users/{Id}/Images/{Type}/{Index}", "POST")] - [Api(Description = "Posts a user image")] [Authenticated] public class PostUserImage : DeleteImageRequest, IRequiresRequestStream, IReturnVoid { diff --git a/MediaBrowser.Api/Images/RemoteImageService.cs b/MediaBrowser.Api/Images/RemoteImageService.cs index 235f18296a..6d2579ea9f 100644 --- a/MediaBrowser.Api/Images/RemoteImageService.cs +++ b/MediaBrowser.Api/Images/RemoteImageService.cs @@ -5,11 +5,11 @@ using MediaBrowser.Controller; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Providers; using ServiceStack; -using ServiceStack.Text.Controller; using System; using System.Collections.Generic; using System.IO; @@ -45,8 +45,8 @@ namespace MediaBrowser.Api.Images public bool IncludeAllLanguages { get; set; } } - [Route("/Items/{Id}/RemoteImages", "GET")] - [Api(Description = "Gets available remote images for an item")] + [Route("/Items/{Id}/RemoteImages", "GET", Summary = "Gets available remote images for an item")] + [Authenticated] public class GetRemoteImages : BaseRemoteImageRequest { /// @@ -57,25 +57,8 @@ namespace MediaBrowser.Api.Images public string Id { get; set; } } - [Route("/Artists/{Name}/RemoteImages", "GET")] - [Route("/Genres/{Name}/RemoteImages", "GET")] - [Route("/GameGenres/{Name}/RemoteImages", "GET")] - [Route("/MusicGenres/{Name}/RemoteImages", "GET")] - [Route("/Persons/{Name}/RemoteImages", "GET")] - [Route("/Studios/{Name}/RemoteImages", "GET")] - [Api(Description = "Gets available remote images for an item")] - public class GetItemByNameRemoteImages : BaseRemoteImageRequest - { - /// - /// Gets or sets the id. - /// - /// The id. - [ApiMember(Name = "Name", Description = "Name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string Name { get; set; } - } - - [Route("/Items/{Id}/RemoteImages/Providers", "GET")] - [Api(Description = "Gets available remote image providers for an item")] + [Route("/Items/{Id}/RemoteImages/Providers", "GET", Summary = "Gets available remote image providers for an item")] + [Authenticated] public class GetRemoteImageProviders : IReturn> { /// @@ -86,23 +69,6 @@ namespace MediaBrowser.Api.Images public string Id { get; set; } } - [Route("/Artists/{Name}/RemoteImages/Providers", "GET")] - [Route("/Genres/{Name}/RemoteImages/Providers", "GET")] - [Route("/GameGenres/{Name}/RemoteImages/Providers", "GET")] - [Route("/MusicGenres/{Name}/RemoteImages/Providers", "GET")] - [Route("/Persons/{Name}/RemoteImages/Providers", "GET")] - [Route("/Studios/{Name}/RemoteImages/Providers", "GET")] - [Api(Description = "Gets available remote image providers for an item")] - public class GetItemByNameRemoteImageProviders : IReturn> - { - /// - /// Gets or sets the id. - /// - /// The id. - [ApiMember(Name = "Name", Description = "Name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string Name { get; set; } - } - public class BaseDownloadRemoteImage : IReturnVoid { [ApiMember(Name = "Type", Description = "The image type", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] @@ -115,8 +81,8 @@ namespace MediaBrowser.Api.Images public string ImageUrl { get; set; } } - [Route("/Items/{Id}/RemoteImages/Download", "POST")] - [Api(Description = "Downloads a remote image for an item")] + [Route("/Items/{Id}/RemoteImages/Download", "POST", Summary = "Downloads a remote image for an item")] + [Authenticated(Roles="Admin")] public class DownloadRemoteImage : BaseDownloadRemoteImage { /// @@ -127,25 +93,7 @@ namespace MediaBrowser.Api.Images public string Id { get; set; } } - [Route("/Artists/{Name}/RemoteImages/Download", "POST")] - [Route("/Genres/{Name}/RemoteImages/Download", "POST")] - [Route("/GameGenres/{Name}/RemoteImages/Download", "POST")] - [Route("/MusicGenres/{Name}/RemoteImages/Download", "POST")] - [Route("/Persons/{Name}/RemoteImages/Download", "POST")] - [Route("/Studios/{Name}/RemoteImages/Download", "POST")] - [Api(Description = "Downloads a remote image for an item")] - public class DownloadItemByNameRemoteImage : BaseDownloadRemoteImage - { - /// - /// Gets or sets the id. - /// - /// The id. - [ApiMember(Name = "Name", Description = "Name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string Name { get; set; } - } - - [Route("/Images/Remote", "GET")] - [Api(Description = "Gets a remote image")] + [Route("/Images/Remote", "GET", Summary = "Gets a remote image")] public class GetRemoteImage { [ApiMember(Name = "ImageUrl", Description = "The image url", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] @@ -182,18 +130,6 @@ namespace MediaBrowser.Api.Images return ToOptimizedSerializedResultUsingCache(result); } - public object Get(GetItemByNameRemoteImageProviders request) - { - var pathInfo = PathInfo.Parse(Request.PathInfo); - var type = pathInfo.GetArgumentValue(0); - - var item = GetItemByName(request.Name, type, _libraryManager); - - var result = GetImageProviders(item); - - return ToOptimizedSerializedResultUsingCache(result); - } - private List GetImageProviders(BaseItem item) { return _providerManager.GetRemoteImageProviderInfo(item).ToList(); @@ -206,16 +142,6 @@ namespace MediaBrowser.Api.Images return await GetRemoteImageResult(item, request).ConfigureAwait(false); } - public async Task Get(GetItemByNameRemoteImages request) - { - var pathInfo = PathInfo.Parse(Request.PathInfo); - var type = pathInfo.GetArgumentValue(0); - - var item = GetItemByName(request.Name, type, _libraryManager); - - return await GetRemoteImageResult(item, request).ConfigureAwait(false); - } - private async Task GetRemoteImageResult(BaseItem item, BaseRemoteImageRequest request) { var images = await _providerManager.GetAvailableRemoteImages(item, new RemoteImageQuery @@ -274,18 +200,6 @@ namespace MediaBrowser.Api.Images Task.WaitAll(task); } - public void Post(DownloadItemByNameRemoteImage request) - { - var pathInfo = PathInfo.Parse(Request.PathInfo); - var type = pathInfo.GetArgumentValue(0); - - var item = GetItemByName(request.Name, type, _libraryManager); - - var task = DownloadRemoteImage(item, request); - - Task.WaitAll(task); - } - /// /// Downloads the remote image. /// diff --git a/MediaBrowser.Api/ItemLookupService.cs b/MediaBrowser.Api/ItemLookupService.cs index 846d718021..b19d6c6549 100644 --- a/MediaBrowser.Api/ItemLookupService.cs +++ b/MediaBrowser.Api/ItemLookupService.cs @@ -8,7 +8,6 @@ using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.Entities; using MediaBrowser.Model.Providers; using ServiceStack; using System; @@ -20,9 +19,8 @@ using System.Threading.Tasks; namespace MediaBrowser.Api { - [Route("/Items/{Id}/ExternalIdInfos", "GET")] - [Api(Description = "Gets external id infos for an item")] - [Authenticated] + [Route("/Items/{Id}/ExternalIdInfos", "GET", Summary = "Gets external id infos for an item")] + [Authenticated(Roles = "Admin")] public class GetExternalIdInfos : IReturn> { /// @@ -34,70 +32,60 @@ namespace MediaBrowser.Api } [Route("/Items/RemoteSearch/Movie", "POST")] - [Api(Description = "Gets external id infos for an item")] [Authenticated] public class GetMovieRemoteSearchResults : RemoteSearchQuery, IReturn> { } [Route("/Items/RemoteSearch/Trailer", "POST")] - [Api(Description = "Gets external id infos for an item")] [Authenticated] public class GetTrailerRemoteSearchResults : RemoteSearchQuery, IReturn> { } [Route("/Items/RemoteSearch/AdultVideo", "POST")] - [Api(Description = "Gets external id infos for an item")] [Authenticated] public class GetAdultVideoRemoteSearchResults : RemoteSearchQuery, IReturn> { } [Route("/Items/RemoteSearch/Series", "POST")] - [Api(Description = "Gets external id infos for an item")] [Authenticated] public class GetSeriesRemoteSearchResults : RemoteSearchQuery, IReturn> { } [Route("/Items/RemoteSearch/Game", "POST")] - [Api(Description = "Gets external id infos for an item")] [Authenticated] public class GetGameRemoteSearchResults : RemoteSearchQuery, IReturn> { } [Route("/Items/RemoteSearch/BoxSet", "POST")] - [Api(Description = "Gets external id infos for an item")] [Authenticated] public class GetBoxSetRemoteSearchResults : RemoteSearchQuery, IReturn> { } [Route("/Items/RemoteSearch/MusicArtist", "POST")] - [Api(Description = "Gets external id infos for an item")] [Authenticated] public class GetMusicArtistRemoteSearchResults : RemoteSearchQuery, IReturn> { } [Route("/Items/RemoteSearch/MusicAlbum", "POST")] - [Api(Description = "Gets external id infos for an item")] [Authenticated] public class GetMusicAlbumRemoteSearchResults : RemoteSearchQuery, IReturn> { } [Route("/Items/RemoteSearch/Person", "POST")] - [Api(Description = "Gets external id infos for an item")] - [Authenticated] + [Authenticated(Roles = "Admin")] public class GetPersonRemoteSearchResults : RemoteSearchQuery, IReturn> { } - [Route("/Items/RemoteSearch/Image", "GET")] - [Api(Description = "Gets a remote image")] + [Route("/Items/RemoteSearch/Image", "GET", Summary = "Gets a remote image")] public class GetRemoteSearchImage { [ApiMember(Name = "ImageUrl", Description = "The image url", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] @@ -107,9 +95,8 @@ namespace MediaBrowser.Api public string ProviderName { get; set; } } - [Route("/Items/RemoteSearch/Apply/{Id}", "POST")] - [Api(Description = "Applies search criteria to an item and refreshes metadata")] - [Authenticated] + [Route("/Items/RemoteSearch/Apply/{Id}", "POST", Summary = "Applies search criteria to an item and refreshes metadata")] + [Authenticated(Roles = "Admin")] public class ApplySearchCriteria : RemoteSearchResult, IReturnVoid { [ApiMember(Name = "Id", Description = "The item id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] diff --git a/MediaBrowser.Api/ItemRefreshService.cs b/MediaBrowser.Api/ItemRefreshService.cs index 34e0726f19..152a64c245 100644 --- a/MediaBrowser.Api/ItemRefreshService.cs +++ b/MediaBrowser.Api/ItemRefreshService.cs @@ -26,8 +26,7 @@ namespace MediaBrowser.Api public bool ReplaceAllImages { get; set; } } - [Route("/Items/{Id}/Refresh", "POST")] - [Api(Description = "Refreshes metadata for an item")] + [Route("/Items/{Id}/Refresh", "POST", Summary = "Refreshes metadata for an item")] public class RefreshItem : BaseRefreshRequest { [ApiMember(Name = "Recursive", Description = "Indicates if the refresh should occur recursively.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "POST")] diff --git a/MediaBrowser.Api/Library/FileOrganizationService.cs b/MediaBrowser.Api/Library/FileOrganizationService.cs index adf6a522b3..29a9826295 100644 --- a/MediaBrowser.Api/Library/FileOrganizationService.cs +++ b/MediaBrowser.Api/Library/FileOrganizationService.cs @@ -7,8 +7,7 @@ using System.Threading.Tasks; namespace MediaBrowser.Api.Library { - [Route("/Library/FileOrganization", "GET")] - [Api(Description = "Gets file organization results")] + [Route("/Library/FileOrganization", "GET", Summary = "Gets file organization results")] public class GetFileOrganizationActivity : IReturn> { /// @@ -26,14 +25,12 @@ namespace MediaBrowser.Api.Library public int? Limit { get; set; } } - [Route("/Library/FileOrganizations", "DELETE")] - [Api(Description = "Clears the activity log")] + [Route("/Library/FileOrganizations", "DELETE", Summary = "Clears the activity log")] public class ClearOrganizationLog : IReturnVoid { } - [Route("/Library/FileOrganizations/{Id}/File", "DELETE")] - [Api(Description = "Deletes the original file of a organizer result")] + [Route("/Library/FileOrganizations/{Id}/File", "DELETE", Summary = "Deletes the original file of a organizer result")] public class DeleteOriginalFile : IReturnVoid { /// @@ -44,8 +41,7 @@ namespace MediaBrowser.Api.Library public string Id { get; set; } } - [Route("/Library/FileOrganizations/{Id}/Organize", "POST")] - [Api(Description = "Performs an organization")] + [Route("/Library/FileOrganizations/{Id}/Organize", "POST", Summary = "Performs an organization")] public class PerformOrganization : IReturn> { /// @@ -56,8 +52,7 @@ namespace MediaBrowser.Api.Library public string Id { get; set; } } - [Route("/Library/FileOrganizations/{Id}/Episode/Organize", "POST")] - [Api(Description = "Performs an organization")] + [Route("/Library/FileOrganizations/{Id}/Episode/Organize", "POST", Summary = "Performs an organization")] public class OrganizeEpisode { [ApiMember(Name = "Id", Description = "Result Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] @@ -79,7 +74,7 @@ namespace MediaBrowser.Api.Library public bool RememberCorrection { get; set; } } - [Authenticated] + [Authenticated(Roles = "Admin")] public class FileOrganizationService : BaseApiService { private readonly IFileOrganizationService _iFileOrganizationService; diff --git a/MediaBrowser.Api/Library/LibraryService.cs b/MediaBrowser.Api/Library/LibraryService.cs index 231f5e9a6c..ec7b1f0b35 100644 --- a/MediaBrowser.Api/Library/LibraryService.cs +++ b/MediaBrowser.Api/Library/LibraryService.cs @@ -23,8 +23,8 @@ using System.Threading.Tasks; namespace MediaBrowser.Api.Library { - [Route("/Items/{Id}/File", "GET")] - [Api(Description = "Gets the original file of an item")] + [Route("/Items/{Id}/File", "GET", Summary = "Gets the original file of an item")] + [Authenticated] public class GetFile { /// @@ -38,8 +38,8 @@ namespace MediaBrowser.Api.Library /// /// Class GetCriticReviews /// - [Route("/Items/{Id}/CriticReviews", "GET")] - [Api(Description = "Gets critic reviews for an item")] + [Route("/Items/{Id}/CriticReviews", "GET", Summary = "Gets critic reviews for an item")] + [Authenticated] public class GetCriticReviews : IReturn> { /// @@ -67,8 +67,8 @@ namespace MediaBrowser.Api.Library /// /// Class GetThemeSongs /// - [Route("/Items/{Id}/ThemeSongs", "GET")] - [Api(Description = "Gets theme songs for an item")] + [Route("/Items/{Id}/ThemeSongs", "GET", Summary = "Gets theme songs for an item")] + [Authenticated] public class GetThemeSongs : IReturn { /// @@ -92,8 +92,8 @@ namespace MediaBrowser.Api.Library /// /// Class GetThemeVideos /// - [Route("/Items/{Id}/ThemeVideos", "GET")] - [Api(Description = "Gets theme videos for an item")] + [Route("/Items/{Id}/ThemeVideos", "GET", Summary = "Gets theme videos for an item")] + [Authenticated] public class GetThemeVideos : IReturn { /// @@ -117,8 +117,8 @@ namespace MediaBrowser.Api.Library /// /// Class GetThemeVideos /// - [Route("/Items/{Id}/ThemeMedia", "GET")] - [Api(Description = "Gets theme videos and songs for an item")] + [Route("/Items/{Id}/ThemeMedia", "GET", Summary = "Gets theme videos and songs for an item")] + [Authenticated] public class GetThemeMedia : IReturn { /// @@ -139,14 +139,14 @@ namespace MediaBrowser.Api.Library public bool InheritFromParent { get; set; } } - [Route("/Library/Refresh", "POST")] - [Api(Description = "Starts a library scan")] + [Route("/Library/Refresh", "POST", Summary = "Starts a library scan")] + [Authenticated(Roles = "Admin")] public class RefreshLibrary : IReturnVoid { } - [Route("/Items/{Id}", "DELETE")] - [Api(Description = "Deletes an item from the library and file system")] + [Route("/Items/{Id}", "DELETE", Summary = "Deletes an item from the library and file system")] + [Authenticated(Roles = "Delete")] public class DeleteItem : IReturnVoid { [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")] @@ -154,7 +154,7 @@ namespace MediaBrowser.Api.Library } [Route("/Items/Counts", "GET")] - [Api(Description = "Gets counts of various item types")] + [Authenticated] public class GetItemCounts : IReturn { [ApiMember(Name = "UserId", Description = "Optional. Get counts from a specific user's library.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] @@ -164,8 +164,8 @@ namespace MediaBrowser.Api.Library public bool? IsFavorite { get; set; } } - [Route("/Items/{Id}/Ancestors", "GET")] - [Api(Description = "Gets all parents of an item")] + [Route("/Items/{Id}/Ancestors", "GET", Summary = "Gets all parents of an item")] + [Authenticated] public class GetAncestors : IReturn { /// @@ -183,8 +183,8 @@ namespace MediaBrowser.Api.Library public string Id { get; set; } } - [Route("/Items/YearIndex", "GET")] - [Api(Description = "Gets a year index based on an item query.")] + [Route("/Items/YearIndex", "GET", Summary = "Gets a year index based on an item query.")] + [Authenticated] public class GetYearIndex : IReturn> { /// @@ -201,23 +201,23 @@ namespace MediaBrowser.Api.Library /// /// Class GetPhyscialPaths /// - [Route("/Library/PhysicalPaths", "GET")] - [Api(Description = "Gets a list of physical paths from virtual folders")] + [Route("/Library/PhysicalPaths", "GET", Summary = "Gets a list of physical paths from virtual folders")] + [Authenticated(Roles = "Admin")] public class GetPhyscialPaths : IReturn> { } - [Route("/Library/MediaFolders", "GET")] - [Api(Description = "Gets all user media folders.")] + [Route("/Library/MediaFolders", "GET", Summary = "Gets all user media folders.")] + [Authenticated] public class GetMediaFolders : IReturn { [ApiMember(Name = "IsHidden", Description = "Optional. Filter by folders that are marked hidden, or not.", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")] public bool? IsHidden { get; set; } } - [Route("/Library/Series/Added", "POST")] - [Route("/Library/Series/Updated", "POST")] - [Api(Description = "Reports that new episodes of a series have been added by an external source")] + [Route("/Library/Series/Added", "POST", Summary = "Reports that new episodes of a series have been added by an external source")] + [Route("/Library/Series/Updated", "POST", Summary = "Reports that new episodes of a series have been added by an external source")] + [Authenticated] public class PostUpdatedSeries : IReturnVoid { [ApiMember(Name = "TvdbId", Description = "Tvdb Id", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")] @@ -227,7 +227,6 @@ namespace MediaBrowser.Api.Library /// /// Class LibraryService /// - [Authenticated] public class LibraryService : BaseApiService { /// diff --git a/MediaBrowser.Api/Library/LibraryStructureService.cs b/MediaBrowser.Api/Library/LibraryStructureService.cs index f56daca1e7..ee80d4b734 100644 --- a/MediaBrowser.Api/Library/LibraryStructureService.cs +++ b/MediaBrowser.Api/Library/LibraryStructureService.cs @@ -131,11 +131,11 @@ namespace MediaBrowser.Api.Library /// true if [refresh library]; otherwise, false. public bool RefreshLibrary { get; set; } } - + /// /// Class LibraryStructureService /// - [Authenticated] + [Authenticated(Roles = "Admin", AllowBeforeStartupWizard = true)] public class LibraryStructureService : BaseApiService { /// @@ -164,7 +164,6 @@ namespace MediaBrowser.Api.Library /// The app paths. /// The user manager. /// The library manager. - /// appPaths public LibraryStructureService(IServerApplicationPaths appPaths, IUserManager userManager, ILibraryManager libraryManager, ILibraryMonitor libraryMonitor, IFileSystem fileSystem, ILogger logger) { if (appPaths == null) diff --git a/MediaBrowser.Api/LiveTv/LiveTvService.cs b/MediaBrowser.Api/LiveTv/LiveTvService.cs index 906c0d830a..3afe72866d 100644 --- a/MediaBrowser.Api/LiveTv/LiveTvService.cs +++ b/MediaBrowser.Api/LiveTv/LiveTvService.cs @@ -18,6 +18,7 @@ namespace MediaBrowser.Api.LiveTv /// This is insecure right now to avoid windows phone refactoring /// [Route("/LiveTv/Info", "GET", Summary = "Gets available live tv services.")] + [Authenticated] public class GetLiveTvInfo : IReturn { } diff --git a/MediaBrowser.Api/LocalizationService.cs b/MediaBrowser.Api/LocalizationService.cs index 5696d8df75..a8bb61b737 100644 --- a/MediaBrowser.Api/LocalizationService.cs +++ b/MediaBrowser.Api/LocalizationService.cs @@ -43,7 +43,7 @@ namespace MediaBrowser.Api /// /// Class CulturesService /// - [Authenticated] + [Authenticated(AllowBeforeStartupWizard = true)] public class LocalizationService : BaseApiService { /// diff --git a/MediaBrowser.Api/MediaBrowser.Api.csproj b/MediaBrowser.Api/MediaBrowser.Api.csproj index 5a6e6f5450..bc6cba5b9f 100644 --- a/MediaBrowser.Api/MediaBrowser.Api.csproj +++ b/MediaBrowser.Api/MediaBrowser.Api.csproj @@ -69,6 +69,7 @@ Properties\SharedVersion.cs + @@ -79,10 +80,10 @@ + - diff --git a/MediaBrowser.Api/PackageService.cs b/MediaBrowser.Api/PackageService.cs index 84b42baa38..eebdafc5c8 100644 --- a/MediaBrowser.Api/PackageService.cs +++ b/MediaBrowser.Api/PackageService.cs @@ -122,7 +122,7 @@ namespace MediaBrowser.Api /// /// Class PackageService /// - [Authenticated] + [Authenticated(Roles = "Admin")] public class PackageService : BaseApiService { private readonly IInstallationManager _installationManager; diff --git a/MediaBrowser.Api/PlaylistService.cs b/MediaBrowser.Api/PlaylistService.cs index 1318b322ac..5325e9c448 100644 --- a/MediaBrowser.Api/PlaylistService.cs +++ b/MediaBrowser.Api/PlaylistService.cs @@ -6,7 +6,6 @@ using MediaBrowser.Model.Dto; using MediaBrowser.Model.Playlists; using MediaBrowser.Model.Querying; using ServiceStack; -using System; using System.Linq; using System.Threading.Tasks; diff --git a/MediaBrowser.Api/PluginService.cs b/MediaBrowser.Api/PluginService.cs index d6c854734a..b4e12184aa 100644 --- a/MediaBrowser.Api/PluginService.cs +++ b/MediaBrowser.Api/PluginService.cs @@ -20,6 +20,7 @@ namespace MediaBrowser.Api /// Class Plugins /// [Route("/Plugins", "GET", Summary = "Gets a list of currently installed plugins")] + [Authenticated] public class GetPlugins : IReturn> { } @@ -28,6 +29,7 @@ namespace MediaBrowser.Api /// Class UninstallPlugin /// [Route("/Plugins/{Id}", "DELETE", Summary = "Uninstalls a plugin")] + [Authenticated(Roles = "Admin")] public class UninstallPlugin : IReturnVoid { /// @@ -42,6 +44,7 @@ namespace MediaBrowser.Api /// Class GetPluginConfiguration /// [Route("/Plugins/{Id}/Configuration", "GET", Summary = "Gets a plugin's configuration")] + [Authenticated] public class GetPluginConfiguration { /// @@ -56,6 +59,7 @@ namespace MediaBrowser.Api /// Class UpdatePluginConfiguration /// [Route("/Plugins/{Id}/Configuration", "POST", Summary = "Updates a plugin's configuration")] + [Authenticated] public class UpdatePluginConfiguration : IRequiresRequestStream, IReturnVoid { /// @@ -76,6 +80,7 @@ namespace MediaBrowser.Api /// Class GetPluginSecurityInfo /// [Route("/Plugins/SecurityInfo", "GET", Summary = "Gets plugin registration information")] + [Authenticated] public class GetPluginSecurityInfo : IReturn { } @@ -84,11 +89,13 @@ namespace MediaBrowser.Api /// Class UpdatePluginSecurityInfo /// [Route("/Plugins/SecurityInfo", "POST", Summary = "Updates plugin registration information")] + [Authenticated(Roles = "Admin")] public class UpdatePluginSecurityInfo : PluginSecurityInfo, IReturnVoid { } [Route("/Plugins/RegistrationRecords/{Name}", "GET", Summary = "Gets registration status for a feature")] + [Authenticated] public class GetRegistrationStatus { [ApiMember(Name = "Name", Description = "Feature Name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] @@ -97,11 +104,10 @@ namespace MediaBrowser.Api [ApiMember(Name = "Mb2Equivalent", Description = "Optional. The equivalent feature name in MB2", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] public string Mb2Equivalent { get; set; } } - + /// /// Class PluginsService /// - [Authenticated] public class PluginService : BaseApiService { /// @@ -118,14 +124,6 @@ namespace MediaBrowser.Api private readonly IInstallationManager _installationManager; - /// - /// Initializes a new instance of the class. - /// - /// The json serializer. - /// The app host. - /// The security manager. - /// The installation manager. - /// jsonSerializer public PluginService(IJsonSerializer jsonSerializer, IApplicationHost appHost, ISecurityManager securityManager, IInstallationManager installationManager) : base() { @@ -151,7 +149,7 @@ namespace MediaBrowser.Api return ToOptimizedResult(result); } - + /// /// Gets the specified request. /// diff --git a/MediaBrowser.Api/ScheduledTasks/ScheduledTaskService.cs b/MediaBrowser.Api/ScheduledTasks/ScheduledTaskService.cs index 483133c102..a05be024e0 100644 --- a/MediaBrowser.Api/ScheduledTasks/ScheduledTaskService.cs +++ b/MediaBrowser.Api/ScheduledTasks/ScheduledTaskService.cs @@ -79,7 +79,7 @@ namespace MediaBrowser.Api.ScheduledTasks /// /// Class ScheduledTasksService /// - [Authenticated] + [Authenticated(Roles = "Admin")] public class ScheduledTaskService : BaseApiService { /// diff --git a/MediaBrowser.Api/Session/SessionsService.cs b/MediaBrowser.Api/Session/SessionsService.cs index 74dccb7afd..4d68cbf393 100644 --- a/MediaBrowser.Api/Session/SessionsService.cs +++ b/MediaBrowser.Api/Session/SessionsService.cs @@ -241,16 +241,19 @@ namespace MediaBrowser.Api.Session } [Route("/Sessions/Logout", "POST", Summary = "Reports that a session has ended")] + [Authenticated] public class ReportSessionEnded : IReturnVoid { } [Route("/Auth/Keys", "GET")] + [Authenticated(Roles = "Admin")] public class GetApiKeys { } [Route("/Auth/Keys/{Key}", "DELETE")] + [Authenticated(Roles = "Admin")] public class RevokeKey { [ApiMember(Name = "Key", Description = "Auth Key", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] @@ -258,6 +261,7 @@ namespace MediaBrowser.Api.Session } [Route("/Auth/Keys", "POST")] + [Authenticated(Roles = "Admin")] public class CreateKey { [ApiMember(Name = "App", Description = "App", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")] diff --git a/MediaBrowser.Api/StartupWizardService.cs b/MediaBrowser.Api/StartupWizardService.cs new file mode 100644 index 0000000000..fd50191264 --- /dev/null +++ b/MediaBrowser.Api/StartupWizardService.cs @@ -0,0 +1,163 @@ +using MediaBrowser.Common.Configuration; +using MediaBrowser.Controller; +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Connect; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Net; +using MediaBrowser.Model.Configuration; +using ServiceStack; +using System; +using System.Linq; +using System.Threading.Tasks; + +namespace MediaBrowser.Api +{ + [Route("/Startup/Complete", "POST", Summary = "Reports that the startup wizard has been completed")] + public class ReportStartupWizardComplete : IReturnVoid + { + } + + [Route("/Startup/Info", "GET", Summary = "Gets initial server info")] + public class GetStartupInfo : IReturn + { + } + + [Route("/Startup/Configuration", "GET", Summary = "Gets initial server configuration")] + public class GetStartupConfiguration : IReturn + { + } + + [Route("/Startup/Configuration", "POST", Summary = "Updates initial server configuration")] + public class UpdateStartupConfiguration : StartupConfiguration, IReturnVoid + { + } + + [Route("/Startup/User", "GET", Summary = "Gets initial user info")] + public class GetStartupUser : IReturn + { + } + + [Route("/Startup/User", "POST", Summary = "Updates initial user info")] + public class UpdateStartupUser : StartupUser, IReturn + { + } + + [Authenticated(AllowBeforeStartupWizard = true, Roles = "Admin")] + public class StartupWizardService : BaseApiService + { + private readonly IServerConfigurationManager _config; + private readonly IServerApplicationHost _appHost; + private readonly IUserManager _userManager; + private readonly IConnectManager _connectManager; + + public StartupWizardService(IServerConfigurationManager config, IServerApplicationHost appHost, IUserManager userManager, IConnectManager connectManager) + { + _config = config; + _appHost = appHost; + _userManager = userManager; + _connectManager = connectManager; + } + + public void Post(ReportStartupWizardComplete request) + { + _config.Configuration.IsStartupWizardCompleted = true; + _config.SaveConfiguration(); + } + + public object Get(GetStartupInfo request) + { + var info = _appHost.GetSystemInfo(); + + return new StartupInfo + { + SupportsRunningAsService = info.SupportsRunningAsService + }; + } + + public object Get(GetStartupConfiguration request) + { + return new StartupConfiguration + { + UICulture = _config.Configuration.UICulture, + EnableInternetProviders = _config.Configuration.EnableInternetProviders, + SaveLocalMeta = _config.Configuration.SaveLocalMeta, + MetadataCountryCode = _config.Configuration.MetadataCountryCode, + PreferredMetadataLanguage = _config.Configuration.PreferredMetadataLanguage + }; + } + + public void Post(UpdateStartupConfiguration request) + { + _config.Configuration.UICulture = request.UICulture; + _config.Configuration.EnableInternetProviders = request.EnableInternetProviders; + _config.Configuration.SaveLocalMeta = request.SaveLocalMeta; + _config.Configuration.MetadataCountryCode = request.MetadataCountryCode; + _config.Configuration.PreferredMetadataLanguage = request.PreferredMetadataLanguage; + _config.SaveConfiguration(); + } + + public object Get(GetStartupUser request) + { + var user = _userManager.Users.First(); + + return new StartupUser + { + Name = user.Name, + ConnectUserName = user.ConnectUserName + }; + } + + public async Task Post(UpdateStartupUser request) + { + var user = _userManager.Users.First(); + + // TODO: This should be handled internally by xbmc metadata + const string metadataKey = "xbmcmetadata"; + var metadata = _config.GetConfiguration(metadataKey); + metadata.UserId = user.Id.ToString("N"); + _config.SaveConfiguration(metadataKey, metadata); + + user.Name = request.Name; + await _userManager.UpdateUser(user).ConfigureAwait(false); + + var result = new UpdateStartupUserResult(); + + if (!string.IsNullOrWhiteSpace(user.ConnectUserName) && + string.IsNullOrWhiteSpace(request.ConnectUserName)) + { + await _connectManager.RemoveConnect(user.Id.ToString("N")).ConfigureAwait(false); + } + else if (!string.Equals(user.ConnectUserName, request.ConnectUserName, StringComparison.OrdinalIgnoreCase)) + { + result.UserLinkResult = await _connectManager.LinkUser(user.Id.ToString("N"), request.ConnectUserName).ConfigureAwait(false); + } + + return result; + } + } + + public class StartupConfiguration + { + public string UICulture { get; set; } + public bool EnableInternetProviders { get; set; } + public bool SaveLocalMeta { get; set; } + public string MetadataCountryCode { get; set; } + public string PreferredMetadataLanguage { get; set; } + } + + public class StartupInfo + { + public bool SupportsRunningAsService { get; set; } + } + + public class StartupUser + { + public string Name { get; set; } + public string ConnectUserName { get; set; } + } + + public class UpdateStartupUserResult + { + public UserLinkResult UserLinkResult { get; set; } + } +} diff --git a/MediaBrowser.Api/Subtitles/SubtitleService.cs b/MediaBrowser.Api/Subtitles/SubtitleService.cs index 1d62374bd3..a2a93d50e3 100644 --- a/MediaBrowser.Api/Subtitles/SubtitleService.cs +++ b/MediaBrowser.Api/Subtitles/SubtitleService.cs @@ -19,7 +19,7 @@ using System.Threading.Tasks; namespace MediaBrowser.Api.Subtitles { [Route("/Videos/{Id}/Subtitles/{Index}", "DELETE", Summary = "Deletes an external subtitle file")] - [Authenticated] + [Authenticated(Roles = "Admin")] public class DeleteSubtitle { /// diff --git a/MediaBrowser.Api/System/ActivityLogService.cs b/MediaBrowser.Api/System/ActivityLogService.cs index 8dc3079c72..d24bc99cb6 100644 --- a/MediaBrowser.Api/System/ActivityLogService.cs +++ b/MediaBrowser.Api/System/ActivityLogService.cs @@ -29,7 +29,7 @@ namespace MediaBrowser.Api.System public string MinDate { get; set; } } - [Authenticated] + [Authenticated(Roles = "Admin")] public class ActivityLogService : BaseApiService { private readonly IActivityManager _activityManager; diff --git a/MediaBrowser.Api/System/SystemService.cs b/MediaBrowser.Api/System/SystemService.cs index 3b8eb7b6f4..0a49cf47cf 100644 --- a/MediaBrowser.Api/System/SystemService.cs +++ b/MediaBrowser.Api/System/SystemService.cs @@ -35,7 +35,7 @@ namespace MediaBrowser.Api.System /// Class RestartApplication /// [Route("/System/Restart", "POST", Summary = "Restarts the application, if needed")] - [Authenticated] + [Authenticated(Roles = "Admin")] public class RestartApplication { } @@ -51,7 +51,7 @@ namespace MediaBrowser.Api.System } [Route("/System/Logs", "GET", Summary = "Gets a list of available server log files")] - [Authenticated] + [Authenticated(Roles = "Admin")] public class GetServerLogs : IReturn> { } @@ -64,6 +64,7 @@ namespace MediaBrowser.Api.System } [Route("/System/Logs/Log", "GET", Summary = "Gets a log file")] + [Authenticated(Roles = "Admin")] public class GetLogFile { [ApiMember(Name = "Name", Description = "The log file name.", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] diff --git a/MediaBrowser.Api/TvShowsService.cs b/MediaBrowser.Api/TvShowsService.cs index f4ea7ef94a..2f9bbca476 100644 --- a/MediaBrowser.Api/TvShowsService.cs +++ b/MediaBrowser.Api/TvShowsService.cs @@ -1,5 +1,4 @@ -using MediaBrowser.Api.UserLibrary; -using MediaBrowser.Common.Extensions; +using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.TV; diff --git a/MediaBrowser.Api/UserLibrary/GameGenresService.cs b/MediaBrowser.Api/UserLibrary/GameGenresService.cs index 49e979e551..adc21c362a 100644 --- a/MediaBrowser.Api/UserLibrary/GameGenresService.cs +++ b/MediaBrowser.Api/UserLibrary/GameGenresService.cs @@ -13,8 +13,7 @@ using System.Linq; namespace MediaBrowser.Api.UserLibrary { - [Route("/GameGenres", "GET")] - [Api(Description = "Gets all Game genres from a given item, folder, or the entire library")] + [Route("/GameGenres", "GET", Summary = "Gets all Game genres from a given item, folder, or the entire library")] public class GetGameGenres : GetItemsByName { public GetGameGenres() @@ -23,8 +22,7 @@ namespace MediaBrowser.Api.UserLibrary } } - [Route("/GameGenres/{Name}", "GET")] - [Api(Description = "Gets a Game genre, by name")] + [Route("/GameGenres/{Name}", "GET", Summary = "Gets a Game genre, by name")] public class GetGameGenre : IReturn { /// diff --git a/MediaBrowser.Api/UserLibrary/GenresService.cs b/MediaBrowser.Api/UserLibrary/GenresService.cs index c8c45c1bb3..7b6b18c9ca 100644 --- a/MediaBrowser.Api/UserLibrary/GenresService.cs +++ b/MediaBrowser.Api/UserLibrary/GenresService.cs @@ -16,8 +16,7 @@ namespace MediaBrowser.Api.UserLibrary /// /// Class GetGenres /// - [Route("/Genres", "GET")] - [Api(Description = "Gets all genres from a given item, folder, or the entire library")] + [Route("/Genres", "GET", Summary = "Gets all genres from a given item, folder, or the entire library")] public class GetGenres : GetItemsByName { } @@ -25,8 +24,7 @@ namespace MediaBrowser.Api.UserLibrary /// /// Class GetGenre /// - [Route("/Genres/{Name}", "GET")] - [Api(Description = "Gets a genre, by name")] + [Route("/Genres/{Name}", "GET", Summary = "Gets a genre, by name")] public class GetGenre : IReturn { /// diff --git a/MediaBrowser.Api/UserLibrary/MusicGenresService.cs b/MediaBrowser.Api/UserLibrary/MusicGenresService.cs index f50f0a0bd4..1088ada023 100644 --- a/MediaBrowser.Api/UserLibrary/MusicGenresService.cs +++ b/MediaBrowser.Api/UserLibrary/MusicGenresService.cs @@ -13,8 +13,7 @@ using System.Linq; namespace MediaBrowser.Api.UserLibrary { - [Route("/MusicGenres", "GET")] - [Api(Description = "Gets all music genres from a given item, folder, or the entire library")] + [Route("/MusicGenres", "GET", Summary = "Gets all music genres from a given item, folder, or the entire library")] public class GetMusicGenres : GetItemsByName { public GetMusicGenres() @@ -23,8 +22,7 @@ namespace MediaBrowser.Api.UserLibrary } } - [Route("/MusicGenres/{Name}", "GET")] - [Api(Description = "Gets a music genre, by name")] + [Route("/MusicGenres/{Name}", "GET", Summary = "Gets a music genre, by name")] public class GetMusicGenre : IReturn { /// diff --git a/MediaBrowser.Api/UserLibrary/PersonsService.cs b/MediaBrowser.Api/UserLibrary/PersonsService.cs index 5651f2c73f..dd80801eea 100644 --- a/MediaBrowser.Api/UserLibrary/PersonsService.cs +++ b/MediaBrowser.Api/UserLibrary/PersonsService.cs @@ -15,8 +15,7 @@ namespace MediaBrowser.Api.UserLibrary /// /// Class GetPersons /// - [Route("/Persons", "GET")] - [Api(Description = "Gets all persons from a given item, folder, or the entire library")] + [Route("/Persons", "GET", Summary = "Gets all persons from a given item, folder, or the entire library")] public class GetPersons : GetItemsByName { /// @@ -30,8 +29,7 @@ namespace MediaBrowser.Api.UserLibrary /// /// Class GetPerson /// - [Route("/Persons/{Name}", "GET")] - [Api(Description = "Gets a person, by name")] + [Route("/Persons/{Name}", "GET", Summary = "Gets a person, by name")] public class GetPerson : IReturn { /// diff --git a/MediaBrowser.Api/UserLibrary/PlaystateService.cs b/MediaBrowser.Api/UserLibrary/PlaystateService.cs index 932ce77d3a..fae83e3692 100644 --- a/MediaBrowser.Api/UserLibrary/PlaystateService.cs +++ b/MediaBrowser.Api/UserLibrary/PlaystateService.cs @@ -15,8 +15,7 @@ namespace MediaBrowser.Api.UserLibrary /// /// Class MarkPlayedItem /// - [Route("/Users/{UserId}/PlayedItems/{Id}", "POST")] - [Api(Description = "Marks an item as played")] + [Route("/Users/{UserId}/PlayedItems/{Id}", "POST", Summary = "Marks an item as played")] public class MarkPlayedItem : IReturn { /// @@ -40,8 +39,7 @@ namespace MediaBrowser.Api.UserLibrary /// /// Class MarkUnplayedItem /// - [Route("/Users/{UserId}/PlayedItems/{Id}", "DELETE")] - [Api(Description = "Marks an item as unplayed")] + [Route("/Users/{UserId}/PlayedItems/{Id}", "DELETE", Summary = "Marks an item as unplayed")] public class MarkUnplayedItem : IReturn { /// @@ -59,20 +57,17 @@ namespace MediaBrowser.Api.UserLibrary public string Id { get; set; } } - [Route("/Sessions/Playing", "POST")] - [Api(Description = "Reports playback has started within a session")] + [Route("/Sessions/Playing", "POST", Summary = "Reports playback has started within a session")] public class ReportPlaybackStart : PlaybackStartInfo, IReturnVoid { } - [Route("/Sessions/Playing/Progress", "POST")] - [Api(Description = "Reports playback progress within a session")] + [Route("/Sessions/Playing/Progress", "POST", Summary = "Reports playback progress within a session")] public class ReportPlaybackProgress : PlaybackProgressInfo, IReturnVoid { } - [Route("/Sessions/Playing/Stopped", "POST")] - [Api(Description = "Reports playback has stopped within a session")] + [Route("/Sessions/Playing/Stopped", "POST", Summary = "Reports playback has stopped within a session")] public class ReportPlaybackStopped : PlaybackStopInfo, IReturnVoid { } @@ -80,8 +75,7 @@ namespace MediaBrowser.Api.UserLibrary /// /// Class OnPlaybackStart /// - [Route("/Users/{UserId}/PlayingItems/{Id}", "POST")] - [Api(Description = "Reports that a user has begun playing an item")] + [Route("/Users/{UserId}/PlayingItems/{Id}", "POST", Summary = "Reports that a user has begun playing an item")] public class OnPlaybackStart : IReturnVoid { /// @@ -125,8 +119,7 @@ namespace MediaBrowser.Api.UserLibrary /// /// Class OnPlaybackProgress /// - [Route("/Users/{UserId}/PlayingItems/{Id}/Progress", "POST")] - [Api(Description = "Reports a user's playback progress")] + [Route("/Users/{UserId}/PlayingItems/{Id}/Progress", "POST", Summary = "Reports a user's playback progress")] public class OnPlaybackProgress : IReturnVoid { /// @@ -172,8 +165,7 @@ namespace MediaBrowser.Api.UserLibrary /// /// Class OnPlaybackStopped /// - [Route("/Users/{UserId}/PlayingItems/{Id}", "DELETE")] - [Api(Description = "Reports that a user has stopped playing an item")] + [Route("/Users/{UserId}/PlayingItems/{Id}", "DELETE", Summary = "Reports that a user has stopped playing an item")] public class OnPlaybackStopped : IReturnVoid { /// diff --git a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs index 4722df6415..fd0e79a217 100644 --- a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs +++ b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs @@ -1,6 +1,5 @@ using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Net; @@ -21,8 +20,7 @@ namespace MediaBrowser.Api.UserLibrary /// /// Class GetItem /// - [Route("/Users/{UserId}/Items/{Id}", "GET")] - [Api(Description = "Gets an item from a user's library")] + [Route("/Users/{UserId}/Items/{Id}", "GET", Summary = "Gets an item from a user's library")] public class GetItem : IReturn { /// @@ -57,8 +55,7 @@ namespace MediaBrowser.Api.UserLibrary /// /// Class GetItem /// - [Route("/Users/{UserId}/Items/Root", "GET")] - [Api(Description = "Gets the root folder from a user's library")] + [Route("/Users/{UserId}/Items/Root", "GET", Summary = "Gets the root folder from a user's library")] public class GetRootFolder : IReturn { /// @@ -72,8 +69,7 @@ namespace MediaBrowser.Api.UserLibrary /// /// Class GetIntros /// - [Route("/Users/{UserId}/Items/{Id}/Intros", "GET")] - [Api(("Gets intros to play before the main media item plays"))] + [Route("/Users/{UserId}/Items/{Id}/Intros", "GET", Summary = "Gets intros to play before the main media item plays")] public class GetIntros : IReturn { /// @@ -94,8 +90,7 @@ namespace MediaBrowser.Api.UserLibrary /// /// Class MarkFavoriteItem /// - [Route("/Users/{UserId}/FavoriteItems/{Id}", "POST")] - [Api(Description = "Marks an item as a favorite")] + [Route("/Users/{UserId}/FavoriteItems/{Id}", "POST", Summary = "Marks an item as a favorite")] public class MarkFavoriteItem : IReturn { /// @@ -116,8 +111,7 @@ namespace MediaBrowser.Api.UserLibrary /// /// Class UnmarkFavoriteItem /// - [Route("/Users/{UserId}/FavoriteItems/{Id}", "DELETE")] - [Api(Description = "Unmarks an item as a favorite")] + [Route("/Users/{UserId}/FavoriteItems/{Id}", "DELETE", Summary = "Unmarks an item as a favorite")] public class UnmarkFavoriteItem : IReturn { /// @@ -138,8 +132,7 @@ namespace MediaBrowser.Api.UserLibrary /// /// Class ClearUserItemRating /// - [Route("/Users/{UserId}/Items/{Id}/Rating", "DELETE")] - [Api(Description = "Deletes a user's saved personal rating for an item")] + [Route("/Users/{UserId}/Items/{Id}/Rating", "DELETE", Summary = "Deletes a user's saved personal rating for an item")] public class DeleteUserItemRating : IReturn { /// @@ -160,8 +153,7 @@ namespace MediaBrowser.Api.UserLibrary /// /// Class UpdateUserItemRating /// - [Route("/Users/{UserId}/Items/{Id}/Rating", "POST")] - [Api(Description = "Updates a user's rating for an item")] + [Route("/Users/{UserId}/Items/{Id}/Rating", "POST", Summary = "Updates a user's rating for an item")] public class UpdateUserItemRating : IReturn { /// @@ -189,8 +181,7 @@ namespace MediaBrowser.Api.UserLibrary /// /// Class GetLocalTrailers /// - [Route("/Users/{UserId}/Items/{Id}/LocalTrailers", "GET")] - [Api(Description = "Gets local trailers for an item")] + [Route("/Users/{UserId}/Items/{Id}/LocalTrailers", "GET", Summary = "Gets local trailers for an item")] public class GetLocalTrailers : IReturn> { /// @@ -211,8 +202,7 @@ namespace MediaBrowser.Api.UserLibrary /// /// Class GetSpecialFeatures /// - [Route("/Users/{UserId}/Items/{Id}/SpecialFeatures", "GET")] - [Api(Description = "Gets special features for an item")] + [Route("/Users/{UserId}/Items/{Id}/SpecialFeatures", "GET", Summary = "Gets special features for an item")] public class GetSpecialFeatures : IReturn> { /// @@ -280,15 +270,6 @@ namespace MediaBrowser.Api.UserLibrary private readonly IDtoService _dtoService; private readonly IUserViewManager _userViewManager; - /// - /// Initializes a new instance of the class. - /// - /// The user manager. - /// The library manager. - /// The user data repository. - /// The dto service. - /// The user view manager. - /// jsonSerializer public UserLibraryService(IUserManager userManager, ILibraryManager libraryManager, IUserDataManager userDataRepository, IDtoService dtoService, IUserViewManager userViewManager) { _userManager = userManager; diff --git a/MediaBrowser.Api/UserService.cs b/MediaBrowser.Api/UserService.cs index 9bd85426d5..a8a288b7e4 100644 --- a/MediaBrowser.Api/UserService.cs +++ b/MediaBrowser.Api/UserService.cs @@ -12,7 +12,6 @@ using ServiceStack; using ServiceStack.Text.Controller; using System; using System.Collections.Generic; -using System.IO; using System.Linq; using System.Threading.Tasks; @@ -59,7 +58,7 @@ namespace MediaBrowser.Api /// Class DeleteUser /// [Route("/Users/{Id}", "DELETE", Summary = "Deletes a user")] - [Authenticated] + [Authenticated(Roles = "Admin")] public class DeleteUser : IReturnVoid { /// @@ -151,25 +150,16 @@ namespace MediaBrowser.Api /// Class UpdateUser /// [Route("/Users/{Id}", "POST", Summary = "Updates a user")] - [Authenticated] + [Authenticated(Roles = "Admin")] public class UpdateUser : UserDto, IReturnVoid { } - /// - /// Class CreateUser - /// - [Route("/Users", "POST", Summary = "Creates a user")] - [Authenticated] - public class CreateUser : UserDto, IReturn - { - } - /// /// Class CreateUser /// [Route("/Users/New", "POST", Summary = "Creates a user")] - [Authenticated] + [Authenticated(Roles = "Admin")] public class CreateUserByName : IReturn { [ApiMember(Name = "Name", IsRequired = true, DataType = "string", ParameterType = "body", Verb = "POST")] @@ -473,24 +463,6 @@ namespace MediaBrowser.Api user.UpdateConfiguration(dtoUser.Configuration); } - /// - /// Posts the specified request. - /// - /// The request. - /// System.Object. - public object Post(CreateUser request) - { - var dtoUser = request; - - var newUser = _userManager.CreateUser(dtoUser.Name).Result; - - newUser.UpdateConfiguration(dtoUser.Configuration); - - var result = _userManager.GetUserDto(newUser, Request.RemoteIp); - - return ToOptimizedResult(result); - } - /// /// Posts the specified request. /// diff --git a/MediaBrowser.Api/VideosService.cs b/MediaBrowser.Api/VideosService.cs index c964ab4589..fbf9cec2e7 100644 --- a/MediaBrowser.Api/VideosService.cs +++ b/MediaBrowser.Api/VideosService.cs @@ -15,6 +15,7 @@ using System.Threading.Tasks; namespace MediaBrowser.Api { [Route("/Videos/{Id}/AdditionalParts", "GET", Summary = "Gets additional parts for a video.")] + [Authenticated] public class GetAdditionalParts : IReturn { [ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] @@ -29,6 +30,7 @@ namespace MediaBrowser.Api } [Route("/Videos/{Id}/AlternateSources", "DELETE", Summary = "Removes alternate video sources.")] + [Authenticated(Roles = "Admin")] public class DeleteAlternateSources : IReturnVoid { [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")] @@ -36,13 +38,13 @@ namespace MediaBrowser.Api } [Route("/Videos/MergeVersions", "POST", Summary = "Merges videos into a single record")] + [Authenticated(Roles = "Admin")] public class MergeVersions : IReturnVoid { [ApiMember(Name = "Ids", Description = "Item id list. This allows multiple, comma delimited.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST", AllowMultiple = true)] public string Ids { get; set; } } - [Authenticated] public class VideosService : BaseApiService { private readonly ILibraryManager _libraryManager; diff --git a/MediaBrowser.Controller/Entities/UserViewBuilder.cs b/MediaBrowser.Controller/Entities/UserViewBuilder.cs index a76a284693..71e26b23cc 100644 --- a/MediaBrowser.Controller/Entities/UserViewBuilder.cs +++ b/MediaBrowser.Controller/Entities/UserViewBuilder.cs @@ -1618,7 +1618,7 @@ namespace MediaBrowser.Controller.Entities var parent = user.RootFolder; - list.Add(await GetUserView(SpecialFolder.LiveTvNowPlaying, user, "0", parent).ConfigureAwait(false)); + //list.Add(await GetUserView(SpecialFolder.LiveTvNowPlaying, user, "0", parent).ConfigureAwait(false)); list.Add(await GetUserView(SpecialFolder.LiveTvChannels, user, string.Empty, parent).ConfigureAwait(false)); list.Add(await GetUserView(SpecialFolder.LiveTvRecordingGroups, user, string.Empty, parent).ConfigureAwait(false)); diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 69edabfb92..72a8f60163 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -220,8 +220,11 @@ + + + diff --git a/MediaBrowser.Controller/Net/AuthenticatedAttribute.cs b/MediaBrowser.Controller/Net/AuthenticatedAttribute.cs index 17c91c9773..b1bab79c5c 100644 --- a/MediaBrowser.Controller/Net/AuthenticatedAttribute.cs +++ b/MediaBrowser.Controller/Net/AuthenticatedAttribute.cs @@ -5,7 +5,7 @@ using System.Linq; namespace MediaBrowser.Controller.Net { - public class AuthenticatedAttribute : Attribute, IHasRequestFilter, IAuthenticated + public class AuthenticatedAttribute : Attribute, IHasRequestFilter, IAuthenticationAttributes { public IAuthService AuthService { get; set; } @@ -21,6 +21,12 @@ namespace MediaBrowser.Controller.Net /// true if [escape parental control]; otherwise, false. public bool EscapeParentalControl { get; set; } + /// + /// Gets or sets a value indicating whether [allow before startup wizard]. + /// + /// true if [allow before startup wizard]; otherwise, false. + public bool AllowBeforeStartupWizard { get; set; } + /// /// The request filter is executed before the service. /// @@ -29,7 +35,9 @@ namespace MediaBrowser.Controller.Net /// The request DTO public void RequestFilter(IRequest request, IResponse response, object requestDto) { - AuthService.Authenticate(request, response, requestDto, this); + var serviceRequest = new ServiceStackServiceRequest(request); + + AuthService.Authenticate(serviceRequest, this); } /// @@ -60,9 +68,10 @@ namespace MediaBrowser.Controller.Net } } - public interface IAuthenticated + public interface IAuthenticationAttributes { bool EscapeParentalControl { get; } + bool AllowBeforeStartupWizard { get; } IEnumerable GetRoles(); } diff --git a/MediaBrowser.Controller/Net/IAuthService.cs b/MediaBrowser.Controller/Net/IAuthService.cs index 9d335566ff..dc298c8d90 100644 --- a/MediaBrowser.Controller/Net/IAuthService.cs +++ b/MediaBrowser.Controller/Net/IAuthService.cs @@ -1,12 +1,9 @@ -using ServiceStack.Web; - + namespace MediaBrowser.Controller.Net { public interface IAuthService { - void Authenticate(IRequest request, - IResponse response, - object requestDto, - IAuthenticated authAttribtues); + void Authenticate(IServiceRequest request, + IAuthenticationAttributes authAttribtues); } } diff --git a/MediaBrowser.Controller/Net/IAuthorizationContext.cs b/MediaBrowser.Controller/Net/IAuthorizationContext.cs index 9cf5623700..bdaed60469 100644 --- a/MediaBrowser.Controller/Net/IAuthorizationContext.cs +++ b/MediaBrowser.Controller/Net/IAuthorizationContext.cs @@ -1,5 +1,4 @@ -using ServiceStack.Web; - + namespace MediaBrowser.Controller.Net { public interface IAuthorizationContext @@ -9,6 +8,13 @@ namespace MediaBrowser.Controller.Net /// /// The request context. /// AuthorizationInfo. - AuthorizationInfo GetAuthorizationInfo(IRequest requestContext); + AuthorizationInfo GetAuthorizationInfo(object requestContext); + + /// + /// Gets the authorization information. + /// + /// The request context. + /// AuthorizationInfo. + AuthorizationInfo GetAuthorizationInfo(IServiceRequest requestContext); } } diff --git a/MediaBrowser.Controller/Net/IServiceRequest.cs b/MediaBrowser.Controller/Net/IServiceRequest.cs new file mode 100644 index 0000000000..e48247362b --- /dev/null +++ b/MediaBrowser.Controller/Net/IServiceRequest.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; +using System.Collections.Specialized; + +namespace MediaBrowser.Controller.Net +{ + public interface IServiceRequest + { + object OriginalRequest { get; } + string RemoteIp { get; } + NameValueCollection Headers { get; } + NameValueCollection QueryString { get; } + IDictionary Items { get; } + void AddResponseHeader(string name, string value); + } +} diff --git a/MediaBrowser.Controller/Net/ISessionContext.cs b/MediaBrowser.Controller/Net/ISessionContext.cs index 31ccd53731..be8d28acc6 100644 --- a/MediaBrowser.Controller/Net/ISessionContext.cs +++ b/MediaBrowser.Controller/Net/ISessionContext.cs @@ -1,13 +1,14 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Session; -using ServiceStack.Web; namespace MediaBrowser.Controller.Net { public interface ISessionContext { - SessionInfo GetSession(IRequest requestContext); - - User GetUser(IRequest requestContext); + SessionInfo GetSession(object requestContext); + User GetUser(object requestContext); + + SessionInfo GetSession(IServiceRequest requestContext); + User GetUser(IServiceRequest requestContext); } } diff --git a/MediaBrowser.Controller/Net/LoggedAttribute.cs b/MediaBrowser.Controller/Net/LoggedAttribute.cs index ea07b1c7f5..d56a015a86 100644 --- a/MediaBrowser.Controller/Net/LoggedAttribute.cs +++ b/MediaBrowser.Controller/Net/LoggedAttribute.cs @@ -22,8 +22,10 @@ namespace MediaBrowser.Controller.Net /// The request DTO public void RequestFilter(IRequest request, IResponse response, object requestDto) { + var serviceRequest = new ServiceStackServiceRequest(request); + //This code is executed before the service - var auth = AuthorizationContext.GetAuthorizationInfo(request); + var auth = AuthorizationContext.GetAuthorizationInfo(serviceRequest); if (auth != null) { diff --git a/MediaBrowser.Controller/Net/SecurityException.cs b/MediaBrowser.Controller/Net/SecurityException.cs new file mode 100644 index 0000000000..b251ab9a92 --- /dev/null +++ b/MediaBrowser.Controller/Net/SecurityException.cs @@ -0,0 +1,21 @@ +using System; + +namespace MediaBrowser.Controller.Net +{ + public class SecurityException : Exception + { + public SecurityException(string message) + : base(message) + { + + } + + public SecurityExceptionType SecurityExceptionType { get; set; } + } + + public enum SecurityExceptionType + { + Unauthenticated = 0, + ParentalControl = 1 + } +} diff --git a/MediaBrowser.Controller/Net/ServiceStackServiceRequest.cs b/MediaBrowser.Controller/Net/ServiceStackServiceRequest.cs new file mode 100644 index 0000000000..a33e9c1c6e --- /dev/null +++ b/MediaBrowser.Controller/Net/ServiceStackServiceRequest.cs @@ -0,0 +1,62 @@ +using ServiceStack.Web; +using System; +using System.Collections.Generic; +using System.Collections.Specialized; + +namespace MediaBrowser.Controller.Net +{ + public class ServiceStackServiceRequest : IServiceRequest + { + private readonly IRequest _request; + + public ServiceStackServiceRequest(IRequest request) + { + _request = request; + } + + public object OriginalRequest + { + get { return _request; } + } + + public string RemoteIp + { + get { return _request.RemoteIp; } + } + + private NameValueCollection _headers; + public NameValueCollection Headers + { + get { return _headers ?? (_headers = Get(_request.Headers)); } + } + + private NameValueCollection _query; + public NameValueCollection QueryString + { + get { return _query ?? (_query = Get(_request.QueryString)); } + } + + private NameValueCollection Get(INameValueCollection coll) + { + var nv = new NameValueCollection(StringComparer.OrdinalIgnoreCase); + + foreach (var key in coll.AllKeys) + { + nv[key] = coll[key]; + } + + return nv; + //return coll.ToNameValueCollection(); + } + + public IDictionary Items + { + get { return _request.Items; } + } + + public void AddResponseHeader(string name, string value) + { + _request.Response.AddHeader(name, value); + } + } +} diff --git a/MediaBrowser.Controller/Session/ISessionManager.cs b/MediaBrowser.Controller/Session/ISessionManager.cs index 8bc5168875..f0272b3350 100644 --- a/MediaBrowser.Controller/Session/ISessionManager.cs +++ b/MediaBrowser.Controller/Session/ISessionManager.cs @@ -255,12 +255,6 @@ namespace MediaBrowser.Controller.Session /// SessionInfo. SessionInfo GetSession(string deviceId, string client, string version); - /// - /// Validates the security token. - /// - /// The access token. - void ValidateSecurityToken(string accessToken); - /// /// Logouts the specified access token. /// diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs index 5b22083334..c9df615e1c 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -170,7 +170,7 @@ namespace MediaBrowser.Model.Configuration public PeopleMetadataOptions PeopleMetadataOptions { get; set; } public bool FindInternetTrailers { get; set; } - public string[] InsecureApps6 { get; set; } + public string[] InsecureApps7 { get; set; } public bool SaveMetadataHidden { get; set; } @@ -222,19 +222,16 @@ namespace MediaBrowser.Model.Configuration PeopleMetadataOptions = new PeopleMetadataOptions(); - InsecureApps6 = new[] + InsecureApps7 = new[] { - "Roku", "Chromecast", "iOS", - "Windows Phone", - "Windows RT", - "Xbmc", "Unknown app", "MediaPortal", "Media Portal", "iPad", - "iPhone" + "iPhone", + "Roku" }; MetadataOptions = new[] diff --git a/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs b/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs index a750dd82ee..56e2e52471 100644 --- a/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs +++ b/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs @@ -91,7 +91,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer {typeof (ResourceNotFoundException), 404}, {typeof (FileNotFoundException), 404}, {typeof (DirectoryNotFoundException), 404}, - {typeof (Implementations.Security.AuthenticationException), 401}, + {typeof (SecurityException), 401}, {typeof (UnauthorizedAccessException), 401} }; diff --git a/MediaBrowser.Server.Implementations/HttpServer/Security/AuthService.cs b/MediaBrowser.Server.Implementations/HttpServer/Security/AuthService.cs index dde61079b9..e9a8cc7527 100644 --- a/MediaBrowser.Server.Implementations/HttpServer/Security/AuthService.cs +++ b/MediaBrowser.Server.Implementations/HttpServer/Security/AuthService.cs @@ -1,13 +1,11 @@ using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Connect; +using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Net; -using MediaBrowser.Controller.Session; -using ServiceStack; -using ServiceStack.Auth; -using ServiceStack.Web; +using MediaBrowser.Controller.Security; using System; -using System.Collections.Specialized; +using System.Collections.Generic; using System.Linq; namespace MediaBrowser.Server.Implementations.HttpServer.Security @@ -16,61 +14,43 @@ namespace MediaBrowser.Server.Implementations.HttpServer.Security { private readonly IServerConfigurationManager _config; - public AuthService(IUserManager userManager, ISessionManager sessionManager, IAuthorizationContext authorizationContext, IServerConfigurationManager config, IConnectManager connectManager) + public AuthService(IUserManager userManager, IAuthorizationContext authorizationContext, IServerConfigurationManager config, IConnectManager connectManager) { AuthorizationContext = authorizationContext; _config = config; ConnectManager = connectManager; - SessionManager = sessionManager; UserManager = userManager; } public IUserManager UserManager { get; private set; } - public ISessionManager SessionManager { get; private set; } public IAuthorizationContext AuthorizationContext { get; private set; } public IConnectManager ConnectManager { get; private set; } - /// - /// Restrict authentication to a specific . - /// For example, if this attribute should only permit access - /// if the user is authenticated with , - /// you should set this property to . - /// - public string Provider { get; set; } - /// /// Redirect the client to a specific URL if authentication failed. /// If this property is null, simply `401 Unauthorized` is returned. /// public string HtmlRedirect { get; set; } - public void Authenticate(IRequest request, - IResponse response, - object requestDto, - IAuthenticated authAttribtues) + public void Authenticate(IServiceRequest request, + IAuthenticationAttributes authAttribtues) { - if (HostContext.HasValidAuthSecret(request)) - return; - - //ExecuteBasic(req, res, requestDto); //first check if session is authenticated - //if (res.IsClosed) return; //AuthenticateAttribute already closed the request (ie auth failed) - - ValidateUser(request, response, authAttribtues); + ValidateUser(request, authAttribtues); } - private void ValidateUser(IRequest req, IResponse response, IAuthenticated authAttribtues) + private void ValidateUser(IServiceRequest request, + IAuthenticationAttributes authAttribtues) { // This code is executed before the service - var auth = AuthorizationContext.GetAuthorizationInfo(req); + var auth = AuthorizationContext.GetAuthorizationInfo(request); - if (!string.IsNullOrWhiteSpace(auth.Token) || - !_config.Configuration.InsecureApps6.Contains(auth.Client ?? string.Empty, StringComparer.OrdinalIgnoreCase)) + if (!IsExemptFromAuthenticationToken(auth, authAttribtues)) { var valid = IsValidConnectKey(auth.Token); if (!valid) { - SessionManager.ValidateSecurityToken(auth.Token); + ValidateSecurityToken(request, auth.Token); } } @@ -80,45 +60,83 @@ namespace MediaBrowser.Server.Implementations.HttpServer.Security if (user == null & !string.IsNullOrWhiteSpace(auth.UserId)) { - throw new ArgumentException("User with Id " + auth.UserId + " not found"); + throw new SecurityException("User with Id " + auth.UserId + " not found"); } if (user != null) { if (user.Configuration.IsDisabled) { - throw new AuthenticationException("User account has been disabled."); + throw new SecurityException("User account has been disabled.") + { + SecurityExceptionType = SecurityExceptionType.Unauthenticated + }; } if (!user.Configuration.IsAdministrator && !authAttribtues.EscapeParentalControl && !user.IsParentalScheduleAllowed()) { - response.AddHeader("X-Application-Error-Code", "ParentalControl"); - throw new AuthenticationException("This user account is not allowed access at this time."); + request.AddResponseHeader("X-Application-Error-Code", "ParentalControl"); + throw new SecurityException("This user account is not allowed access at this time.") + { + SecurityExceptionType = SecurityExceptionType.ParentalControl + }; } } - var roles = authAttribtues.GetRoles().ToList(); + if (!IsExemptFromRoles(auth, authAttribtues)) + { + var roles = authAttribtues.GetRoles().ToList(); + + ValidateRoles(roles, user); + } + } + + private bool IsExemptFromAuthenticationToken(AuthorizationInfo auth, IAuthenticationAttributes authAttribtues) + { + if (!_config.Configuration.IsStartupWizardCompleted && + authAttribtues.AllowBeforeStartupWizard) + { + return true; + } + + return _config.Configuration.InsecureApps7.Contains(auth.Client ?? string.Empty, + StringComparer.OrdinalIgnoreCase); + } + + private bool IsExemptFromRoles(AuthorizationInfo auth, IAuthenticationAttributes authAttribtues) + { + if (!_config.Configuration.IsStartupWizardCompleted && + authAttribtues.AllowBeforeStartupWizard) + { + return true; + } + + return false; + } + private void ValidateRoles(List roles, User user) + { if (roles.Contains("admin", StringComparer.OrdinalIgnoreCase)) { if (user == null || !user.Configuration.IsAdministrator) { - throw new ArgumentException("Administrative access is required for this request."); + throw new SecurityException("User does not have admin access.") + { + SecurityExceptionType = SecurityExceptionType.Unauthenticated + }; } } - - if (!string.IsNullOrWhiteSpace(auth.DeviceId) && - !string.IsNullOrWhiteSpace(auth.Client) && - !string.IsNullOrWhiteSpace(auth.Device)) + if (roles.Contains("delete", StringComparer.OrdinalIgnoreCase)) { - SessionManager.LogSessionActivity(auth.Client, - auth.Version, - auth.DeviceId, - auth.Device, - req.RemoteIp, - user); + if (user == null || !user.Configuration.EnableContentDeletion) + { + throw new SecurityException("User does not have delete access.") + { + SecurityExceptionType = SecurityExceptionType.Unauthenticated + }; + } } } @@ -132,40 +150,34 @@ namespace MediaBrowser.Server.Implementations.HttpServer.Security return ConnectManager.IsAuthorizationTokenValid(token); } - protected bool DoHtmlRedirectIfConfigured(IRequest req, IResponse res, bool includeRedirectParam = false) + private void ValidateSecurityToken(IServiceRequest request, string token) { - var htmlRedirect = this.HtmlRedirect ?? AuthenticateService.HtmlRedirect; - if (htmlRedirect != null && req.ResponseContentType.MatchesContentType(MimeTypes.Html)) + if (string.IsNullOrWhiteSpace(token)) { - DoHtmlRedirect(htmlRedirect, req, res, includeRedirectParam); - return true; + throw new SecurityException("Access token is invalid or expired."); } - return false; - } - public static void DoHtmlRedirect(string redirectUrl, IRequest req, IResponse res, bool includeRedirectParam) - { - var url = req.ResolveAbsoluteUrl(redirectUrl); - if (includeRedirectParam) + var info = (AuthenticationInfo)request.Items["OriginalAuthenticationInfo"]; + + if (info == null) { - var absoluteRequestPath = req.ResolveAbsoluteUrl("~" + req.PathInfo + ToQueryString(req.QueryString)); - url = url.AddQueryParam(HostContext.ResolveLocalizedString(LocalizedStrings.Redirect), absoluteRequestPath); + throw new SecurityException("Access token is invalid or expired."); } - res.RedirectToUrl(url); - } - - private static string ToQueryString(INameValueCollection queryStringCollection) - { - return ToQueryString((NameValueCollection)queryStringCollection.Original); - } + if (!info.IsActive) + { + throw new SecurityException("Access token has expired."); + } - private static string ToQueryString(NameValueCollection queryStringCollection) - { - if (queryStringCollection == null || queryStringCollection.Count == 0) - return String.Empty; + //if (!string.IsNullOrWhiteSpace(info.UserId)) + //{ + // var user = _userManager.GetUserById(info.UserId); - return "?" + queryStringCollection.ToFormUrlEncoded(); + // if (user == null || user.Configuration.IsDisabled) + // { + // throw new SecurityException("User account has been disabled."); + // } + //} } } } diff --git a/MediaBrowser.Server.Implementations/HttpServer/Security/AuthorizationContext.cs b/MediaBrowser.Server.Implementations/HttpServer/Security/AuthorizationContext.cs index 3cc703bbfb..4e98993014 100644 --- a/MediaBrowser.Server.Implementations/HttpServer/Security/AuthorizationContext.cs +++ b/MediaBrowser.Server.Implementations/HttpServer/Security/AuthorizationContext.cs @@ -1,14 +1,35 @@ -using System; -using System.Collections.Generic; -using MediaBrowser.Controller.Net; +using MediaBrowser.Controller.Net; +using MediaBrowser.Controller.Security; using ServiceStack.Web; +using System; +using System.Collections.Generic; +using System.Linq; namespace MediaBrowser.Server.Implementations.HttpServer.Security { public class AuthorizationContext : IAuthorizationContext { - public AuthorizationInfo GetAuthorizationInfo(IRequest requestContext) + private readonly IAuthenticationRepository _authRepo; + + public AuthorizationContext(IAuthenticationRepository authRepo) + { + _authRepo = authRepo; + } + + public AuthorizationInfo GetAuthorizationInfo(object requestContext) { + var req = new ServiceStackServiceRequest((IRequest)requestContext); + return GetAuthorizationInfo(req); + } + + public AuthorizationInfo GetAuthorizationInfo(IServiceRequest requestContext) + { + object cached; + if (requestContext.Items.TryGetValue("AuthorizationInfo", out cached)) + { + return (AuthorizationInfo)cached; + } + return GetAuthorization(requestContext); } @@ -17,7 +38,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer.Security /// /// The HTTP req. /// Dictionary{System.StringSystem.String}. - private AuthorizationInfo GetAuthorization(IRequest httpReq) + private AuthorizationInfo GetAuthorization(IServiceRequest httpReq) { var auth = GetAuthorizationDictionary(httpReq); @@ -29,7 +50,9 @@ namespace MediaBrowser.Server.Implementations.HttpServer.Security if (auth != null) { + // TODO: Remove this auth.TryGetValue("UserId", out userId); + auth.TryGetValue("DeviceId", out deviceId); auth.TryGetValue("Device", out device); auth.TryGetValue("Client", out client); @@ -84,7 +107,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer.Security } } - return new AuthorizationInfo + var info = new AuthorizationInfo { Client = client, Device = device, @@ -93,6 +116,26 @@ namespace MediaBrowser.Server.Implementations.HttpServer.Security Version = version, Token = token }; + + if (!string.IsNullOrWhiteSpace(token)) + { + var result = _authRepo.Get(new AuthenticationInfoQuery + { + AccessToken = token + }); + + var tokenInfo = result.Items.FirstOrDefault(); + + if (tokenInfo != null) + { + info.UserId = tokenInfo.UserId; + } + httpReq.Items["OriginalAuthenticationInfo"] = tokenInfo; + } + + httpReq.Items["AuthorizationInfo"] = info; + + return info; } /// @@ -100,7 +143,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer.Security /// /// The HTTP req. /// Dictionary{System.StringSystem.String}. - private Dictionary GetAuthorizationDictionary(IRequest httpReq) + private Dictionary GetAuthorizationDictionary(IServiceRequest httpReq) { var auth = httpReq.Headers["Authorization"]; diff --git a/MediaBrowser.Server.Implementations/HttpServer/Security/SessionContext.cs b/MediaBrowser.Server.Implementations/HttpServer/Security/SessionContext.cs index f67c643c82..9d1ddb7fc4 100644 --- a/MediaBrowser.Server.Implementations/HttpServer/Security/SessionContext.cs +++ b/MediaBrowser.Server.Implementations/HttpServer/Security/SessionContext.cs @@ -19,18 +19,30 @@ namespace MediaBrowser.Server.Implementations.HttpServer.Security _sessionManager = sessionManager; } - public SessionInfo GetSession(IRequest requestContext) + public SessionInfo GetSession(IServiceRequest requestContext) { var authorization = _authContext.GetAuthorizationInfo(requestContext); return _sessionManager.GetSession(authorization.DeviceId, authorization.Client, authorization.Version); } - public User GetUser(IRequest requestContext) + public User GetUser(IServiceRequest requestContext) { var session = GetSession(requestContext); return session == null || !session.UserId.HasValue ? null : _userManager.GetUserById(session.UserId.Value); } + + public SessionInfo GetSession(object requestContext) + { + var req = new ServiceStackServiceRequest((IRequest)requestContext); + return GetSession(req); + } + + public User GetUser(object requestContext) + { + var req = new ServiceStackServiceRequest((IRequest)requestContext); + return GetUser(req); + } } } diff --git a/MediaBrowser.Server.Implementations/Library/UserManager.cs b/MediaBrowser.Server.Implementations/Library/UserManager.cs index fa34a2c1f1..803fbd454d 100644 --- a/MediaBrowser.Server.Implementations/Library/UserManager.cs +++ b/MediaBrowser.Server.Implementations/Library/UserManager.cs @@ -8,6 +8,7 @@ using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Configuration; @@ -180,12 +181,12 @@ namespace MediaBrowser.Server.Implementations.Library if (user == null) { - throw new AuthenticationException("Invalid username or password entered."); + throw new SecurityException("Invalid username or password entered."); } if (user.Configuration.IsDisabled) { - throw new AuthenticationException(string.Format("The {0} account is currently disabled. Please consult with your administrator.", user.Name)); + throw new SecurityException(string.Format("The {0} account is currently disabled. Please consult with your administrator.", user.Name)); } var success = false; diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj index 591dfba493..644b041f95 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj @@ -247,7 +247,6 @@ - diff --git a/MediaBrowser.Server.Implementations/Security/AuthenticationException.cs b/MediaBrowser.Server.Implementations/Security/AuthenticationException.cs deleted file mode 100644 index f4ba2fb630..0000000000 --- a/MediaBrowser.Server.Implementations/Security/AuthenticationException.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; - -namespace MediaBrowser.Server.Implementations.Security -{ - public class AuthenticationException : Exception - { - public AuthenticationException(string message) - : base(message) - { - } - - public AuthenticationException() - { - } - } -} diff --git a/MediaBrowser.Server.Implementations/Session/SessionManager.cs b/MediaBrowser.Server.Implementations/Session/SessionManager.cs index d6db836cc7..1f294c325e 100644 --- a/MediaBrowser.Server.Implementations/Session/SessionManager.cs +++ b/MediaBrowser.Server.Implementations/Session/SessionManager.cs @@ -11,10 +11,10 @@ using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; +using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Security; using MediaBrowser.Controller.Session; -using MediaBrowser.Model.Connect; using MediaBrowser.Model.Devices; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Events; @@ -23,7 +23,6 @@ using MediaBrowser.Model.Logging; using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Session; using MediaBrowser.Model.Users; -using MediaBrowser.Server.Implementations.Security; using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -253,11 +252,6 @@ namespace MediaBrowser.Server.Implementations.Session throw new ArgumentNullException("deviceName"); } - if (user != null && user.Configuration.IsDisabled) - { - throw new AuthenticationException(string.Format("The {0} account is currently disabled. Please consult with your administrator.", user.Name)); - } - var activityDate = DateTime.UtcNow; var userId = user == null ? (Guid?)null : user.Id; @@ -1261,49 +1255,11 @@ namespace MediaBrowser.Server.Implementations.Session } } - public void ValidateSecurityToken(string token) - { - if (string.IsNullOrWhiteSpace(token)) - { - throw new AuthenticationException(); - } - - var result = _authRepo.Get(new AuthenticationInfoQuery - { - AccessToken = token - }); - - var info = result.Items.FirstOrDefault(); - - if (info == null) - { - throw new AuthenticationException(); - } - - if (!info.IsActive) - { - throw new AuthenticationException("Access token has expired."); - } - - if (!string.IsNullOrWhiteSpace(info.UserId)) - { - var user = _userManager.GetUserById(info.UserId); - - if (user == null || user.Configuration.IsDisabled) - { - throw new AuthenticationException("User account has been disabled."); - } - } - } - /// /// Authenticates the new session. /// /// The request. /// Task{SessionInfo}. - /// Invalid user or password entered. - /// Invalid user or password entered. - /// Invalid user or password entered. public async Task AuthenticateNewSession(AuthenticationRequest request) { var user = _userManager.Users @@ -1315,7 +1271,7 @@ namespace MediaBrowser.Server.Implementations.Session { EventHelper.FireEventIfNotNull(AuthenticationFailed, this, new GenericEventArgs(request), _logger); - throw new AuthenticationException("Invalid user or password entered."); + throw new UnauthorizedAccessException("Invalid user or password entered."); } var token = await GetAuthorizationToken(user.Id.ToString("N"), request.DeviceId, request.App, request.DeviceName).ConfigureAwait(false); diff --git a/MediaBrowser.Server.Startup.Common/ApplicationHost.cs b/MediaBrowser.Server.Startup.Common/ApplicationHost.cs index c4a3562ea7..4389dd98c5 100644 --- a/MediaBrowser.Server.Startup.Common/ApplicationHost.cs +++ b/MediaBrowser.Server.Startup.Common/ApplicationHost.cs @@ -496,10 +496,10 @@ namespace MediaBrowser.Server.Startup.Common RegisterSingleInstance(activityLogRepo); RegisterSingleInstance(new ActivityManager(LogManager.GetLogger("ActivityManager"), activityLogRepo, UserManager)); - var authContext = new AuthorizationContext(); + var authContext = new AuthorizationContext(AuthenticationRepository); RegisterSingleInstance(authContext); RegisterSingleInstance(new SessionContext(UserManager, authContext, SessionManager)); - RegisterSingleInstance(new AuthService(UserManager, SessionManager, authContext, ServerConfigurationManager, ConnectManager)); + RegisterSingleInstance(new AuthService(UserManager, authContext, ServerConfigurationManager, ConnectManager)); RegisterSingleInstance(new SubtitleEncoder(LibraryManager, LogManager.GetLogger("SubtitleEncoder"), ApplicationPaths, FileSystemManager, MediaEncoder, JsonSerializer)); @@ -534,6 +534,8 @@ namespace MediaBrowser.Server.Startup.Common var info = await new FFMpegDownloader(Logger, ApplicationPaths, HttpClient, ZipClient, FileSystemManager) .GetFFMpegInfo(NativeApp.Environment, _startupOptions, progress).ConfigureAwait(false); + new FFmpegValidator(Logger, ApplicationPaths).Validate(info); + MediaEncoder = new MediaEncoder(LogManager.GetLogger("MediaEncoder"), JsonSerializer, info.EncoderPath, info.ProbePath, info.Version); RegisterSingleInstance(MediaEncoder); } diff --git a/MediaBrowser.Server.Startup.Common/FFMpeg/FFmpegValidator.cs b/MediaBrowser.Server.Startup.Common/FFMpeg/FFmpegValidator.cs new file mode 100644 index 0000000000..a2abea13cc --- /dev/null +++ b/MediaBrowser.Server.Startup.Common/FFMpeg/FFmpegValidator.cs @@ -0,0 +1,116 @@ +using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.Extensions; +using MediaBrowser.Model.Logging; +using System; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Text; + +namespace MediaBrowser.Server.Startup.Common.FFMpeg +{ + public class FFmpegValidator + { + private readonly ILogger _logger; + private readonly IApplicationPaths _appPaths; + + public FFmpegValidator(ILogger logger, IApplicationPaths appPaths) + { + _logger = logger; + _appPaths = appPaths; + } + + public void Validate(FFMpegInfo info) + { + _logger.Info("FFMpeg: {0}", info.EncoderPath); + _logger.Info("FFProbe: {0}", info.ProbePath); + + var fileInfo = new FileInfo(info.EncoderPath); + var cachePath = Path.Combine(_appPaths.CachePath, fileInfo.Length.ToString(CultureInfo.InvariantCulture).GetMD5().ToString("N")); + + if (!File.Exists(cachePath)) + { + ValidateCodecs(info.EncoderPath); + + Directory.CreateDirectory(Path.GetDirectoryName(cachePath)); + File.WriteAllText(cachePath, string.Empty, Encoding.UTF8); + } + } + + private void ValidateCodecs(string path) + { + var output = GetOutput(path, "-encoders"); + + var required = new[] + { + "libx264", + "mpeg4", + "msmpeg4", + "libvpx", + //"libvpx-vp9", + "aac", + "ac3", + "libmp3lame", + "libvorbis", + "srt" + }; + + foreach (var encoder in required) + { + var srch = " " + encoder + " "; + + if (output.IndexOf(srch, StringComparison.OrdinalIgnoreCase) == -1) + { + throw new ArgumentException("ffmpeg is missing encoder " + encoder); + } + } + } + + private string GetOutput(string path, string arguments) + { + var process = new Process + { + StartInfo = new ProcessStartInfo + { + CreateNoWindow = true, + UseShellExecute = false, + FileName = path, + Arguments = arguments, + WindowStyle = ProcessWindowStyle.Hidden, + ErrorDialog = false, + RedirectStandardOutput = true, + RedirectStandardError = true + } + }; + + using (process) + { + process.Start(); + + try + { + process.BeginErrorReadLine(); + + using (var reader = new StreamReader(process.StandardOutput.BaseStream)) + { + return reader.ReadToEnd(); + } + } + catch + { + // Hate having to do this + try + { + process.Kill(); + } + catch (Exception ex1) + { + _logger.ErrorException("Error killing ffmpeg", ex1); + } + + throw; + } + } + } + } +} diff --git a/MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj b/MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj index 4667fe194e..474dadfe28 100644 --- a/MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj +++ b/MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj @@ -59,6 +59,7 @@ + diff --git a/MediaBrowser.ServerApplication/MainStartup.cs b/MediaBrowser.ServerApplication/MainStartup.cs index 294752efc1..ab96f1da1a 100644 --- a/MediaBrowser.ServerApplication/MainStartup.cs +++ b/MediaBrowser.ServerApplication/MainStartup.cs @@ -195,8 +195,7 @@ namespace MediaBrowser.ServerApplication /// The app paths. private static void BeginLog(ILogger logger, IApplicationPaths appPaths) { - logger.Info("Media Browser Server started"); - ApplicationHost.LogEnvironmentInfo(logger, appPaths); + ApplicationHost.LogEnvironmentInfo(logger, appPaths, true); } private static readonly TaskCompletionSource ApplicationTaskCompletionSource = new TaskCompletionSource();