Merge pull request #3812 from jellyfin/api-migration

Merge API Migration into master
This commit is contained in:
Anthony Lavado
2020-08-08 13:22:36 -04:00
committed by GitHub
248 changed files with 25546 additions and 22770 deletions

View File

@@ -4,7 +4,6 @@ using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
@@ -46,7 +45,7 @@ using Emby.Server.Implementations.Session;
using Emby.Server.Implementations.SyncPlay;
using Emby.Server.Implementations.TV;
using Emby.Server.Implementations.Updates;
using MediaBrowser.Api;
using Jellyfin.Api.Helpers;
using MediaBrowser.Common;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Events;
@@ -97,7 +96,6 @@ using MediaBrowser.Providers.Chapters;
using MediaBrowser.Providers.Manager;
using MediaBrowser.Providers.Plugins.TheTvdb;
using MediaBrowser.Providers.Subtitles;
using MediaBrowser.WebDashboard.Api;
using MediaBrowser.XbmcMetadata.Providers;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
@@ -632,6 +630,8 @@ namespace Emby.Server.Implementations
serviceCollection.AddSingleton<EncodingHelper>();
serviceCollection.AddSingleton<IAttachmentExtractor, MediaBrowser.MediaEncoding.Attachments.AttachmentExtractor>();
serviceCollection.AddSingleton<TranscodingJobHelper>();
}
/// <summary>
@@ -1031,12 +1031,6 @@ namespace Emby.Server.Implementations
}
}
// Include composable parts in the Api assembly
yield return typeof(ApiEntryPoint).Assembly;
// Include composable parts in the Dashboard assembly
yield return typeof(DashboardService).Assembly;
// Include composable parts in the Model assembly
yield return typeof(SystemInfo).Assembly;

View File

@@ -1,5 +1,7 @@
using System;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Browser
@@ -24,7 +26,7 @@ namespace Emby.Server.Implementations.Browser
/// <param name="appHost">The app host.</param>
public static void OpenSwaggerPage(IServerApplicationHost appHost)
{
TryOpenUrl(appHost, "/swagger/index.html");
TryOpenUrl(appHost, "/api-docs/swagger");
}
/// <summary>

View File

@@ -13,10 +13,8 @@
<ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj" />
<ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" />
<ProjectReference Include="..\MediaBrowser.Providers\MediaBrowser.Providers.csproj" />
<ProjectReference Include="..\MediaBrowser.WebDashboard\MediaBrowser.WebDashboard.csproj" />
<ProjectReference Include="..\MediaBrowser.XbmcMetadata\MediaBrowser.XbmcMetadata.csproj" />
<ProjectReference Include="..\Emby.Dlna\Emby.Dlna.csproj" />
<ProjectReference Include="..\MediaBrowser.Api\MediaBrowser.Api.csproj" />
<ProjectReference Include="..\MediaBrowser.LocalMetadata\MediaBrowser.LocalMetadata.csproj" />
<ProjectReference Include="..\Emby.Photos\Emby.Photos.csproj" />
<ProjectReference Include="..\Emby.Drawing\Emby.Drawing.csproj" />

View File

@@ -35,9 +35,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)
@@ -63,17 +63,17 @@ namespace Emby.Server.Implementations.HttpServer.Security
return auth;
}
private User ValidateUser(IRequest request, IAuthenticationAttributes authAttribtues)
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.");
}
@@ -87,14 +87,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);
}
@@ -118,8 +118,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
private void ValidateUserAccess(
User user,
IRequest request,
IAuthenticationAttributes authAttributes,
AuthorizationInfo auth)
IAuthenticationAttributes authAttributes)
{
if (user.HasPermission(PermissionKind.IsDisabled))
{
@@ -158,6 +157,11 @@ namespace Emby.Server.Implementations.HttpServer.Security
return true;
}
if (authAttribtues.IgnoreLegacyAuth)
{
return true;
}
return false;
}
@@ -237,16 +241,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.");
// }
//}
}
}
}

View File

@@ -97,6 +97,12 @@ namespace Emby.Server.Implementations.HttpServer.Security
token = headers["X-MediaBrowser-Token"];
}
if (string.IsNullOrEmpty(token))
{
token = queryString["ApiKey"];
}
// TODO deprecate this query parameter.
if (string.IsNullOrEmpty(token))
{
token = queryString["api_key"];
@@ -276,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);
}
}
}

View File

@@ -1,287 +0,0 @@
#pragma warning disable CS1591
using System;
using System.Collections.Generic;
using System.Linq;
using Emby.Server.Implementations.HttpServer;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Services;
namespace Emby.Server.Implementations.Services
{
[Route("/swagger", "GET", Summary = "Gets the swagger specifications")]
[Route("/swagger.json", "GET", Summary = "Gets the swagger specifications")]
public class GetSwaggerSpec : IReturn<SwaggerSpec>
{
}
public class SwaggerSpec
{
public string swagger { get; set; }
public string[] schemes { get; set; }
public SwaggerInfo info { get; set; }
public string host { get; set; }
public string basePath { get; set; }
public SwaggerTag[] tags { get; set; }
public IDictionary<string, Dictionary<string, SwaggerMethod>> paths { get; set; }
public Dictionary<string, SwaggerDefinition> definitions { get; set; }
public SwaggerComponents components { get; set; }
}
public class SwaggerComponents
{
public Dictionary<string, SwaggerSecurityScheme> securitySchemes { get; set; }
}
public class SwaggerSecurityScheme
{
public string name { get; set; }
public string type { get; set; }
public string @in { get; set; }
}
public class SwaggerInfo
{
public string description { get; set; }
public string version { get; set; }
public string title { get; set; }
public string termsOfService { get; set; }
public SwaggerConcactInfo contact { get; set; }
}
public class SwaggerConcactInfo
{
public string email { get; set; }
public string name { get; set; }
public string url { get; set; }
}
public class SwaggerTag
{
public string description { get; set; }
public string name { get; set; }
}
public class SwaggerMethod
{
public string summary { get; set; }
public string description { get; set; }
public string[] tags { get; set; }
public string operationId { get; set; }
public string[] consumes { get; set; }
public string[] produces { get; set; }
public SwaggerParam[] parameters { get; set; }
public Dictionary<string, SwaggerResponse> responses { get; set; }
public Dictionary<string, string[]>[] security { get; set; }
}
public class SwaggerParam
{
public string @in { get; set; }
public string name { get; set; }
public string description { get; set; }
public bool required { get; set; }
public string type { get; set; }
public string collectionFormat { get; set; }
}
public class SwaggerResponse
{
public string description { get; set; }
// ex. "$ref":"#/definitions/Pet"
public Dictionary<string, string> schema { get; set; }
}
public class SwaggerDefinition
{
public string type { get; set; }
public Dictionary<string, SwaggerProperty> properties { get; set; }
}
public class SwaggerProperty
{
public string type { get; set; }
public string format { get; set; }
public string description { get; set; }
public string[] @enum { get; set; }
public string @default { get; set; }
}
public class SwaggerService : IService, IRequiresRequest
{
private readonly IHttpServer _httpServer;
private SwaggerSpec _spec;
public IRequest Request { get; set; }
public SwaggerService(IHttpServer httpServer)
{
_httpServer = httpServer;
}
public object Get(GetSwaggerSpec request)
{
return _spec ?? (_spec = GetSpec());
}
private SwaggerSpec GetSpec()
{
string host = null;
Uri uri;
if (Uri.TryCreate(Request.RawUrl, UriKind.Absolute, out uri))
{
host = uri.Host;
}
var securitySchemes = new Dictionary<string, SwaggerSecurityScheme>();
securitySchemes["api_key"] = new SwaggerSecurityScheme
{
name = "api_key",
type = "apiKey",
@in = "query"
};
var spec = new SwaggerSpec
{
schemes = new[] { "http" },
tags = GetTags(),
swagger = "2.0",
info = new SwaggerInfo
{
title = "Jellyfin Server API",
version = "1.0.0",
description = "Explore the Jellyfin Server API",
contact = new SwaggerConcactInfo
{
name = "Jellyfin Community",
url = "https://jellyfin.readthedocs.io/en/latest/user-docs/getting-help/"
}
},
paths = GetPaths(),
definitions = GetDefinitions(),
basePath = "/jellyfin",
host = host,
components = new SwaggerComponents
{
securitySchemes = securitySchemes
}
};
return spec;
}
private SwaggerTag[] GetTags()
{
return Array.Empty<SwaggerTag>();
}
private Dictionary<string, SwaggerDefinition> GetDefinitions()
{
return new Dictionary<string, SwaggerDefinition>();
}
private IDictionary<string, Dictionary<string, SwaggerMethod>> GetPaths()
{
var paths = new SortedDictionary<string, Dictionary<string, SwaggerMethod>>();
// REVIEW: this can be done better
var all = ((HttpListenerHost)_httpServer).ServiceController.RestPathMap.OrderBy(i => i.Key, StringComparer.OrdinalIgnoreCase).ToList();
foreach (var current in all)
{
foreach (var info in current.Value)
{
if (info.IsHidden)
{
continue;
}
if (info.Path.StartsWith("/mediabrowser", StringComparison.OrdinalIgnoreCase)
|| info.Path.StartsWith("/jellyfin", StringComparison.OrdinalIgnoreCase))
{
continue;
}
paths[info.Path] = GetPathInfo(info);
}
}
return paths;
}
private Dictionary<string, SwaggerMethod> GetPathInfo(RestPath info)
{
var result = new Dictionary<string, SwaggerMethod>();
foreach (var verb in info.Verbs)
{
var responses = new Dictionary<string, SwaggerResponse>
{
{ "200", new SwaggerResponse { description = "OK" } }
};
var apiKeySecurity = new Dictionary<string, string[]>
{
{ "api_key", Array.Empty<string>() }
};
result[verb.ToLowerInvariant()] = new SwaggerMethod
{
summary = info.Summary,
description = info.Description,
produces = new[] { "application/json" },
consumes = new[] { "application/json" },
operationId = info.RequestType.Name,
tags = Array.Empty<string>(),
parameters = Array.Empty<SwaggerParam>(),
responses = responses,
security = new[] { apiKeySecurity }
};
}
return result;
}
}
}