diff options
Diffstat (limited to 'MediaBrowser.Server.Implementations/HttpServer/Security')
4 files changed, 280 insertions, 0 deletions
diff --git a/MediaBrowser.Server.Implementations/HttpServer/Security/AuthService.cs b/MediaBrowser.Server.Implementations/HttpServer/Security/AuthService.cs new file mode 100644 index 000000000..ddb583f5d --- /dev/null +++ b/MediaBrowser.Server.Implementations/HttpServer/Security/AuthService.cs @@ -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(); + } + } +} diff --git a/MediaBrowser.Server.Implementations/HttpServer/Security/AuthorizationContext.cs b/MediaBrowser.Server.Implementations/HttpServer/Security/AuthorizationContext.cs new file mode 100644 index 000000000..6ea77f251 --- /dev/null +++ b/MediaBrowser.Server.Implementations/HttpServer/Security/AuthorizationContext.cs @@ -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; + } + } +} diff --git a/MediaBrowser.Server.Implementations/HttpServer/Security/SessionAuthProvider.cs b/MediaBrowser.Server.Implementations/HttpServer/Security/SessionAuthProvider.cs new file mode 100644 index 000000000..7c3173101 --- /dev/null +++ b/MediaBrowser.Server.Implementations/HttpServer/Security/SessionAuthProvider.cs @@ -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); + } + } +} diff --git a/MediaBrowser.Server.Implementations/HttpServer/Security/SessionContext.cs b/MediaBrowser.Server.Implementations/HttpServer/Security/SessionContext.cs new file mode 100644 index 000000000..f67c643c8 --- /dev/null +++ b/MediaBrowser.Server.Implementations/HttpServer/Security/SessionContext.cs @@ -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); + } + } +} |
