diff options
Diffstat (limited to 'MediaBrowser.Server.Implementations/HttpServer')
4 files changed, 150 insertions, 83 deletions
diff --git a/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs b/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs index a750dd82e..56e2e5247 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 dde61079b..e9a8cc752 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; } /// <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 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<string> 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 3cc703bbf..4e9899301 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 /// </summary> /// <param name="httpReq">The HTTP req.</param> /// <returns>Dictionary{System.StringSystem.String}.</returns> - 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; } /// <summary> @@ -100,7 +143,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer.Security /// </summary> /// <param name="httpReq">The HTTP req.</param> /// <returns>Dictionary{System.StringSystem.String}.</returns> - private Dictionary<string, string> GetAuthorizationDictionary(IRequest httpReq) + private Dictionary<string, string> 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 f67c643c8..9d1ddb7fc 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); + } } } |
