fixes #789 - Security Issue: API allows access to any folder of the PC running MediaBrowser
This commit is contained in:
parent
3bef6ead9c
commit
389390b82e
|
@ -1,4 +1,5 @@
|
|||
using MediaBrowser.Common.IO;
|
||||
using MediaBrowser.Controller.Net;
|
||||
using MediaBrowser.Controller.Themes;
|
||||
using MediaBrowser.Model.Themes;
|
||||
using ServiceStack;
|
||||
|
@ -47,6 +48,7 @@ namespace MediaBrowser.Api
|
|||
{
|
||||
}
|
||||
|
||||
[Authenticated]
|
||||
public class AppThemeService : BaseApiService
|
||||
{
|
||||
private readonly IAppThemeManager _themeManager;
|
||||
|
|
|
@ -1,190 +0,0 @@
|
|||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.Session;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using ServiceStack.Web;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MediaBrowser.Api
|
||||
{
|
||||
public class AuthorizationRequestFilterAttribute : Attribute, IHasRequestFilter
|
||||
{
|
||||
//This property will be resolved by the IoC container
|
||||
/// <summary>
|
||||
/// Gets or sets the user manager.
|
||||
/// </summary>
|
||||
/// <value>The user manager.</value>
|
||||
public IUserManager UserManager { get; set; }
|
||||
|
||||
public ISessionManager SessionManager { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the logger.
|
||||
/// </summary>
|
||||
/// <value>The logger.</value>
|
||||
public ILogger Logger { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The request filter is executed before the service.
|
||||
/// </summary>
|
||||
/// <param name="request">The http request wrapper</param>
|
||||
/// <param name="response">The http response wrapper</param>
|
||||
/// <param name="requestDto">The request DTO</param>
|
||||
public void RequestFilter(IRequest request, IResponse response, object requestDto)
|
||||
{
|
||||
//This code is executed before the service
|
||||
var auth = GetAuthorizationDictionary(request);
|
||||
|
||||
if (auth != null)
|
||||
{
|
||||
User user = null;
|
||||
|
||||
if (auth.ContainsKey("UserId"))
|
||||
{
|
||||
var userId = auth["UserId"];
|
||||
|
||||
if (!string.IsNullOrEmpty(userId))
|
||||
{
|
||||
user = UserManager.GetUserById(new Guid(userId));
|
||||
}
|
||||
}
|
||||
|
||||
string deviceId;
|
||||
string device;
|
||||
string client;
|
||||
string version;
|
||||
|
||||
auth.TryGetValue("DeviceId", out deviceId);
|
||||
auth.TryGetValue("Device", out device);
|
||||
auth.TryGetValue("Client", out client);
|
||||
auth.TryGetValue("Version", out version);
|
||||
|
||||
if (!string.IsNullOrEmpty(client) && !string.IsNullOrEmpty(deviceId) && !string.IsNullOrEmpty(device) && !string.IsNullOrEmpty(version))
|
||||
{
|
||||
var remoteEndPoint = request.RemoteIp;
|
||||
|
||||
SessionManager.LogSessionActivity(client, version, deviceId, device, remoteEndPoint, user);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the auth.
|
||||
/// </summary>
|
||||
/// <param name="httpReq">The HTTP req.</param>
|
||||
/// <returns>Dictionary{System.StringSystem.String}.</returns>
|
||||
private static Dictionary<string, string> GetAuthorizationDictionary(IRequest httpReq)
|
||||
{
|
||||
var auth = httpReq.Headers["Authorization"];
|
||||
|
||||
return GetAuthorization(auth);
|
||||
}
|
||||
|
||||
public static User GetCurrentUser(IRequest httpReq, IUserManager userManager)
|
||||
{
|
||||
var info = GetAuthorization(httpReq);
|
||||
|
||||
return string.IsNullOrEmpty(info.UserId) ? null :
|
||||
userManager.GetUserById(new Guid(info.UserId));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the authorization.
|
||||
/// </summary>
|
||||
/// <param name="httpReq">The HTTP req.</param>
|
||||
/// <returns>Dictionary{System.StringSystem.String}.</returns>
|
||||
public static AuthorizationInfo GetAuthorization(IRequest httpReq)
|
||||
{
|
||||
var auth = GetAuthorizationDictionary(httpReq);
|
||||
|
||||
string userId = null;
|
||||
string deviceId = null;
|
||||
string device = null;
|
||||
string client = null;
|
||||
string version = null;
|
||||
|
||||
if (auth != null)
|
||||
{
|
||||
auth.TryGetValue("UserId", out userId);
|
||||
auth.TryGetValue("DeviceId", out deviceId);
|
||||
auth.TryGetValue("Device", out device);
|
||||
auth.TryGetValue("Client", out client);
|
||||
auth.TryGetValue("Version", out version);
|
||||
}
|
||||
|
||||
return new AuthorizationInfo
|
||||
{
|
||||
Client = client,
|
||||
Device = device,
|
||||
DeviceId = deviceId,
|
||||
UserId = userId,
|
||||
Version = version
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the authorization.
|
||||
/// </summary>
|
||||
/// <param name="authorizationHeader">The authorization header.</param>
|
||||
/// <returns>Dictionary{System.StringSystem.String}.</returns>
|
||||
private static Dictionary<string, string> GetAuthorization(string authorizationHeader)
|
||||
{
|
||||
if (authorizationHeader == null) return null;
|
||||
|
||||
var parts = authorizationHeader.Split(' ');
|
||||
|
||||
// There should be at least to parts
|
||||
if (parts.Length < 2) return null;
|
||||
|
||||
// It has to be a digest request
|
||||
if (!string.Equals(parts[0], "MediaBrowser", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Remove uptil the first space
|
||||
authorizationHeader = authorizationHeader.Substring(authorizationHeader.IndexOf(' '));
|
||||
parts = authorizationHeader.Split(',');
|
||||
|
||||
var result = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
foreach (var item in parts)
|
||||
{
|
||||
var param = item.Trim().Split(new[] { '=' }, 2);
|
||||
result.Add(param[0], param[1].Trim(new[] { '"' }));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A new shallow copy of this filter is used on every request.
|
||||
/// </summary>
|
||||
/// <returns>IHasRequestFilter.</returns>
|
||||
public IHasRequestFilter Copy()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Order in which Request Filters are executed.
|
||||
/// <0 Executed before global request filters
|
||||
/// >0 Executed after global request filters
|
||||
/// </summary>
|
||||
/// <value>The priority.</value>
|
||||
public int Priority
|
||||
{
|
||||
get { return 0; }
|
||||
}
|
||||
}
|
||||
|
||||
public class AuthorizationInfo
|
||||
{
|
||||
public string UserId;
|
||||
public string DeviceId;
|
||||
public string Device;
|
||||
public string Client;
|
||||
public string Version;
|
||||
}
|
||||
}
|
|
@ -14,8 +14,7 @@ namespace MediaBrowser.Api
|
|||
/// <summary>
|
||||
/// Class BaseApiService
|
||||
/// </summary>
|
||||
[AuthorizationRequestFilter]
|
||||
public class BaseApiService : IHasResultFactory, IRestfulService
|
||||
public class BaseApiService : IHasResultFactory, IRestfulService, IHasSession
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the logger.
|
||||
|
@ -35,6 +34,8 @@ namespace MediaBrowser.Api
|
|||
/// <value>The request context.</value>
|
||||
public IRequest Request { get; set; }
|
||||
|
||||
public ISessionContext SessionContext { get; set; }
|
||||
|
||||
public string GetHeader(string name)
|
||||
{
|
||||
return Request.Headers[name];
|
||||
|
@ -82,13 +83,11 @@ namespace MediaBrowser.Api
|
|||
/// <summary>
|
||||
/// Gets the session.
|
||||
/// </summary>
|
||||
/// <param name="sessionManager">The session manager.</param>
|
||||
/// <returns>SessionInfo.</returns>
|
||||
protected SessionInfo GetSession(ISessionManager sessionManager)
|
||||
/// <exception cref="System.ArgumentException">Session not found.</exception>
|
||||
protected SessionInfo GetSession()
|
||||
{
|
||||
var auth = AuthorizationRequestFilterAttribute.GetAuthorization(Request);
|
||||
|
||||
var session = sessionManager.GetSession(auth.DeviceId, auth.Client, auth.Version);
|
||||
var session = SessionContext.GetSession(Request);
|
||||
|
||||
if (session == null)
|
||||
{
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using MediaBrowser.Controller.Channels;
|
||||
using MediaBrowser.Controller.Net;
|
||||
using MediaBrowser.Model.Channels;
|
||||
using MediaBrowser.Model.Dto;
|
||||
using MediaBrowser.Model.Entities;
|
||||
|
@ -172,7 +173,8 @@ namespace MediaBrowser.Api
|
|||
[ApiMember(Name = "UserId", Description = "Optional attach user data.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
|
||||
public string UserId { get; set; }
|
||||
}
|
||||
|
||||
|
||||
[Authenticated]
|
||||
public class ChannelService : BaseApiService
|
||||
{
|
||||
private readonly IChannelManager _channelManager;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using MediaBrowser.Controller.Persistence;
|
||||
using MediaBrowser.Controller.Net;
|
||||
using MediaBrowser.Controller.Persistence;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Serialization;
|
||||
using ServiceStack;
|
||||
|
@ -48,6 +49,7 @@ namespace MediaBrowser.Api
|
|||
/// <summary>
|
||||
/// Class DisplayPreferencesService
|
||||
/// </summary>
|
||||
[Authenticated]
|
||||
public class DisplayPreferencesService : BaseApiService
|
||||
{
|
||||
/// <summary>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Controller.Net;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.Net;
|
||||
using ServiceStack;
|
||||
|
@ -86,6 +87,7 @@ namespace MediaBrowser.Api
|
|||
/// <summary>
|
||||
/// Class EnvironmentService
|
||||
/// </summary>
|
||||
[Authenticated]
|
||||
public class EnvironmentService : BaseApiService
|
||||
{
|
||||
const char UncSeparator = '\\';
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using MediaBrowser.Controller.Dto;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.Net;
|
||||
using MediaBrowser.Controller.Persistence;
|
||||
using MediaBrowser.Model.Dto;
|
||||
using ServiceStack;
|
||||
|
@ -51,6 +52,7 @@ namespace MediaBrowser.Api
|
|||
/// <summary>
|
||||
/// Class GamesService
|
||||
/// </summary>
|
||||
[Authenticated]
|
||||
public class GamesService : BaseApiService
|
||||
{
|
||||
/// <summary>
|
||||
|
|
|
@ -470,7 +470,7 @@ namespace MediaBrowser.Api.Library
|
|||
{
|
||||
var item = _libraryManager.GetItemById(request.Id);
|
||||
|
||||
var session = GetSession(_sessionManager);
|
||||
var session = GetSession();
|
||||
|
||||
if (!session.UserId.HasValue || !_userManager.GetUserById(session.UserId.Value).Configuration.EnableContentDeletion)
|
||||
{
|
||||
|
|
|
@ -280,7 +280,7 @@ namespace MediaBrowser.Api.LiveTv
|
|||
|
||||
private void AssertUserCanManageLiveTv()
|
||||
{
|
||||
var user = AuthorizationRequestFilterAttribute.GetCurrentUser(Request, _userManager);
|
||||
var user = SessionContext.GetUser(Request);
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
|
|
|
@ -79,7 +79,6 @@
|
|||
<Compile Include="DefaultTheme\Models.cs" />
|
||||
<Compile Include="DisplayPreferencesService.cs" />
|
||||
<Compile Include="EnvironmentService.cs" />
|
||||
<Compile Include="AuthorizationRequestFilterAttribute.cs" />
|
||||
<Compile Include="GamesService.cs" />
|
||||
<Compile Include="IHasItemFields.cs" />
|
||||
<Compile Include="Images\ImageByNameService.cs" />
|
||||
|
|
|
@ -1386,8 +1386,6 @@ namespace MediaBrowser.Api.Playback
|
|||
ParseParams(request);
|
||||
}
|
||||
|
||||
var user = AuthorizationRequestFilterAttribute.GetCurrentUser(Request, UserManager);
|
||||
|
||||
var url = Request.PathInfo;
|
||||
|
||||
if (string.IsNullOrEmpty(request.AudioCodec))
|
||||
|
@ -1409,11 +1407,6 @@ namespace MediaBrowser.Api.Playback
|
|||
|
||||
var item = LibraryManager.GetItemById(request.Id);
|
||||
|
||||
if (user != null && item.GetPlayAccess(user) != PlayAccess.Full)
|
||||
{
|
||||
throw new ArgumentException(string.Format("{0} is not allowed to play media.", user.Name));
|
||||
}
|
||||
|
||||
List<MediaStream> mediaStreams = null;
|
||||
|
||||
state.ItemType = item.GetType().Name;
|
||||
|
|
|
@ -285,7 +285,7 @@ namespace MediaBrowser.Api
|
|||
SeekPositionTicks = request.SeekPositionTicks
|
||||
};
|
||||
|
||||
var task = _sessionManager.SendPlaystateCommand(GetSession(_sessionManager).Id, request.Id, command, CancellationToken.None);
|
||||
var task = _sessionManager.SendPlaystateCommand(GetSession().Id, request.Id, command, CancellationToken.None);
|
||||
|
||||
Task.WaitAll(task);
|
||||
}
|
||||
|
@ -303,7 +303,7 @@ namespace MediaBrowser.Api
|
|||
ItemType = request.ItemType
|
||||
};
|
||||
|
||||
var task = _sessionManager.SendBrowseCommand(GetSession(_sessionManager).Id, request.Id, command, CancellationToken.None);
|
||||
var task = _sessionManager.SendBrowseCommand(GetSession().Id, request.Id, command, CancellationToken.None);
|
||||
|
||||
Task.WaitAll(task);
|
||||
}
|
||||
|
@ -318,7 +318,7 @@ namespace MediaBrowser.Api
|
|||
|
||||
if (Enum.TryParse(request.Command, true, out commandType))
|
||||
{
|
||||
var currentSession = GetSession(_sessionManager);
|
||||
var currentSession = GetSession();
|
||||
|
||||
var command = new GeneralCommand
|
||||
{
|
||||
|
@ -345,7 +345,7 @@ namespace MediaBrowser.Api
|
|||
Text = request.Text
|
||||
};
|
||||
|
||||
var task = _sessionManager.SendMessageCommand(GetSession(_sessionManager).Id, request.Id, command, CancellationToken.None);
|
||||
var task = _sessionManager.SendMessageCommand(GetSession().Id, request.Id, command, CancellationToken.None);
|
||||
|
||||
Task.WaitAll(task);
|
||||
}
|
||||
|
@ -364,14 +364,14 @@ namespace MediaBrowser.Api
|
|||
StartPositionTicks = request.StartPositionTicks
|
||||
};
|
||||
|
||||
var task = _sessionManager.SendPlayCommand(GetSession(_sessionManager).Id, request.Id, command, CancellationToken.None);
|
||||
var task = _sessionManager.SendPlayCommand(GetSession().Id, request.Id, command, CancellationToken.None);
|
||||
|
||||
Task.WaitAll(task);
|
||||
}
|
||||
|
||||
public void Post(SendGeneralCommand request)
|
||||
{
|
||||
var currentSession = GetSession(_sessionManager);
|
||||
var currentSession = GetSession();
|
||||
|
||||
var command = new GeneralCommand
|
||||
{
|
||||
|
@ -386,7 +386,7 @@ namespace MediaBrowser.Api
|
|||
|
||||
public void Post(SendFullGeneralCommand request)
|
||||
{
|
||||
var currentSession = GetSession(_sessionManager);
|
||||
var currentSession = GetSession();
|
||||
|
||||
request.ControllingUserId = currentSession.UserId.HasValue ? currentSession.UserId.Value.ToString("N") : null;
|
||||
|
||||
|
@ -409,7 +409,7 @@ namespace MediaBrowser.Api
|
|||
{
|
||||
if (string.IsNullOrWhiteSpace(request.Id))
|
||||
{
|
||||
request.Id = GetSession(_sessionManager).Id;
|
||||
request.Id = GetSession().Id;
|
||||
}
|
||||
_sessionManager.ReportCapabilities(request.Id, new SessionCapabilities
|
||||
{
|
||||
|
|
|
@ -791,7 +791,7 @@ namespace MediaBrowser.Api.UserLibrary
|
|||
datePlayed = DateTime.ParseExact(request.DatePlayed, "yyyyMMddHHmmss", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal);
|
||||
}
|
||||
|
||||
var session = GetSession(_sessionManager);
|
||||
var session = GetSession();
|
||||
|
||||
var dto = await UpdatePlayedStatus(user, request.Id, true, datePlayed).ConfigureAwait(false);
|
||||
|
||||
|
@ -826,7 +826,7 @@ namespace MediaBrowser.Api.UserLibrary
|
|||
|
||||
public void Post(ReportPlaybackStart request)
|
||||
{
|
||||
request.SessionId = GetSession(_sessionManager).Id;
|
||||
request.SessionId = GetSession().Id;
|
||||
|
||||
var task = _sessionManager.OnPlaybackStart(request);
|
||||
|
||||
|
@ -854,7 +854,7 @@ namespace MediaBrowser.Api.UserLibrary
|
|||
|
||||
public void Post(ReportPlaybackProgress request)
|
||||
{
|
||||
request.SessionId = GetSession(_sessionManager).Id;
|
||||
request.SessionId = GetSession().Id;
|
||||
|
||||
var task = _sessionManager.OnPlaybackProgress(request);
|
||||
|
||||
|
@ -877,7 +877,7 @@ namespace MediaBrowser.Api.UserLibrary
|
|||
|
||||
public void Post(ReportPlaybackStopped request)
|
||||
{
|
||||
request.SessionId = GetSession(_sessionManager).Id;
|
||||
request.SessionId = GetSession().Id;
|
||||
|
||||
var task = _sessionManager.OnPlaybackStopped(request);
|
||||
|
||||
|
@ -899,7 +899,7 @@ namespace MediaBrowser.Api.UserLibrary
|
|||
{
|
||||
var user = _userManager.GetUserById(request.UserId);
|
||||
|
||||
var session = GetSession(_sessionManager);
|
||||
var session = GetSession();
|
||||
|
||||
var dto = await UpdatePlayedStatus(user, request.Id, false, null).ConfigureAwait(false);
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Controller.Dto;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.Net;
|
||||
using MediaBrowser.Controller.Session;
|
||||
using MediaBrowser.Model.Dto;
|
||||
using MediaBrowser.Model.Serialization;
|
||||
|
@ -152,7 +153,7 @@ namespace MediaBrowser.Api
|
|||
/// <summary>
|
||||
/// Class UsersService
|
||||
/// </summary>
|
||||
public class UserService : BaseApiService
|
||||
public class UserService : BaseApiService, IHasAuthorization
|
||||
{
|
||||
/// <summary>
|
||||
/// The _XML serializer
|
||||
|
@ -166,6 +167,8 @@ namespace MediaBrowser.Api
|
|||
private readonly IDtoService _dtoService;
|
||||
private readonly ISessionManager _sessionMananger;
|
||||
|
||||
public IAuthorizationContext AuthorizationContext { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UserService" /> class.
|
||||
/// </summary>
|
||||
|
@ -295,7 +298,7 @@ namespace MediaBrowser.Api
|
|||
throw new ResourceNotFoundException("User not found");
|
||||
}
|
||||
|
||||
var auth = AuthorizationRequestFilterAttribute.GetAuthorization(Request);
|
||||
var auth = AuthorizationContext.GetAuthorizationInfo(Request);
|
||||
|
||||
// Login in the old way if the header is missing
|
||||
if (string.IsNullOrEmpty(auth.Client) ||
|
||||
|
|
|
@ -780,7 +780,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
|
||||
var list = new List<BaseItem>();
|
||||
|
||||
var hasLinkedChildren = AddChildrenToList(user, includeLinkedChildren, list, false, null);
|
||||
var hasLinkedChildren = AddChildrenToList(user, includeLinkedChildren, list, false);
|
||||
|
||||
return hasLinkedChildren ? list.DistinctBy(i => i.Id).ToList() : list;
|
||||
}
|
||||
|
@ -797,9 +797,8 @@ namespace MediaBrowser.Controller.Entities
|
|||
/// <param name="includeLinkedChildren">if set to <c>true</c> [include linked children].</param>
|
||||
/// <param name="list">The list.</param>
|
||||
/// <param name="recursive">if set to <c>true</c> [recursive].</param>
|
||||
/// <param name="filter">The filter.</param>
|
||||
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
|
||||
private bool AddChildrenToList(User user, bool includeLinkedChildren, List<BaseItem> list, bool recursive, Func<BaseItem, bool> filter)
|
||||
private bool AddChildrenToList(User user, bool includeLinkedChildren, List<BaseItem> list, bool recursive)
|
||||
{
|
||||
var hasLinkedChildren = false;
|
||||
|
||||
|
@ -807,19 +806,16 @@ namespace MediaBrowser.Controller.Entities
|
|||
{
|
||||
if (child.IsVisible(user))
|
||||
{
|
||||
if (filter == null || filter(child))
|
||||
if (!child.IsHiddenFromUser(user))
|
||||
{
|
||||
if (!child.IsHiddenFromUser(user))
|
||||
{
|
||||
list.Add(child);
|
||||
}
|
||||
list.Add(child);
|
||||
}
|
||||
|
||||
if (recursive && child.IsFolder)
|
||||
{
|
||||
var folder = (Folder)child;
|
||||
|
||||
if (folder.AddChildrenToList(user, includeLinkedChildren, list, true, filter))
|
||||
if (folder.AddChildrenToList(user, includeLinkedChildren, list, true))
|
||||
{
|
||||
hasLinkedChildren = true;
|
||||
}
|
||||
|
@ -831,11 +827,6 @@ namespace MediaBrowser.Controller.Entities
|
|||
{
|
||||
foreach (var child in GetLinkedChildren())
|
||||
{
|
||||
if (filter != null && !filter(child))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (child.IsVisible(user))
|
||||
{
|
||||
hasLinkedChildren = true;
|
||||
|
@ -864,7 +855,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
|
||||
var list = new List<BaseItem>();
|
||||
|
||||
var hasLinkedChildren = AddChildrenToList(user, includeLinkedChildren, list, true, null);
|
||||
var hasLinkedChildren = AddChildrenToList(user, includeLinkedChildren, list, true);
|
||||
|
||||
return hasLinkedChildren ? list.DistinctBy(i => i.Id).ToList() : list;
|
||||
}
|
||||
|
|
|
@ -195,10 +195,18 @@
|
|||
<Compile Include="MediaEncoding\ISubtitleEncoder.cs" />
|
||||
<Compile Include="MediaEncoding\MediaStreamSelector.cs" />
|
||||
<Compile Include="MediaEncoding\VideoEncodingOptions.cs" />
|
||||
<Compile Include="Net\AuthenticatedAttribute.cs" />
|
||||
<Compile Include="Net\AuthorizationInfo.cs" />
|
||||
<Compile Include="Net\IAuthorizationContext.cs" />
|
||||
<Compile Include="Net\IAuthService.cs" />
|
||||
<Compile Include="Net\IHasAuthorization.cs" />
|
||||
<Compile Include="Net\IHasResultFactory.cs" />
|
||||
<Compile Include="Net\IHasSession.cs" />
|
||||
<Compile Include="Net\IHttpResultFactory.cs" />
|
||||
<Compile Include="Net\IHttpServer.cs" />
|
||||
<Compile Include="Net\IRestfulService.cs" />
|
||||
<Compile Include="Net\ISessionContext.cs" />
|
||||
<Compile Include="Net\LoggedAttribute.cs" />
|
||||
<Compile Include="News\INewsService.cs" />
|
||||
<Compile Include="Notifications\INotificationManager.cs" />
|
||||
<Compile Include="Notifications\INotificationService.cs" />
|
||||
|
|
41
MediaBrowser.Controller/Net/AuthenticatedAttribute.cs
Normal file
41
MediaBrowser.Controller/Net/AuthenticatedAttribute.cs
Normal file
|
@ -0,0 +1,41 @@
|
|||
using ServiceStack.Web;
|
||||
using System;
|
||||
|
||||
namespace MediaBrowser.Controller.Net
|
||||
{
|
||||
public class AuthenticatedAttribute : Attribute, IHasRequestFilter
|
||||
{
|
||||
public IAuthService AuthService { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The request filter is executed before the service.
|
||||
/// </summary>
|
||||
/// <param name="request">The http request wrapper</param>
|
||||
/// <param name="response">The http response wrapper</param>
|
||||
/// <param name="requestDto">The request DTO</param>
|
||||
public void RequestFilter(IRequest request, IResponse response, object requestDto)
|
||||
{
|
||||
AuthService.Authenticate(request, response, requestDto);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A new shallow copy of this filter is used on every request.
|
||||
/// </summary>
|
||||
/// <returns>IHasRequestFilter.</returns>
|
||||
public IHasRequestFilter Copy()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Order in which Request Filters are executed.
|
||||
/// <0 Executed before global request filters
|
||||
/// >0 Executed after global request filters
|
||||
/// </summary>
|
||||
/// <value>The priority.</value>
|
||||
public int Priority
|
||||
{
|
||||
get { return 0; }
|
||||
}
|
||||
}
|
||||
}
|
32
MediaBrowser.Controller/Net/AuthorizationInfo.cs
Normal file
32
MediaBrowser.Controller/Net/AuthorizationInfo.cs
Normal file
|
@ -0,0 +1,32 @@
|
|||
|
||||
namespace MediaBrowser.Controller.Net
|
||||
{
|
||||
public class AuthorizationInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the user identifier.
|
||||
/// </summary>
|
||||
/// <value>The user identifier.</value>
|
||||
public string UserId { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the device identifier.
|
||||
/// </summary>
|
||||
/// <value>The device identifier.</value>
|
||||
public string DeviceId { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the device.
|
||||
/// </summary>
|
||||
/// <value>The device.</value>
|
||||
public string Device { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the client.
|
||||
/// </summary>
|
||||
/// <value>The client.</value>
|
||||
public string Client { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the version.
|
||||
/// </summary>
|
||||
/// <value>The version.</value>
|
||||
public string Version { get; set; }
|
||||
}
|
||||
}
|
9
MediaBrowser.Controller/Net/IAuthService.cs
Normal file
9
MediaBrowser.Controller/Net/IAuthService.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
using ServiceStack.Web;
|
||||
|
||||
namespace MediaBrowser.Controller.Net
|
||||
{
|
||||
public interface IAuthService
|
||||
{
|
||||
void Authenticate(IRequest request, IResponse response, object requestDto);
|
||||
}
|
||||
}
|
14
MediaBrowser.Controller/Net/IAuthorizationContext.cs
Normal file
14
MediaBrowser.Controller/Net/IAuthorizationContext.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
using ServiceStack.Web;
|
||||
|
||||
namespace MediaBrowser.Controller.Net
|
||||
{
|
||||
public interface IAuthorizationContext
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the authorization information.
|
||||
/// </summary>
|
||||
/// <param name="requestContext">The request context.</param>
|
||||
/// <returns>AuthorizationInfo.</returns>
|
||||
AuthorizationInfo GetAuthorizationInfo(IRequest requestContext);
|
||||
}
|
||||
}
|
12
MediaBrowser.Controller/Net/IHasAuthorization.cs
Normal file
12
MediaBrowser.Controller/Net/IHasAuthorization.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
|
||||
namespace MediaBrowser.Controller.Net
|
||||
{
|
||||
public interface IHasAuthorization
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the authorization context.
|
||||
/// </summary>
|
||||
/// <value>The authorization context.</value>
|
||||
IAuthorizationContext AuthorizationContext { get; set; }
|
||||
}
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
using MediaBrowser.Common.Net;
|
||||
using ServiceStack.Web;
|
||||
using ServiceStack.Web;
|
||||
|
||||
namespace MediaBrowser.Controller.Net
|
||||
{
|
||||
|
|
12
MediaBrowser.Controller/Net/IHasSession.cs
Normal file
12
MediaBrowser.Controller/Net/IHasSession.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
|
||||
namespace MediaBrowser.Controller.Net
|
||||
{
|
||||
public interface IHasSession
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the session context.
|
||||
/// </summary>
|
||||
/// <value>The session context.</value>
|
||||
ISessionContext SessionContext { get; set; }
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@ namespace MediaBrowser.Controller.Net
|
|||
/// <summary>
|
||||
/// Interface IRestfulService
|
||||
/// </summary>
|
||||
[Logged]
|
||||
public interface IRestfulService : IService
|
||||
{
|
||||
}
|
||||
|
|
13
MediaBrowser.Controller/Net/ISessionContext.cs
Normal file
13
MediaBrowser.Controller/Net/ISessionContext.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
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);
|
||||
}
|
||||
}
|
73
MediaBrowser.Controller/Net/LoggedAttribute.cs
Normal file
73
MediaBrowser.Controller/Net/LoggedAttribute.cs
Normal file
|
@ -0,0 +1,73 @@
|
|||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.Session;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using ServiceStack.Web;
|
||||
using System;
|
||||
|
||||
namespace MediaBrowser.Controller.Net
|
||||
{
|
||||
public class LoggedAttribute : Attribute, IHasRequestFilter
|
||||
{
|
||||
public ILogger Logger { get; set; }
|
||||
public IUserManager UserManager { get; set; }
|
||||
public ISessionManager SessionManager { get; set; }
|
||||
public IAuthorizationContext AuthorizationContext { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The request filter is executed before the service.
|
||||
/// </summary>
|
||||
/// <param name="request">The http request wrapper</param>
|
||||
/// <param name="response">The http response wrapper</param>
|
||||
/// <param name="requestDto">The request DTO</param>
|
||||
public void RequestFilter(IRequest request, IResponse response, object requestDto)
|
||||
{
|
||||
//This code is executed before the service
|
||||
var auth = AuthorizationContext.GetAuthorizationInfo(request);
|
||||
|
||||
if (auth != null)
|
||||
{
|
||||
User user = null;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(auth.UserId))
|
||||
{
|
||||
var userId = auth.UserId;
|
||||
|
||||
user = UserManager.GetUserById(new Guid(userId));
|
||||
}
|
||||
|
||||
string deviceId = auth.DeviceId;
|
||||
string device = auth.Device;
|
||||
string client = auth.Client;
|
||||
string version = auth.Version;
|
||||
|
||||
if (!string.IsNullOrEmpty(client) && !string.IsNullOrEmpty(deviceId) && !string.IsNullOrEmpty(device) && !string.IsNullOrEmpty(version))
|
||||
{
|
||||
var remoteEndPoint = request.RemoteIp;
|
||||
|
||||
SessionManager.LogSessionActivity(client, version, deviceId, device, remoteEndPoint, user);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A new shallow copy of this filter is used on every request.
|
||||
/// </summary>
|
||||
/// <returns>IHasRequestFilter.</returns>
|
||||
public IHasRequestFilter Copy()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Order in which Request Filters are executed.
|
||||
/// <0 Executed before global request filters
|
||||
/// >0 Executed after global request filters
|
||||
/// </summary>
|
||||
/// <value>The priority.</value>
|
||||
public int Priority
|
||||
{
|
||||
get { return 0; }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -340,13 +340,17 @@ namespace MediaBrowser.Providers.Manager
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.ErrorException("Error in {0}", ex, provider.Name);
|
||||
|
||||
// If a local provider fails, consider that a failure
|
||||
refreshResult.Status = ProviderRefreshStatus.Failure;
|
||||
refreshResult.ErrorMessage = ex.Message;
|
||||
Logger.ErrorException("Error in {0}", ex, provider.Name);
|
||||
|
||||
// If the local provider fails don't continue with remote providers because the user's saved metadata could be lost
|
||||
return refreshResult;
|
||||
if (options.MetadataRefreshMode != MetadataRefreshMode.FullRefresh)
|
||||
{
|
||||
// If the local provider fails don't continue with remote providers because the user's saved metadata could be lost
|
||||
return refreshResult;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -12,12 +12,8 @@ namespace MediaBrowser.Server.Implementations.Collections
|
|||
|
||||
public override bool IsVisible(User user)
|
||||
{
|
||||
if (!GetChildren(user, true).Any())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return base.IsVisible(user);
|
||||
return GetChildren(user, true).Any() &&
|
||||
base.IsVisible(user);
|
||||
}
|
||||
|
||||
public override bool IsHidden
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
using System.Net.Sockets;
|
||||
using System.Runtime.Serialization;
|
||||
using Funq;
|
||||
using Funq;
|
||||
using MediaBrowser.Common;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Controller.Net;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using MediaBrowser.Server.Implementations.HttpServer.Security;
|
||||
using ServiceStack;
|
||||
using ServiceStack.Api.Swagger;
|
||||
using ServiceStack.Auth;
|
||||
using ServiceStack.Host;
|
||||
using ServiceStack.Host.Handlers;
|
||||
using ServiceStack.Host.HttpListener;
|
||||
|
@ -27,7 +27,6 @@ namespace MediaBrowser.Server.Implementations.HttpServer
|
|||
{
|
||||
public class HttpListenerHost : ServiceStackHost, IHttpServer
|
||||
{
|
||||
private string ServerName { get; set; }
|
||||
private string HandlerPath { get; set; }
|
||||
private string DefaultRedirectPath { get; set; }
|
||||
|
||||
|
@ -59,7 +58,6 @@ namespace MediaBrowser.Server.Implementations.HttpServer
|
|||
: base(serviceName, assembliesWithServices)
|
||||
{
|
||||
DefaultRedirectPath = defaultRedirectPath;
|
||||
ServerName = serviceName;
|
||||
HandlerPath = handlerPath;
|
||||
|
||||
_logger = logManager.GetLogger("HttpServer");
|
||||
|
@ -95,7 +93,12 @@ namespace MediaBrowser.Server.Implementations.HttpServer
|
|||
container.Adapter = _containerAdapter;
|
||||
|
||||
Plugins.Add(new SwaggerFeature());
|
||||
Plugins.Add(new CorsFeature(allowedHeaders: "Content-Type, Authorization"));
|
||||
Plugins.Add(new CorsFeature(allowedHeaders: "Content-Type, Authorization"));
|
||||
|
||||
Plugins.Add(new AuthFeature(() => new AuthUserSession(), new IAuthProvider[] {
|
||||
new SessionAuthProvider(_containerAdapter.Resolve<ISessionContext>()),
|
||||
}));
|
||||
|
||||
HostContext.GlobalResponseFilters.Add(new ResponseFilter(_logger).FilterResponse);
|
||||
}
|
||||
|
||||
|
@ -112,7 +115,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer
|
|||
|
||||
Config.HandlerFactoryPath = string.IsNullOrEmpty(HandlerPath)
|
||||
? null
|
||||
: HandlerPath;
|
||||
: "/" + HandlerPath;
|
||||
|
||||
Config.MetadataRedirectPath = string.IsNullOrEmpty(HandlerPath)
|
||||
? "metadata"
|
||||
|
@ -161,8 +164,6 @@ namespace MediaBrowser.Server.Implementations.HttpServer
|
|||
if (Listener == null)
|
||||
Listener = new HttpListener();
|
||||
|
||||
HostContext.Config.HandlerFactoryPath = ListenerRequest.GetHandlerPathIfAny(UrlPrefixes.First());
|
||||
|
||||
foreach (var prefix in UrlPrefixes)
|
||||
{
|
||||
_logger.Info("Adding HttpListener prefix " + prefix);
|
||||
|
@ -172,6 +173,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer
|
|||
IsStarted = true;
|
||||
_logger.Info("Starting HttpListner");
|
||||
Listener.Start();
|
||||
_logger.Info("HttpListener started");
|
||||
|
||||
for (var i = 0; i < _autoResetEvents.Count; i++)
|
||||
{
|
||||
|
@ -263,27 +265,27 @@ namespace MediaBrowser.Server.Implementations.HttpServer
|
|||
|
||||
var localPath = request.Url.LocalPath;
|
||||
|
||||
if (string.Equals(localPath, "/mediabrowser/", StringComparison.OrdinalIgnoreCase))
|
||||
if (string.Equals(localPath, "/" + HandlerPath + "/", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
context.Response.Redirect(DefaultRedirectPath);
|
||||
context.Response.Close();
|
||||
return;
|
||||
}
|
||||
if (string.Equals(localPath, "/mediabrowser", StringComparison.OrdinalIgnoreCase))
|
||||
if (string.Equals(localPath, "/" + HandlerPath, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
context.Response.Redirect("mediabrowser/" + DefaultRedirectPath);
|
||||
context.Response.Redirect(HandlerPath + "/" + DefaultRedirectPath);
|
||||
context.Response.Close();
|
||||
return;
|
||||
}
|
||||
if (string.Equals(localPath, "/", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
context.Response.Redirect("mediabrowser/" + DefaultRedirectPath);
|
||||
context.Response.Redirect(HandlerPath + "/" + DefaultRedirectPath);
|
||||
context.Response.Close();
|
||||
return;
|
||||
}
|
||||
if (string.IsNullOrEmpty(localPath))
|
||||
{
|
||||
context.Response.Redirect("/mediabrowser/" + DefaultRedirectPath);
|
||||
context.Response.Redirect("/" + HandlerPath + "/" + DefaultRedirectPath);
|
||||
context.Response.Close();
|
||||
return;
|
||||
}
|
||||
|
@ -410,6 +412,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer
|
|||
{
|
||||
var req = new ListenerRequest(httpContext, operationName, RequestAttributes.None);
|
||||
req.RequestAttributes = req.GetAttributes();
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
|
@ -442,7 +445,10 @@ namespace MediaBrowser.Server.Implementations.HttpServer
|
|||
|
||||
var httpReq = GetRequest(context, operationName);
|
||||
var httpRes = httpReq.Response;
|
||||
//var pathInfo = httpReq.PathInfo;
|
||||
|
||||
var handler = HttpHandlerFactory.GetHandler(httpReq);
|
||||
//var handler = HttpHandlerFactory.GetHandlerForPathInfo(httpReq.HttpMethod, pathInfo, pathInfo, httpReq.GetPhysicalPath());
|
||||
|
||||
var serviceStackHandler = handler as IServiceStackHandler;
|
||||
if (serviceStackHandler != null)
|
||||
|
|
|
@ -228,5 +228,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer
|
|||
}
|
||||
|
||||
public string StatusDescription { get; set; }
|
||||
|
||||
public int PaddingLength { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,113 @@
|
|||
using MediaBrowser.Controller.Net;
|
||||
using ServiceStack;
|
||||
using ServiceStack.Auth;
|
||||
using ServiceStack.Web;
|
||||
using System;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
|
||||
namespace MediaBrowser.Server.Implementations.HttpServer.Security
|
||||
{
|
||||
public class AuthService : IAuthService
|
||||
{
|
||||
/// <summary>
|
||||
/// Restrict authentication to a specific <see cref="IAuthProvider"/>.
|
||||
/// For example, if this attribute should only permit access
|
||||
/// if the user is authenticated with <see cref="BasicAuthProvider"/>,
|
||||
/// you should set this property to <see cref="BasicAuthProvider.Name"/>.
|
||||
/// </summary>
|
||||
public string Provider { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Redirect the client to a specific URL if authentication failed.
|
||||
/// If this property is null, simply `401 Unauthorized` is returned.
|
||||
/// </summary>
|
||||
public string HtmlRedirect { get; set; }
|
||||
|
||||
public void Authenticate(IRequest req, IResponse res, object requestDto)
|
||||
{
|
||||
if (HostContext.HasValidAuthSecret(req))
|
||||
return;
|
||||
|
||||
ExecuteBasic(req, res, requestDto); //first check if session is authenticated
|
||||
if (res.IsClosed) return; //AuthenticateAttribute already closed the request (ie auth failed)
|
||||
|
||||
ValidateUser(req);
|
||||
}
|
||||
|
||||
private void ValidateUser(IRequest req)
|
||||
{
|
||||
var user = req.TryResolve<ISessionContext>().GetUser(req);
|
||||
|
||||
if (user == null || user.Configuration.IsDisabled)
|
||||
{
|
||||
throw new UnauthorizedAccessException("Unauthorized access.");
|
||||
}
|
||||
}
|
||||
|
||||
private void ExecuteBasic(IRequest req, IResponse res, object requestDto)
|
||||
{
|
||||
if (AuthenticateService.AuthProviders == null)
|
||||
throw new InvalidOperationException(
|
||||
"The AuthService must be initialized by calling AuthService.Init to use an authenticate attribute");
|
||||
|
||||
var matchingOAuthConfigs = AuthenticateService.AuthProviders.Where(x =>
|
||||
this.Provider.IsNullOrEmpty()
|
||||
|| x.Provider == this.Provider).ToList();
|
||||
|
||||
if (matchingOAuthConfigs.Count == 0)
|
||||
{
|
||||
res.WriteError(req, requestDto, "No OAuth Configs found matching {0} provider"
|
||||
.Fmt(this.Provider ?? "any"));
|
||||
res.EndRequest();
|
||||
}
|
||||
|
||||
matchingOAuthConfigs.OfType<IAuthWithRequest>()
|
||||
.Each(x => x.PreAuthenticate(req, res));
|
||||
|
||||
var session = req.GetSession();
|
||||
if (session == null || !matchingOAuthConfigs.Any(x => session.IsAuthorized(x.Provider)))
|
||||
{
|
||||
if (this.DoHtmlRedirectIfConfigured(req, res, true)) return;
|
||||
|
||||
AuthProvider.HandleFailedAuth(matchingOAuthConfigs[0], session, req, res);
|
||||
}
|
||||
}
|
||||
|
||||
protected bool DoHtmlRedirectIfConfigured(IRequest req, IResponse res, bool includeRedirectParam = false)
|
||||
{
|
||||
var htmlRedirect = this.HtmlRedirect ?? AuthenticateService.HtmlRedirect;
|
||||
if (htmlRedirect != null && req.ResponseContentType.MatchesContentType(MimeTypes.Html))
|
||||
{
|
||||
DoHtmlRedirect(htmlRedirect, req, res, includeRedirectParam);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void DoHtmlRedirect(string redirectUrl, IRequest req, IResponse res, bool includeRedirectParam)
|
||||
{
|
||||
var url = req.ResolveAbsoluteUrl(redirectUrl);
|
||||
if (includeRedirectParam)
|
||||
{
|
||||
var absoluteRequestPath = req.ResolveAbsoluteUrl("~" + req.PathInfo + ToQueryString(req.QueryString));
|
||||
url = url.AddQueryParam(HostContext.ResolveLocalizedString(LocalizedStrings.Redirect), absoluteRequestPath);
|
||||
}
|
||||
|
||||
res.RedirectToUrl(url);
|
||||
}
|
||||
|
||||
private static string ToQueryString(INameValueCollection queryStringCollection)
|
||||
{
|
||||
return ToQueryString((NameValueCollection)queryStringCollection.Original);
|
||||
}
|
||||
|
||||
private static string ToQueryString(NameValueCollection queryStringCollection)
|
||||
{
|
||||
if (queryStringCollection == null || queryStringCollection.Count == 0)
|
||||
return String.Empty;
|
||||
|
||||
return "?" + queryStringCollection.ToFormUrlEncoded();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using MediaBrowser.Controller.Net;
|
||||
using ServiceStack.Web;
|
||||
|
||||
namespace MediaBrowser.Server.Implementations.HttpServer.Security
|
||||
{
|
||||
public class AuthorizationContext : IAuthorizationContext
|
||||
{
|
||||
public AuthorizationInfo GetAuthorizationInfo(IRequest requestContext)
|
||||
{
|
||||
return GetAuthorization(requestContext);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the authorization.
|
||||
/// </summary>
|
||||
/// <param name="httpReq">The HTTP req.</param>
|
||||
/// <returns>Dictionary{System.StringSystem.String}.</returns>
|
||||
private static AuthorizationInfo GetAuthorization(IRequest httpReq)
|
||||
{
|
||||
var auth = GetAuthorizationDictionary(httpReq);
|
||||
|
||||
string userId = null;
|
||||
string deviceId = null;
|
||||
string device = null;
|
||||
string client = null;
|
||||
string version = null;
|
||||
|
||||
if (auth != null)
|
||||
{
|
||||
auth.TryGetValue("UserId", out userId);
|
||||
auth.TryGetValue("DeviceId", out deviceId);
|
||||
auth.TryGetValue("Device", out device);
|
||||
auth.TryGetValue("Client", out client);
|
||||
auth.TryGetValue("Version", out version);
|
||||
}
|
||||
|
||||
return new AuthorizationInfo
|
||||
{
|
||||
Client = client,
|
||||
Device = device,
|
||||
DeviceId = deviceId,
|
||||
UserId = userId,
|
||||
Version = version
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the auth.
|
||||
/// </summary>
|
||||
/// <param name="httpReq">The HTTP req.</param>
|
||||
/// <returns>Dictionary{System.StringSystem.String}.</returns>
|
||||
private static Dictionary<string, string> GetAuthorizationDictionary(IRequest httpReq)
|
||||
{
|
||||
var auth = httpReq.Headers["Authorization"];
|
||||
|
||||
return GetAuthorization(auth);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the authorization.
|
||||
/// </summary>
|
||||
/// <param name="authorizationHeader">The authorization header.</param>
|
||||
/// <returns>Dictionary{System.StringSystem.String}.</returns>
|
||||
private static Dictionary<string, string> GetAuthorization(string authorizationHeader)
|
||||
{
|
||||
if (authorizationHeader == null) return null;
|
||||
|
||||
var parts = authorizationHeader.Split(' ');
|
||||
|
||||
// There should be at least to parts
|
||||
if (parts.Length < 2) return null;
|
||||
|
||||
// It has to be a digest request
|
||||
if (!string.Equals(parts[0], "MediaBrowser", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Remove uptil the first space
|
||||
authorizationHeader = authorizationHeader.Substring(authorizationHeader.IndexOf(' '));
|
||||
parts = authorizationHeader.Split(',');
|
||||
|
||||
var result = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
foreach (var item in parts)
|
||||
{
|
||||
var param = item.Trim().Split(new[] { '=' }, 2);
|
||||
result.Add(param[0], param[1].Trim(new[] { '"' }));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
using MediaBrowser.Controller.Net;
|
||||
using ServiceStack;
|
||||
using ServiceStack.Auth;
|
||||
|
||||
namespace MediaBrowser.Server.Implementations.HttpServer.Security
|
||||
{
|
||||
public class SessionAuthProvider : CredentialsAuthProvider
|
||||
{
|
||||
private readonly ISessionContext _sessionContext;
|
||||
|
||||
public SessionAuthProvider(ISessionContext sessionContext)
|
||||
{
|
||||
_sessionContext = sessionContext;
|
||||
}
|
||||
|
||||
public override bool TryAuthenticate(IServiceBase authService, string userName, string password)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool IsAuthorized(IAuthSession session, IAuthTokens tokens, Authenticate request = null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void SaveUserAuth(IServiceBase authService, IAuthSession session, IAuthRepository authRepo, IAuthTokens tokens)
|
||||
{
|
||||
}
|
||||
|
||||
public override object Authenticate(IServiceBase authService, IAuthSession session, Authenticate request)
|
||||
{
|
||||
return base.Authenticate(authService, session, request);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.Net;
|
||||
using MediaBrowser.Controller.Session;
|
||||
using ServiceStack.Web;
|
||||
|
||||
namespace MediaBrowser.Server.Implementations.HttpServer.Security
|
||||
{
|
||||
public class SessionContext : ISessionContext
|
||||
{
|
||||
private readonly IUserManager _userManager;
|
||||
private readonly ISessionManager _sessionManager;
|
||||
private readonly IAuthorizationContext _authContext;
|
||||
|
||||
public SessionContext(IUserManager userManager, IAuthorizationContext authContext, ISessionManager sessionManager)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_authContext = authContext;
|
||||
_sessionManager = sessionManager;
|
||||
}
|
||||
|
||||
public SessionInfo GetSession(IRequest requestContext)
|
||||
{
|
||||
var authorization = _authContext.GetAuthorizationInfo(requestContext);
|
||||
|
||||
return _sessionManager.GetSession(authorization.DeviceId, authorization.Client, authorization.Version);
|
||||
}
|
||||
|
||||
public User GetUser(IRequest requestContext)
|
||||
{
|
||||
var session = GetSession(requestContext);
|
||||
|
||||
return session == null || !session.UserId.HasValue ? null : _userManager.GetUserById(session.UserId.Value);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -133,6 +133,7 @@
|
|||
<Compile Include="EntryPoints\ServerEventNotifier.cs" />
|
||||
<Compile Include="EntryPoints\UserDataChangeNotifier.cs" />
|
||||
<Compile Include="FileOrganization\OrganizerScheduledTask.cs" />
|
||||
<Compile Include="HttpServer\Security\AuthorizationContext.cs" />
|
||||
<Compile Include="HttpServer\ContainerAdapter.cs" />
|
||||
<Compile Include="HttpServer\GetSwaggerResource.cs" />
|
||||
<Compile Include="HttpServer\HttpListenerHost.cs" />
|
||||
|
@ -141,9 +142,12 @@
|
|||
<Compile Include="HttpServer\NativeWebSocket.cs" />
|
||||
<Compile Include="HttpServer\RangeRequestWriter.cs" />
|
||||
<Compile Include="HttpServer\ResponseFilter.cs" />
|
||||
<Compile Include="HttpServer\Security\AuthService.cs" />
|
||||
<Compile Include="HttpServer\Security\SessionAuthProvider.cs" />
|
||||
<Compile Include="HttpServer\ServerFactory.cs" />
|
||||
<Compile Include="HttpServer\ServerLogFactory.cs" />
|
||||
<Compile Include="HttpServer\ServerLogger.cs" />
|
||||
<Compile Include="HttpServer\Security\SessionContext.cs" />
|
||||
<Compile Include="HttpServer\StreamWriter.cs" />
|
||||
<Compile Include="HttpServer\SwaggerService.cs" />
|
||||
<Compile Include="Drawing\ImageProcessor.cs" />
|
||||
|
|
|
@ -62,6 +62,7 @@ using MediaBrowser.Server.Implementations.Dto;
|
|||
using MediaBrowser.Server.Implementations.EntryPoints;
|
||||
using MediaBrowser.Server.Implementations.FileOrganization;
|
||||
using MediaBrowser.Server.Implementations.HttpServer;
|
||||
using MediaBrowser.Server.Implementations.HttpServer.Security;
|
||||
using MediaBrowser.Server.Implementations.IO;
|
||||
using MediaBrowser.Server.Implementations.Library;
|
||||
using MediaBrowser.Server.Implementations.LiveTv;
|
||||
|
@ -598,7 +599,7 @@ namespace MediaBrowser.ServerApplication
|
|||
|
||||
RegisterSingleInstance<ISearchEngine>(() => new SearchEngine(LogManager, LibraryManager, UserManager));
|
||||
|
||||
HttpServer = ServerFactory.CreateServer(this, LogManager, "Media Browser", "mediabrowser", "dashboard/index.html");
|
||||
HttpServer = ServerFactory.CreateServer(this, LogManager, "Media Browser", WebApplicationName, "dashboard/index.html");
|
||||
RegisterSingleInstance(HttpServer, false);
|
||||
progress.Report(10);
|
||||
|
||||
|
@ -667,6 +668,11 @@ namespace MediaBrowser.ServerApplication
|
|||
MediaEncoder, ChapterManager);
|
||||
RegisterSingleInstance(EncodingManager);
|
||||
|
||||
var authContext = new AuthorizationContext();
|
||||
RegisterSingleInstance<IAuthorizationContext>(authContext);
|
||||
RegisterSingleInstance<ISessionContext>(new SessionContext(UserManager, authContext, SessionManager));
|
||||
RegisterSingleInstance<IAuthService>(new AuthService());
|
||||
|
||||
RegisterSingleInstance<ISubtitleEncoder>(new SubtitleEncoder(LibraryManager, LogManager.GetLogger("SubtitleEncoder"), ApplicationPaths, FileSystemManager, MediaEncoder));
|
||||
|
||||
var displayPreferencesTask = Task.Run(async () => await ConfigureDisplayPreferencesRepositories().ConfigureAwait(false));
|
||||
|
|
|
@ -25,7 +25,7 @@ namespace MediaBrowser.ServerApplication.Native
|
|||
public static void OpenDashboardPage(string page, User loggedInUser, IServerConfigurationManager configurationManager, IServerApplicationHost appHost, ILogger logger)
|
||||
{
|
||||
var url = "http://localhost:" + configurationManager.Configuration.HttpServerPortNumber + "/" +
|
||||
appHost.WebApplicationName + "/dashboard/" + page;
|
||||
appHost.WebApplicationName + "/web/" + page;
|
||||
|
||||
OpenUrl(url, logger);
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ namespace MediaBrowser.WebDashboard.Api
|
|||
/// Class GetDashboardConfigurationPages
|
||||
/// </summary>
|
||||
[Route("/dashboard/ConfigurationPages", "GET")]
|
||||
[Route("/web/ConfigurationPages", "GET")]
|
||||
public class GetDashboardConfigurationPages : IReturn<List<ConfigurationPageInfo>>
|
||||
{
|
||||
/// <summary>
|
||||
|
@ -38,6 +39,7 @@ namespace MediaBrowser.WebDashboard.Api
|
|||
/// Class GetDashboardConfigurationPage
|
||||
/// </summary>
|
||||
[Route("/dashboard/ConfigurationPage", "GET")]
|
||||
[Route("/web/ConfigurationPage", "GET")]
|
||||
public class GetDashboardConfigurationPage
|
||||
{
|
||||
/// <summary>
|
||||
|
@ -50,6 +52,7 @@ namespace MediaBrowser.WebDashboard.Api
|
|||
/// <summary>
|
||||
/// Class GetDashboardResource
|
||||
/// </summary>
|
||||
[Route("/web/{ResourceName*}", "GET")]
|
||||
[Route("/dashboard/{ResourceName*}", "GET")]
|
||||
public class GetDashboardResource
|
||||
{
|
||||
|
|
|
@ -28,7 +28,7 @@ namespace MediaBrowser.XbmcMetadata.Providers
|
|||
|
||||
protected override FileSystemInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService)
|
||||
{
|
||||
return directoryService.GetFile(Path.Combine(info.Path, "series.nfo"));
|
||||
return directoryService.GetFile(Path.Combine(info.Path, "tvshow.nfo"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user