aboutsummaryrefslogtreecommitdiff
path: root/Emby.Server.Implementations/HttpServer
diff options
context:
space:
mode:
Diffstat (limited to 'Emby.Server.Implementations/HttpServer')
-rw-r--r--Emby.Server.Implementations/HttpServer/ResponseFilter.cs1
-rw-r--r--Emby.Server.Implementations/HttpServer/Security/AuthService.cs65
-rw-r--r--Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs115
-rw-r--r--Emby.Server.Implementations/HttpServer/Security/SessionContext.cs2
-rw-r--r--Emby.Server.Implementations/HttpServer/WebSocketConnection.cs10
5 files changed, 121 insertions, 72 deletions
diff --git a/Emby.Server.Implementations/HttpServer/ResponseFilter.cs b/Emby.Server.Implementations/HttpServer/ResponseFilter.cs
index 85c3db9b2..3ab5dbc16 100644
--- a/Emby.Server.Implementations/HttpServer/ResponseFilter.cs
+++ b/Emby.Server.Implementations/HttpServer/ResponseFilter.cs
@@ -1,5 +1,4 @@
using System;
-using System.Collections.Generic;
using System.Globalization;
using System.Text;
using MediaBrowser.Controller.Net;
diff --git a/Emby.Server.Implementations/HttpServer/Security/AuthService.cs b/Emby.Server.Implementations/HttpServer/Security/AuthService.cs
index 07e199d32..6a2d8fdbb 100644
--- a/Emby.Server.Implementations/HttpServer/Security/AuthService.cs
+++ b/Emby.Server.Implementations/HttpServer/Security/AuthService.cs
@@ -3,10 +3,11 @@
using System;
using System.Linq;
using Emby.Server.Implementations.SocketSharp;
+using Jellyfin.Data.Entities;
+using Jellyfin.Data.Enums;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Authentication;
using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Security;
using MediaBrowser.Controller.Session;
@@ -38,9 +39,9 @@ namespace Emby.Server.Implementations.HttpServer.Security
_networkManager = networkManager;
}
- public void Authenticate(IRequest request, IAuthenticationAttributes authAttribtues)
+ public void Authenticate(IRequest request, IAuthenticationAttributes authAttributes)
{
- ValidateUser(request, authAttribtues);
+ ValidateUser(request, authAttributes);
}
public User Authenticate(HttpRequest request, IAuthenticationAttributes authAttributes)
@@ -50,17 +51,33 @@ namespace Emby.Server.Implementations.HttpServer.Security
return user;
}
- private User ValidateUser(IRequest request, IAuthenticationAttributes authAttribtues)
+ public AuthorizationInfo Authenticate(HttpRequest request)
+ {
+ var auth = _authorizationContext.GetAuthorizationInfo(request);
+ if (auth?.User == null)
+ {
+ return null;
+ }
+
+ if (auth.User.HasPermission(PermissionKind.IsDisabled))
+ {
+ throw new SecurityException("User account has been disabled.");
+ }
+
+ return auth;
+ }
+
+ private User ValidateUser(IRequest request, IAuthenticationAttributes authAttributes)
{
// This code is executed before the service
var auth = _authorizationContext.GetAuthorizationInfo(request);
- if (!IsExemptFromAuthenticationToken(authAttribtues, request))
+ if (!IsExemptFromAuthenticationToken(authAttributes, request))
{
ValidateSecurityToken(request, auth.Token);
}
- if (authAttribtues.AllowLocalOnly && !request.IsLocal)
+ if (authAttributes.AllowLocalOnly && !request.IsLocal)
{
throw new SecurityException("Operation not found.");
}
@@ -74,14 +91,14 @@ namespace Emby.Server.Implementations.HttpServer.Security
if (user != null)
{
- ValidateUserAccess(user, request, authAttribtues, auth);
+ ValidateUserAccess(user, request, authAttributes);
}
var info = GetTokenInfo(request);
- if (!IsExemptFromRoles(auth, authAttribtues, request, info))
+ if (!IsExemptFromRoles(auth, authAttributes, request, info))
{
- var roles = authAttribtues.GetRoles();
+ var roles = authAttributes.GetRoles();
ValidateRoles(roles, user);
}
@@ -90,7 +107,8 @@ namespace Emby.Server.Implementations.HttpServer.Security
!string.IsNullOrEmpty(auth.Client) &&
!string.IsNullOrEmpty(auth.Device))
{
- _sessionManager.LogSessionActivity(auth.Client,
+ _sessionManager.LogSessionActivity(
+ auth.Client,
auth.Version,
auth.DeviceId,
auth.Device,
@@ -104,21 +122,20 @@ namespace Emby.Server.Implementations.HttpServer.Security
private void ValidateUserAccess(
User user,
IRequest request,
- IAuthenticationAttributes authAttribtues,
- AuthorizationInfo auth)
+ IAuthenticationAttributes authAttributes)
{
- if (user.Policy.IsDisabled)
+ if (user.HasPermission(PermissionKind.IsDisabled))
{
throw new SecurityException("User account has been disabled.");
}
- if (!user.Policy.EnableRemoteAccess && !_networkManager.IsInLocalNetwork(request.RemoteIp))
+ if (!user.HasPermission(PermissionKind.EnableRemoteAccess) && !_networkManager.IsInLocalNetwork(request.RemoteIp))
{
throw new SecurityException("User account has been disabled.");
}
- if (!user.Policy.IsAdministrator
- && !authAttribtues.EscapeParentalControl
+ if (!user.HasPermission(PermissionKind.IsAdministrator)
+ && !authAttributes.EscapeParentalControl
&& !user.IsParentalScheduleAllowed())
{
request.Response.Headers.Add("X-Application-Error-Code", "ParentalControl");
@@ -186,7 +203,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
{
if (roles.Contains("admin", StringComparer.OrdinalIgnoreCase))
{
- if (user == null || !user.Policy.IsAdministrator)
+ if (user == null || !user.HasPermission(PermissionKind.IsAdministrator))
{
throw new SecurityException("User does not have admin access.");
}
@@ -194,7 +211,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
if (roles.Contains("delete", StringComparer.OrdinalIgnoreCase))
{
- if (user == null || !user.Policy.EnableContentDeletion)
+ if (user == null || !user.HasPermission(PermissionKind.EnableContentDeletion))
{
throw new SecurityException("User does not have delete access.");
}
@@ -202,7 +219,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
if (roles.Contains("download", StringComparer.OrdinalIgnoreCase))
{
- if (user == null || !user.Policy.EnableContentDownloading)
+ if (user == null || !user.HasPermission(PermissionKind.EnableContentDownloading))
{
throw new SecurityException("User does not have download access.");
}
@@ -228,16 +245,6 @@ namespace Emby.Server.Implementations.HttpServer.Security
{
throw new AuthenticationException("Access token is invalid or expired.");
}
-
- //if (!string.IsNullOrEmpty(info.UserId))
- //{
- // var user = _userManager.GetUserById(info.UserId);
-
- // if (user == null || user.Configuration.IsDisabled)
- // {
- // throw new SecurityException("User account has been disabled.");
- // }
- //}
}
}
}
diff --git a/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs b/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs
index 129faeaab..fb93fae3e 100644
--- a/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs
+++ b/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs
@@ -8,6 +8,7 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Security;
using MediaBrowser.Model.Services;
+using Microsoft.AspNetCore.Http;
using Microsoft.Net.Http.Headers;
namespace Emby.Server.Implementations.HttpServer.Security
@@ -38,6 +39,14 @@ namespace Emby.Server.Implementations.HttpServer.Security
return GetAuthorization(requestContext);
}
+ public AuthorizationInfo GetAuthorizationInfo(HttpRequest requestContext)
+ {
+ var auth = GetAuthorizationDictionary(requestContext);
+ var (authInfo, _) =
+ GetAuthorizationInfoFromDictionary(auth, requestContext.Headers, requestContext.Query);
+ return authInfo;
+ }
+
/// <summary>
/// Gets the authorization.
/// </summary>
@@ -46,7 +55,23 @@ namespace Emby.Server.Implementations.HttpServer.Security
private AuthorizationInfo GetAuthorization(IRequest httpReq)
{
var auth = GetAuthorizationDictionary(httpReq);
+ var (authInfo, originalAuthInfo) =
+ GetAuthorizationInfoFromDictionary(auth, httpReq.Headers, httpReq.QueryString);
+ if (originalAuthInfo != null)
+ {
+ httpReq.Items["OriginalAuthenticationInfo"] = originalAuthInfo;
+ }
+
+ httpReq.Items["AuthorizationInfo"] = authInfo;
+ return authInfo;
+ }
+
+ private (AuthorizationInfo authInfo, AuthenticationInfo originalAuthenticationInfo) GetAuthorizationInfoFromDictionary(
+ in Dictionary<string, string> auth,
+ in IHeaderDictionary headers,
+ in IQueryCollection queryString)
+ {
string deviceId = null;
string device = null;
string client = null;
@@ -64,19 +89,26 @@ namespace Emby.Server.Implementations.HttpServer.Security
if (string.IsNullOrEmpty(token))
{
- token = httpReq.Headers["X-Emby-Token"];
+ token = headers["X-Emby-Token"];
}
if (string.IsNullOrEmpty(token))
{
- token = httpReq.Headers["X-MediaBrowser-Token"];
+ token = headers["X-MediaBrowser-Token"];
}
+
+ if (string.IsNullOrEmpty(token))
+ {
+ token = queryString["ApiKey"];
+ }
+
+ // TODO deprecate this query parameter.
if (string.IsNullOrEmpty(token))
{
- token = httpReq.QueryString["api_key"];
+ token = queryString["api_key"];
}
- var info = new AuthorizationInfo
+ var authInfo = new AuthorizationInfo
{
Client = client,
Device = device,
@@ -85,6 +117,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
Token = token
};
+ AuthenticationInfo originalAuthenticationInfo = null;
if (!string.IsNullOrWhiteSpace(token))
{
var result = _authRepo.Get(new AuthenticationInfoQuery
@@ -92,81 +125,77 @@ namespace Emby.Server.Implementations.HttpServer.Security
AccessToken = token
});
- var tokenInfo = result.Items.Count > 0 ? result.Items[0] : null;
+ originalAuthenticationInfo = result.Items.Count > 0 ? result.Items[0] : null;
- if (tokenInfo != null)
+ if (originalAuthenticationInfo != null)
{
var updateToken = false;
// TODO: Remove these checks for IsNullOrWhiteSpace
- if (string.IsNullOrWhiteSpace(info.Client))
+ if (string.IsNullOrWhiteSpace(authInfo.Client))
{
- info.Client = tokenInfo.AppName;
+ authInfo.Client = originalAuthenticationInfo.AppName;
}
- if (string.IsNullOrWhiteSpace(info.DeviceId))
+ if (string.IsNullOrWhiteSpace(authInfo.DeviceId))
{
- info.DeviceId = tokenInfo.DeviceId;
+ authInfo.DeviceId = originalAuthenticationInfo.DeviceId;
}
// Temporary. TODO - allow clients to specify that the token has been shared with a casting device
- var allowTokenInfoUpdate = info.Client == null || info.Client.IndexOf("chromecast", StringComparison.OrdinalIgnoreCase) == -1;
+ var allowTokenInfoUpdate = authInfo.Client == null || authInfo.Client.IndexOf("chromecast", StringComparison.OrdinalIgnoreCase) == -1;
- if (string.IsNullOrWhiteSpace(info.Device))
+ if (string.IsNullOrWhiteSpace(authInfo.Device))
{
- info.Device = tokenInfo.DeviceName;
+ authInfo.Device = originalAuthenticationInfo.DeviceName;
}
-
- else if (!string.Equals(info.Device, tokenInfo.DeviceName, StringComparison.OrdinalIgnoreCase))
+ else if (!string.Equals(authInfo.Device, originalAuthenticationInfo.DeviceName, StringComparison.OrdinalIgnoreCase))
{
if (allowTokenInfoUpdate)
{
updateToken = true;
- tokenInfo.DeviceName = info.Device;
+ originalAuthenticationInfo.DeviceName = authInfo.Device;
}
}
- if (string.IsNullOrWhiteSpace(info.Version))
+ if (string.IsNullOrWhiteSpace(authInfo.Version))
{
- info.Version = tokenInfo.AppVersion;
+ authInfo.Version = originalAuthenticationInfo.AppVersion;
}
- else if (!string.Equals(info.Version, tokenInfo.AppVersion, StringComparison.OrdinalIgnoreCase))
+ else if (!string.Equals(authInfo.Version, originalAuthenticationInfo.AppVersion, StringComparison.OrdinalIgnoreCase))
{
if (allowTokenInfoUpdate)
{
updateToken = true;
- tokenInfo.AppVersion = info.Version;
+ originalAuthenticationInfo.AppVersion = authInfo.Version;
}
}
- if ((DateTime.UtcNow - tokenInfo.DateLastActivity).TotalMinutes > 3)
+ if ((DateTime.UtcNow - originalAuthenticationInfo.DateLastActivity).TotalMinutes > 3)
{
- tokenInfo.DateLastActivity = DateTime.UtcNow;
+ originalAuthenticationInfo.DateLastActivity = DateTime.UtcNow;
updateToken = true;
}
- if (!tokenInfo.UserId.Equals(Guid.Empty))
+ if (!originalAuthenticationInfo.UserId.Equals(Guid.Empty))
{
- info.User = _userManager.GetUserById(tokenInfo.UserId);
+ authInfo.User = _userManager.GetUserById(originalAuthenticationInfo.UserId);
- if (info.User != null && !string.Equals(info.User.Name, tokenInfo.UserName, StringComparison.OrdinalIgnoreCase))
+ if (authInfo.User != null && !string.Equals(authInfo.User.Username, originalAuthenticationInfo.UserName, StringComparison.OrdinalIgnoreCase))
{
- tokenInfo.UserName = info.User.Name;
+ originalAuthenticationInfo.UserName = authInfo.User.Username;
updateToken = true;
}
}
if (updateToken)
{
- _authRepo.Update(tokenInfo);
+ _authRepo.Update(originalAuthenticationInfo);
}
}
- httpReq.Items["OriginalAuthenticationInfo"] = tokenInfo;
}
- httpReq.Items["AuthorizationInfo"] = info;
-
- return info;
+ return (authInfo, originalAuthenticationInfo);
}
/// <summary>
@@ -187,6 +216,23 @@ namespace Emby.Server.Implementations.HttpServer.Security
}
/// <summary>
+ /// Gets the auth.
+ /// </summary>
+ /// <param name="httpReq">The HTTP req.</param>
+ /// <returns>Dictionary{System.StringSystem.String}.</returns>
+ private Dictionary<string, string> GetAuthorizationDictionary(HttpRequest httpReq)
+ {
+ var auth = httpReq.Headers["X-Emby-Authorization"];
+
+ if (string.IsNullOrEmpty(auth))
+ {
+ auth = httpReq.Headers[HeaderNames.Authorization];
+ }
+
+ return GetAuthorization(auth);
+ }
+
+ /// <summary>
/// Gets the authorization.
/// </summary>
/// <param name="authorizationHeader">The authorization header.</param>
@@ -236,12 +282,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
private static string NormalizeValue(string value)
{
- if (string.IsNullOrEmpty(value))
- {
- return value;
- }
-
- return WebUtility.HtmlEncode(value);
+ return string.IsNullOrEmpty(value) ? value : WebUtility.HtmlEncode(value);
}
}
}
diff --git a/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs b/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs
index 166952c64..03fcfa53d 100644
--- a/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs
+++ b/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs
@@ -1,7 +1,7 @@
#pragma warning disable CS1591
using System;
-using MediaBrowser.Controller.Entities;
+using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Security;
diff --git a/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs b/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs
index c64d57339..316cd84cf 100644
--- a/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs
+++ b/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs
@@ -234,10 +234,12 @@ namespace Emby.Server.Implementations.HttpServer
private Task SendKeepAliveResponse()
{
LastKeepAliveDate = DateTime.UtcNow;
- return SendAsync(new WebSocketMessage<string>
- {
- MessageType = "KeepAlive"
- }, CancellationToken.None);
+ return SendAsync(
+ new WebSocketMessage<string>
+ {
+ MessageId = Guid.NewGuid(),
+ MessageType = "KeepAlive"
+ }, CancellationToken.None);
}
/// <inheritdoc />