move classes

This commit is contained in:
Luke Pulverenti
2016-11-03 21:18:51 -04:00
parent 70e146a0b2
commit 46efa464d8
13 changed files with 22 additions and 22 deletions

View File

@@ -67,6 +67,11 @@
<Compile Include="FileOrganization\NameUtils.cs" />
<Compile Include="FileOrganization\OrganizerScheduledTask.cs" />
<Compile Include="FileOrganization\TvFolderOrganizer.cs" />
<Compile Include="HttpServer\IHttpListener.cs" />
<Compile Include="HttpServer\Security\AuthorizationContext.cs" />
<Compile Include="HttpServer\Security\AuthService.cs" />
<Compile Include="HttpServer\Security\SessionContext.cs" />
<Compile Include="HttpServer\StreamWriter.cs" />
<Compile Include="Images\BaseDynamicImageProvider.cs" />
<Compile Include="Intros\DefaultIntroProvider.cs" />
<Compile Include="IO\ThrottledStream.cs" />

View File

@@ -0,0 +1,46 @@
using MediaBrowser.Controller.Net;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using MediaBrowser.Model.Services;
namespace Emby.Server.Implementations.HttpServer
{
public interface IHttpListener : IDisposable
{
/// <summary>
/// Gets or sets the error handler.
/// </summary>
/// <value>The error handler.</value>
Action<Exception, IRequest> ErrorHandler { get; set; }
/// <summary>
/// Gets or sets the request handler.
/// </summary>
/// <value>The request handler.</value>
Func<IHttpRequest, Uri, Task> RequestHandler { get; set; }
/// <summary>
/// Gets or sets the web socket handler.
/// </summary>
/// <value>The web socket handler.</value>
Action<WebSocketConnectEventArgs> WebSocketConnected { get; set; }
/// <summary>
/// Gets or sets the web socket connecting.
/// </summary>
/// <value>The web socket connecting.</value>
Action<WebSocketConnectingEventArgs> WebSocketConnecting { get; set; }
/// <summary>
/// Starts this instance.
/// </summary>
/// <param name="urlPrefixes">The URL prefixes.</param>
void Start(IEnumerable<string> urlPrefixes);
/// <summary>
/// Stops this instance.
/// </summary>
void Stop();
}
}

View File

@@ -0,0 +1,246 @@
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Connect;
using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Security;
using MediaBrowser.Controller.Session;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Emby.Server.Implementations.HttpServer.Security
{
public class AuthService : IAuthService
{
private readonly IServerConfigurationManager _config;
public AuthService(IUserManager userManager, IAuthorizationContext authorizationContext, IServerConfigurationManager config, IConnectManager connectManager, ISessionManager sessionManager, IDeviceManager deviceManager)
{
AuthorizationContext = authorizationContext;
_config = config;
DeviceManager = deviceManager;
SessionManager = sessionManager;
ConnectManager = connectManager;
UserManager = userManager;
}
public IUserManager UserManager { get; private set; }
public IAuthorizationContext AuthorizationContext { get; private set; }
public IConnectManager ConnectManager { get; private set; }
public ISessionManager SessionManager { get; private set; }
public IDeviceManager DeviceManager { get; private 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(IServiceRequest request,
IAuthenticationAttributes authAttribtues)
{
ValidateUser(request, authAttribtues);
}
private void ValidateUser(IServiceRequest request,
IAuthenticationAttributes authAttribtues)
{
// This code is executed before the service
var auth = AuthorizationContext.GetAuthorizationInfo(request);
if (!IsExemptFromAuthenticationToken(auth, authAttribtues))
{
var valid = IsValidConnectKey(auth.Token);
if (!valid)
{
ValidateSecurityToken(request, auth.Token);
}
}
var user = string.IsNullOrWhiteSpace(auth.UserId)
? null
: UserManager.GetUserById(auth.UserId);
if (user == null & !string.IsNullOrWhiteSpace(auth.UserId))
{
throw new SecurityException("User with Id " + auth.UserId + " not found");
}
if (user != null)
{
ValidateUserAccess(user, request, authAttribtues, auth);
}
var info = GetTokenInfo(request);
if (!IsExemptFromRoles(auth, authAttribtues, info))
{
var roles = authAttribtues.GetRoles().ToList();
ValidateRoles(roles, user);
}
if (!string.IsNullOrWhiteSpace(auth.DeviceId) &&
!string.IsNullOrWhiteSpace(auth.Client) &&
!string.IsNullOrWhiteSpace(auth.Device))
{
SessionManager.LogSessionActivity(auth.Client,
auth.Version,
auth.DeviceId,
auth.Device,
request.RemoteIp,
user);
}
}
private void ValidateUserAccess(User user, IServiceRequest request,
IAuthenticationAttributes authAttribtues,
AuthorizationInfo auth)
{
if (user.Policy.IsDisabled)
{
throw new SecurityException("User account has been disabled.")
{
SecurityExceptionType = SecurityExceptionType.Unauthenticated
};
}
if (!user.Policy.IsAdministrator &&
!authAttribtues.EscapeParentalControl &&
!user.IsParentalScheduleAllowed())
{
request.AddResponseHeader("X-Application-Error-Code", "ParentalControl");
throw new SecurityException("This user account is not allowed access at this time.")
{
SecurityExceptionType = SecurityExceptionType.ParentalControl
};
}
if (!string.IsNullOrWhiteSpace(auth.DeviceId))
{
if (!DeviceManager.CanAccessDevice(user.Id.ToString("N"), auth.DeviceId))
{
throw new SecurityException("User is not allowed access from this device.")
{
SecurityExceptionType = SecurityExceptionType.ParentalControl
};
}
}
}
private bool IsExemptFromAuthenticationToken(AuthorizationInfo auth, IAuthenticationAttributes authAttribtues)
{
if (!_config.Configuration.IsStartupWizardCompleted && authAttribtues.AllowBeforeStartupWizard)
{
return true;
}
return false;
}
private bool IsExemptFromRoles(AuthorizationInfo auth, IAuthenticationAttributes authAttribtues, AuthenticationInfo tokenInfo)
{
if (!_config.Configuration.IsStartupWizardCompleted && authAttribtues.AllowBeforeStartupWizard)
{
return true;
}
if (string.IsNullOrWhiteSpace(auth.Token))
{
return true;
}
if (tokenInfo != null && string.IsNullOrWhiteSpace(tokenInfo.UserId))
{
return true;
}
return false;
}
private void ValidateRoles(List<string> roles, User user)
{
if (roles.Contains("admin", StringComparer.OrdinalIgnoreCase))
{
if (user == null || !user.Policy.IsAdministrator)
{
throw new SecurityException("User does not have admin access.")
{
SecurityExceptionType = SecurityExceptionType.Unauthenticated
};
}
}
if (roles.Contains("delete", StringComparer.OrdinalIgnoreCase))
{
if (user == null || !user.Policy.EnableContentDeletion)
{
throw new SecurityException("User does not have delete access.")
{
SecurityExceptionType = SecurityExceptionType.Unauthenticated
};
}
}
if (roles.Contains("download", StringComparer.OrdinalIgnoreCase))
{
if (user == null || !user.Policy.EnableContentDownloading)
{
throw new SecurityException("User does not have download access.")
{
SecurityExceptionType = SecurityExceptionType.Unauthenticated
};
}
}
}
private AuthenticationInfo GetTokenInfo(IServiceRequest request)
{
object info;
request.Items.TryGetValue("OriginalAuthenticationInfo", out info);
return info as AuthenticationInfo;
}
private bool IsValidConnectKey(string token)
{
if (string.IsNullOrEmpty(token))
{
return false;
}
return ConnectManager.IsAuthorizationTokenValid(token);
}
private void ValidateSecurityToken(IServiceRequest request, string token)
{
if (string.IsNullOrWhiteSpace(token))
{
throw new SecurityException("Access token is required.");
}
var info = GetTokenInfo(request);
if (info == null)
{
throw new SecurityException("Access token is invalid or expired.");
}
if (!info.IsActive)
{
throw new SecurityException("Access token has expired.");
}
//if (!string.IsNullOrWhiteSpace(info.UserId))
//{
// var user = _userManager.GetUserById(info.UserId);
// if (user == null || user.Configuration.IsDisabled)
// {
// throw new SecurityException("User account has been disabled.");
// }
//}
}
}
}

View File

@@ -0,0 +1,195 @@
using MediaBrowser.Controller.Connect;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Security;
using System;
using System.Collections.Generic;
using System.Linq;
using MediaBrowser.Model.Services;
namespace Emby.Server.Implementations.HttpServer.Security
{
public class AuthorizationContext : IAuthorizationContext
{
private readonly IAuthenticationRepository _authRepo;
private readonly IConnectManager _connectManager;
public AuthorizationContext(IAuthenticationRepository authRepo, IConnectManager connectManager)
{
_authRepo = authRepo;
_connectManager = connectManager;
}
public AuthorizationInfo GetAuthorizationInfo(object requestContext)
{
var req = new ServiceRequest((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);
}
/// <summary>
/// Gets the authorization.
/// </summary>
/// <param name="httpReq">The HTTP req.</param>
/// <returns>Dictionary{System.StringSystem.String}.</returns>
private AuthorizationInfo GetAuthorization(IServiceRequest httpReq)
{
var auth = GetAuthorizationDictionary(httpReq);
string deviceId = null;
string device = null;
string client = null;
string version = null;
if (auth != null)
{
auth.TryGetValue("DeviceId", out deviceId);
auth.TryGetValue("Device", out device);
auth.TryGetValue("Client", out client);
auth.TryGetValue("Version", out version);
}
var token = httpReq.Headers["X-Emby-Token"];
if (string.IsNullOrWhiteSpace(token))
{
token = httpReq.Headers["X-MediaBrowser-Token"];
}
if (string.IsNullOrWhiteSpace(token))
{
token = httpReq.QueryString["api_key"];
}
var info = new AuthorizationInfo
{
Client = client,
Device = device,
DeviceId = deviceId,
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;
// TODO: Remove these checks for IsNullOrWhiteSpace
if (string.IsNullOrWhiteSpace(info.Client))
{
info.Client = tokenInfo.AppName;
}
if (string.IsNullOrWhiteSpace(info.Device))
{
info.Device = tokenInfo.DeviceName;
}
if (string.IsNullOrWhiteSpace(info.DeviceId))
{
info.DeviceId = tokenInfo.DeviceId;
}
if (string.IsNullOrWhiteSpace(info.Version))
{
info.Version = tokenInfo.AppVersion;
}
}
else
{
var user = _connectManager.GetUserFromExchangeToken(token);
if (user != null)
{
info.UserId = user.Id.ToString("N");
}
}
httpReq.Items["OriginalAuthenticationInfo"] = tokenInfo;
}
httpReq.Items["AuthorizationInfo"] = info;
return info;
}
/// <summary>
/// Gets the auth.
/// </summary>
/// <param name="httpReq">The HTTP req.</param>
/// <returns>Dictionary{System.StringSystem.String}.</returns>
private Dictionary<string, string> GetAuthorizationDictionary(IServiceRequest httpReq)
{
var auth = httpReq.Headers["X-Emby-Authorization"];
if (string.IsNullOrWhiteSpace(auth))
{
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 Dictionary<string, string> GetAuthorization(string authorizationHeader)
{
if (authorizationHeader == null) return null;
var parts = authorizationHeader.Split(new[] { ' ' }, 2);
// 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 = parts[1];
parts = authorizationHeader.Split(',');
var result = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
foreach (var item in parts)
{
var param = item.Trim().Split(new[] { '=' }, 2);
if (param.Length == 2)
{
var value = NormalizeValue (param[1].Trim(new[] { '"' }));
result.Add(param[0], value);
}
}
return result;
}
private string NormalizeValue(string value)
{
if (string.IsNullOrWhiteSpace (value))
{
return value;
}
return System.Net.WebUtility.HtmlEncode(value);
}
}
}

View File

@@ -0,0 +1,67 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Security;
using MediaBrowser.Controller.Session;
using System.Threading.Tasks;
using MediaBrowser.Model.Services;
namespace Emby.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 Task<SessionInfo> GetSession(IServiceRequest requestContext)
{
var authorization = _authContext.GetAuthorizationInfo(requestContext);
//if (!string.IsNullOrWhiteSpace(authorization.Token))
//{
// var auth = GetTokenInfo(requestContext);
// if (auth != null)
// {
// return _sessionManager.GetSessionByAuthenticationToken(auth, authorization.DeviceId, requestContext.RemoteIp, authorization.Version);
// }
//}
var user = string.IsNullOrWhiteSpace(authorization.UserId) ? null : _userManager.GetUserById(authorization.UserId);
return _sessionManager.LogSessionActivity(authorization.Client, authorization.Version, authorization.DeviceId, authorization.Device, requestContext.RemoteIp, user);
}
private AuthenticationInfo GetTokenInfo(IServiceRequest request)
{
object info;
request.Items.TryGetValue("OriginalAuthenticationInfo", out info);
return info as AuthenticationInfo;
}
public Task<SessionInfo> GetSession(object requestContext)
{
var req = new ServiceRequest((IRequest)requestContext);
return GetSession(req);
}
public async Task<User> GetUser(IServiceRequest requestContext)
{
var session = await GetSession(requestContext).ConfigureAwait(false);
return session == null || !session.UserId.HasValue ? null : _userManager.GetUserById(session.UserId.Value);
}
public Task<User> GetUser(object requestContext)
{
var req = new ServiceRequest((IRequest)requestContext);
return GetUser(req);
}
}
}

View File

@@ -0,0 +1,127 @@
using MediaBrowser.Model.Logging;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.IO;
using MediaBrowser.Model.Services;
namespace Emby.Server.Implementations.HttpServer
{
/// <summary>
/// Class StreamWriter
/// </summary>
public class StreamWriter : IAsyncStreamWriter, IHasHeaders
{
private ILogger Logger { get; set; }
private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
/// <summary>
/// Gets or sets the source stream.
/// </summary>
/// <value>The source stream.</value>
private Stream SourceStream { get; set; }
/// <summary>
/// The _options
/// </summary>
private readonly IDictionary<string, string> _options = new Dictionary<string, string>();
/// <summary>
/// Gets the options.
/// </summary>
/// <value>The options.</value>
public IDictionary<string, string> Headers
{
get { return _options; }
}
public Action OnComplete { get; set; }
public Action OnError { get; set; }
private readonly byte[] _bytes;
/// <summary>
/// Initializes a new instance of the <see cref="StreamWriter" /> class.
/// </summary>
/// <param name="source">The source.</param>
/// <param name="contentType">Type of the content.</param>
/// <param name="logger">The logger.</param>
public StreamWriter(Stream source, string contentType, ILogger logger)
{
if (string.IsNullOrEmpty(contentType))
{
throw new ArgumentNullException("contentType");
}
SourceStream = source;
Logger = logger;
Headers["Content-Type"] = contentType;
if (source.CanSeek)
{
Headers["Content-Length"] = source.Length.ToString(UsCulture);
}
}
/// <summary>
/// Initializes a new instance of the <see cref="StreamWriter"/> class.
/// </summary>
/// <param name="source">The source.</param>
/// <param name="contentType">Type of the content.</param>
/// <param name="logger">The logger.</param>
public StreamWriter(byte[] source, string contentType, ILogger logger)
: this(new MemoryStream(source), contentType, logger)
{
if (string.IsNullOrEmpty(contentType))
{
throw new ArgumentNullException("contentType");
}
_bytes = source;
Logger = logger;
Headers["Content-Type"] = contentType;
Headers["Content-Length"] = source.Length.ToString(UsCulture);
}
public async Task WriteToAsync(Stream responseStream, CancellationToken cancellationToken)
{
try
{
if (_bytes != null)
{
await responseStream.WriteAsync(_bytes, 0, _bytes.Length);
}
else
{
using (var src = SourceStream)
{
await src.CopyToAsync(responseStream).ConfigureAwait(false);
}
}
}
catch (Exception ex)
{
Logger.ErrorException("Error streaming data", ex);
if (OnError != null)
{
OnError();
}
throw;
}
finally
{
if (OnComplete != null)
{
OnComplete();
}
}
}
}
}