aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Server.Implementations/HttpServer/Security
diff options
context:
space:
mode:
author7illusions <z@7illusions.com>2014-08-30 19:06:58 +0200
committer7illusions <z@7illusions.com>2014-08-30 19:06:58 +0200
commit66ad1699e22029b605e17735e8d9450285d8748a (patch)
treeffc92c88d24850b2f82b6b3a8bdd904a2ccc77a5 /MediaBrowser.Server.Implementations/HttpServer/Security
parent34bc54263e886aae777a3537dc50a6535b51330a (diff)
parent9d36f518182bc075c19d78084870f5115fa62d1e (diff)
Merge pull request #1 from MediaBrowser/master
Update to latest
Diffstat (limited to 'MediaBrowser.Server.Implementations/HttpServer/Security')
-rw-r--r--MediaBrowser.Server.Implementations/HttpServer/Security/AuthService.cs164
-rw-r--r--MediaBrowser.Server.Implementations/HttpServer/Security/AuthorizationContext.cs104
-rw-r--r--MediaBrowser.Server.Implementations/HttpServer/Security/SessionAuthProvider.cs35
-rw-r--r--MediaBrowser.Server.Implementations/HttpServer/Security/SessionContext.cs36
4 files changed, 339 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..19870c435
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/HttpServer/Security/AuthService.cs
@@ -0,0 +1,164 @@
+using System.Collections.Generic;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Net;
+using MediaBrowser.Controller.Session;
+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
+ {
+ private readonly IServerConfigurationManager _config;
+
+ public AuthService(IUserManager userManager, ISessionManager sessionManager, IAuthorizationContext authorizationContext, IServerConfigurationManager config)
+ {
+ AuthorizationContext = authorizationContext;
+ _config = config;
+ SessionManager = sessionManager;
+ UserManager = userManager;
+ }
+
+ public IUserManager UserManager { get; private set; }
+ public ISessionManager SessionManager { get; private set; }
+ public IAuthorizationContext AuthorizationContext { 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 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);
+ }
+
+ // TODO: Remove this when all clients have supported the new sescurity
+ private readonly List<string> _updatedClients = new List<string>(){"Dashboard"};
+
+ private void ValidateUser(IRequest req)
+ {
+ //This code is executed before the service
+ var auth = AuthorizationContext.GetAuthorizationInfo(req);
+
+ if (!string.IsNullOrWhiteSpace(auth.Token)
+ || _config.Configuration.EnableTokenAuthentication
+ || _updatedClients.Contains(auth.Client ?? string.Empty, StringComparer.OrdinalIgnoreCase))
+ {
+ SessionManager.ValidateSecurityToken(auth.Token);
+ }
+
+ var user = string.IsNullOrWhiteSpace(auth.UserId)
+ ? null
+ : UserManager.GetUserById(new Guid(auth.UserId));
+
+ if (user == null & !string.IsNullOrWhiteSpace(auth.UserId))
+ {
+ // TODO: Re-enable
+ //throw new ArgumentException("User with Id " + auth.UserId + " not found");
+ }
+
+ if (user != null && user.Configuration.IsDisabled)
+ {
+ throw new AuthenticationException("User account has been disabled.");
+ }
+
+ if (!string.IsNullOrWhiteSpace(auth.DeviceId) &&
+ !string.IsNullOrWhiteSpace(auth.Client) &&
+ !string.IsNullOrWhiteSpace(auth.Device))
+ {
+ SessionManager.LogSessionActivity(auth.Client,
+ auth.Version,
+ auth.DeviceId,
+ auth.Device,
+ req.RemoteIp,
+ user);
+ }
+ }
+
+ 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..94be37e95
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/HttpServer/Security/AuthorizationContext.cs
@@ -0,0 +1,104 @@
+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);
+ }
+
+ var token = httpReq.Headers["X-MediaBrowser-Token"];
+
+ if (string.IsNullOrWhiteSpace(token))
+ {
+ token = httpReq.QueryString["api_key"];
+ }
+
+ return new AuthorizationInfo
+ {
+ Client = client,
+ Device = device,
+ DeviceId = deviceId,
+ UserId = userId,
+ Version = version,
+ Token = token
+ };
+ }
+
+ /// <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);
+ }
+ }
+}