(cherry picked from commit 3ff3de6b90704fba266833115cd9d03ace99aae9)zeus
parent
775b1ba9cf
commit
18fc1413c3
@ -0,0 +1,44 @@
|
|||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import React from 'react';
|
||||||
|
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||||
|
import SelectInput from './SelectInput';
|
||||||
|
|
||||||
|
function PlexMachineInput(props) {
|
||||||
|
const {
|
||||||
|
isFetching,
|
||||||
|
isDisabled,
|
||||||
|
value,
|
||||||
|
values,
|
||||||
|
onChange,
|
||||||
|
...otherProps
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
const helpText = 'Authenticate with plex.tv to show servers to use for authentication';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{
|
||||||
|
isFetching ?
|
||||||
|
<LoadingIndicator /> :
|
||||||
|
<SelectInput
|
||||||
|
value={value}
|
||||||
|
values={values}
|
||||||
|
isDisabled={isDisabled}
|
||||||
|
onChange={onChange}
|
||||||
|
helpText={helpText}
|
||||||
|
{...otherProps}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
PlexMachineInput.propTypes = {
|
||||||
|
isFetching: PropTypes.bool.isRequired,
|
||||||
|
isDisabled: PropTypes.bool.isRequired,
|
||||||
|
value: PropTypes.string,
|
||||||
|
values: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
|
onChange: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PlexMachineInput;
|
@ -0,0 +1,115 @@
|
|||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { createSelector } from 'reselect';
|
||||||
|
import { fetchPlexResources } from 'Store/Actions/settingsActions';
|
||||||
|
import PlexMachineInput from './PlexMachineInput';
|
||||||
|
|
||||||
|
function createMapStateToProps() {
|
||||||
|
return createSelector(
|
||||||
|
(state, { value }) => value,
|
||||||
|
(state) => state.oAuth,
|
||||||
|
(state) => state.settings.plex,
|
||||||
|
(value, oAuth, plex) => {
|
||||||
|
|
||||||
|
let values = [{ key: value, value }];
|
||||||
|
let isDisabled = true;
|
||||||
|
|
||||||
|
if (plex.isPopulated) {
|
||||||
|
const serverValues = plex.items.filter((item) => item.provides.includes('server')).map((item) => {
|
||||||
|
return ({
|
||||||
|
key: item.clientIdentifier,
|
||||||
|
value: `${item.name} / ${item.owned ? 'Owner' : 'User'} / ${item.clientIdentifier}`
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
if (serverValues.find((item) => item.key === value)) {
|
||||||
|
values = serverValues;
|
||||||
|
} else {
|
||||||
|
values = values.concat(serverValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
isDisabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ({
|
||||||
|
accessToken: oAuth.result?.accessToken,
|
||||||
|
values,
|
||||||
|
isDisabled,
|
||||||
|
...plex
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapDispatchToProps = {
|
||||||
|
dispatchFetchPlexResources: fetchPlexResources
|
||||||
|
};
|
||||||
|
|
||||||
|
class PlexMachineInputConnector extends Component {
|
||||||
|
|
||||||
|
//
|
||||||
|
// Lifecycle
|
||||||
|
componentDidMount = () => {
|
||||||
|
const {
|
||||||
|
accessToken,
|
||||||
|
dispatchFetchPlexResources
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
if (accessToken) {
|
||||||
|
dispatchFetchPlexResources({ accessToken });
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps) {
|
||||||
|
const {
|
||||||
|
accessToken,
|
||||||
|
dispatchFetchPlexResources
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
const oldToken = prevProps.accessToken;
|
||||||
|
if (accessToken && accessToken !== oldToken) {
|
||||||
|
dispatchFetchPlexResources({ accessToken });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
isFetching,
|
||||||
|
isPopulated,
|
||||||
|
isDisabled,
|
||||||
|
value,
|
||||||
|
values,
|
||||||
|
onChange
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PlexMachineInput
|
||||||
|
isFetching={isFetching}
|
||||||
|
isPopulated={isPopulated}
|
||||||
|
isDisabled={isDisabled}
|
||||||
|
value={value}
|
||||||
|
values={values}
|
||||||
|
onChange={onChange}
|
||||||
|
{...this.props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PlexMachineInputConnector.propTypes = {
|
||||||
|
dispatchFetchPlexResources: PropTypes.func.isRequired,
|
||||||
|
name: PropTypes.string.isRequired,
|
||||||
|
value: PropTypes.string.isRequired,
|
||||||
|
values: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
|
isFetching: PropTypes.bool.isRequired,
|
||||||
|
isPopulated: PropTypes.bool.isRequired,
|
||||||
|
isDisabled: PropTypes.bool.isRequired,
|
||||||
|
error: PropTypes.object,
|
||||||
|
oAuth: PropTypes.object,
|
||||||
|
accessToken: PropTypes.string,
|
||||||
|
onChange: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
export default connect(createMapStateToProps, mapDispatchToProps)(PlexMachineInputConnector);
|
@ -0,0 +1,48 @@
|
|||||||
|
import createFetchHandler from 'Store/Actions/Creators/createFetchHandler';
|
||||||
|
import { createThunk } from 'Store/thunks';
|
||||||
|
|
||||||
|
//
|
||||||
|
// Variables
|
||||||
|
|
||||||
|
const section = 'settings.plex';
|
||||||
|
|
||||||
|
//
|
||||||
|
// Actions Types
|
||||||
|
|
||||||
|
export const FETCH_PLEX_RESOURCES = 'settings/plex/fetchResources';
|
||||||
|
|
||||||
|
//
|
||||||
|
// Action Creators
|
||||||
|
|
||||||
|
export const fetchPlexResources = createThunk(FETCH_PLEX_RESOURCES);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Details
|
||||||
|
|
||||||
|
export default {
|
||||||
|
|
||||||
|
//
|
||||||
|
// State
|
||||||
|
|
||||||
|
defaultState: {
|
||||||
|
isFetching: false,
|
||||||
|
isPopulated: false,
|
||||||
|
error: null,
|
||||||
|
pendingChanges: {},
|
||||||
|
isSaving: false,
|
||||||
|
saveError: null,
|
||||||
|
items: []
|
||||||
|
},
|
||||||
|
|
||||||
|
//
|
||||||
|
// Action Handlers
|
||||||
|
|
||||||
|
actionHandlers: {
|
||||||
|
[FETCH_PLEX_RESOURCES]: createFetchHandler(section, '/authentication/plex/resources')
|
||||||
|
},
|
||||||
|
|
||||||
|
//
|
||||||
|
// Reducers
|
||||||
|
|
||||||
|
reducers: { }
|
||||||
|
};
|
@ -0,0 +1,13 @@
|
|||||||
|
namespace NzbDrone.Core.Notifications.Plex.PlexTv
|
||||||
|
{
|
||||||
|
public class PlexTvResource
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string Product { get; set; }
|
||||||
|
public string Platform { get; set; }
|
||||||
|
public string ClientIdentifier { get; set; }
|
||||||
|
public string Provides { get; set; }
|
||||||
|
public bool Owned { get; set; }
|
||||||
|
public bool Home { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using NzbDrone.Core.Notifications.Plex.PlexTv;
|
||||||
|
using Radarr.Http;
|
||||||
|
|
||||||
|
namespace Radarr.Api.V3.Authentication
|
||||||
|
{
|
||||||
|
[V3ApiController]
|
||||||
|
public class AuthenticationController : Controller
|
||||||
|
{
|
||||||
|
private readonly IPlexTvService _plex;
|
||||||
|
|
||||||
|
public AuthenticationController(IPlexTvService plex)
|
||||||
|
{
|
||||||
|
_plex = plex;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("plex/resources")]
|
||||||
|
public List<PlexTvResource> GetResources(string accessToken)
|
||||||
|
{
|
||||||
|
return _plex.GetResources(accessToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
|
||||||
|
namespace Radarr.Http.Authentication
|
||||||
|
{
|
||||||
|
public class ApiKeyRequirement : AuthorizationHandler<ApiKeyRequirement>, IAuthorizationRequirement
|
||||||
|
{
|
||||||
|
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, ApiKeyRequirement requirement)
|
||||||
|
{
|
||||||
|
var apiKeyClaim = context.User.FindFirst(c => c.Type == "ApiKey");
|
||||||
|
|
||||||
|
if (apiKeyClaim != null)
|
||||||
|
{
|
||||||
|
context.Succeed(requirement);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
|
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using NzbDrone.Common.Extensions;
|
||||||
|
using NzbDrone.Core.Authentication;
|
||||||
|
using NzbDrone.Core.Configuration;
|
||||||
|
|
||||||
|
namespace Radarr.Http.Authentication.OpenIdConnect
|
||||||
|
{
|
||||||
|
public class ConfigureOidcOptions : IConfigureNamedOptions<OpenIdConnectOptions>
|
||||||
|
{
|
||||||
|
private readonly IConfigService _configService;
|
||||||
|
|
||||||
|
public ConfigureOidcOptions(IConfigService configService)
|
||||||
|
{
|
||||||
|
_configService = configService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Configure(string name, OpenIdConnectOptions options)
|
||||||
|
{
|
||||||
|
options.ClientId = _configService.OidcClientId.IsNullOrWhiteSpace() ? "dummy" : _configService.OidcClientId;
|
||||||
|
options.ClientSecret = _configService.OidcClientSecret.IsNullOrWhiteSpace() ? "dummy" : _configService.OidcClientSecret;
|
||||||
|
options.Authority = _configService.OidcAuthority.IsNullOrWhiteSpace() ? "https://dummy.com" : _configService.OidcAuthority;
|
||||||
|
options.SignedOutRedirectUri = "/login/sso";
|
||||||
|
options.SignInScheme = AuthenticationType.Oidc.ToString();
|
||||||
|
options.NonceCookie.SecurePolicy = Microsoft.AspNetCore.Http.CookieSecurePolicy.Always;
|
||||||
|
options.CorrelationCookie.SecurePolicy = Microsoft.AspNetCore.Http.CookieSecurePolicy.Always;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Configure(OpenIdConnectOptions options)
|
||||||
|
=> Debug.Fail("This infrastructure method shouldn't be called.");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
namespace Radarr.Http.Authentication.Plex
|
||||||
|
{
|
||||||
|
public static class PlexConstants
|
||||||
|
{
|
||||||
|
public static readonly string PinId = "pin_id";
|
||||||
|
public static readonly string ServerOwnedClaim = "plex:server:owned";
|
||||||
|
public static readonly string ServerAccessClaim = "plex:server:access";
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
namespace Radarr.Http.Authentication.Plex
|
||||||
|
{
|
||||||
|
public static class PlexDefaults
|
||||||
|
{
|
||||||
|
public const string AuthenticationScheme = "Plex";
|
||||||
|
public static readonly string DisplayName = "Plex";
|
||||||
|
public static readonly string AuthorizationEndpoint = "https://plex.tv/api/v2/pins";
|
||||||
|
public static readonly string TokenEndpoint = "https://app.plex.tv/auth/#!";
|
||||||
|
public static readonly string UserInformationEndpoint = "https://www.googleapis.com/oauth2/v2/userinfo";
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.AspNetCore.Authentication;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
|
namespace Radarr.Http.Authentication.Plex
|
||||||
|
{
|
||||||
|
public static class PlexExtensions
|
||||||
|
{
|
||||||
|
public static AuthenticationBuilder AddPlex(this AuthenticationBuilder builder, string authenticationScheme, Action<PlexOptions> configureOptions)
|
||||||
|
=> builder.AddOAuth<PlexOptions, PlexHandler>(authenticationScheme, PlexDefaults.DisplayName, configureOptions);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,135 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Net.Http.Headers;
|
||||||
|
using System.Security.Claims;
|
||||||
|
using System.Text.Encodings.Web;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Authentication;
|
||||||
|
using Microsoft.AspNetCore.Authentication.OAuth;
|
||||||
|
using Microsoft.AspNetCore.WebUtilities;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using Microsoft.Extensions.Primitives;
|
||||||
|
using NzbDrone.Core.Notifications.Plex.PlexTv;
|
||||||
|
|
||||||
|
namespace Radarr.Http.Authentication.Plex
|
||||||
|
{
|
||||||
|
public class PlexHandler : OAuthHandler<PlexOptions>
|
||||||
|
{
|
||||||
|
private readonly IPlexTvService _plexTvService;
|
||||||
|
|
||||||
|
public PlexHandler(IOptionsMonitor<PlexOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock, IPlexTvService plexTvService)
|
||||||
|
: base(options, logger, encoder, clock)
|
||||||
|
{
|
||||||
|
_plexTvService = plexTvService;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override string BuildChallengeUrl(AuthenticationProperties properties, string redirectUri)
|
||||||
|
{
|
||||||
|
var pinUrl = _plexTvService.GetPinUrl();
|
||||||
|
|
||||||
|
var requestMessage = new HttpRequestMessage(HttpMethod.Post, pinUrl.Url);
|
||||||
|
requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
||||||
|
|
||||||
|
var response = Backchannel.Send(requestMessage, Context.RequestAborted);
|
||||||
|
var pin = JsonSerializer.Deserialize<PlexPinResponse>(response.Content.ReadAsStream());
|
||||||
|
|
||||||
|
properties.Items.Add(PlexConstants.PinId, pin.id.ToString());
|
||||||
|
|
||||||
|
var state = Options.StateDataFormat.Protect(properties);
|
||||||
|
|
||||||
|
var plexRedirectUrl = QueryHelpers.AddQueryString(redirectUri, new Dictionary<string, string> { { "state", state } });
|
||||||
|
|
||||||
|
return _plexTvService.GetSignInUrl(plexRedirectUrl, pin.id, pin.code).OauthUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task<HandleRequestResult> HandleRemoteAuthenticateAsync()
|
||||||
|
{
|
||||||
|
var query = Request.Query;
|
||||||
|
|
||||||
|
var state = query["state"];
|
||||||
|
var properties = Options.StateDataFormat.Unprotect(state);
|
||||||
|
|
||||||
|
if (properties == null)
|
||||||
|
{
|
||||||
|
return HandleRequestResult.Fail("The oauth state was missing or invalid.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!properties.Items.TryGetValue(PlexConstants.PinId, out var code))
|
||||||
|
{
|
||||||
|
return HandleRequestResult.Fail("The pin was missing or invalid.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!int.TryParse(code, out var _))
|
||||||
|
{
|
||||||
|
return HandleRequestResult.Fail("The pin was in the wrong format.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var codeExchangeContext = new OAuthCodeExchangeContext(properties, code, BuildRedirectUri(Options.CallbackPath));
|
||||||
|
using var tokens = await ExchangeCodeAsync(codeExchangeContext);
|
||||||
|
|
||||||
|
if (tokens.Error != null)
|
||||||
|
{
|
||||||
|
return HandleRequestResult.Fail(tokens.Error);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(tokens.AccessToken))
|
||||||
|
{
|
||||||
|
return HandleRequestResult.Fail("Failed to retrieve access token.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var resources = _plexTvService.GetResources(tokens.AccessToken);
|
||||||
|
|
||||||
|
var identity = new ClaimsIdentity(ClaimsIssuer);
|
||||||
|
|
||||||
|
foreach (var resource in resources)
|
||||||
|
{
|
||||||
|
if (resource.Owned)
|
||||||
|
{
|
||||||
|
identity.AddClaim(new Claim(PlexConstants.ServerOwnedClaim, resource.ClientIdentifier));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
identity.AddClaim(new Claim(PlexConstants.ServerAccessClaim, resource.ClientIdentifier));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var ticket = await CreateTicketAsync(identity, properties, tokens);
|
||||||
|
if (ticket != null)
|
||||||
|
{
|
||||||
|
return HandleRequestResult.Success(ticket);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return HandleRequestResult.Fail("Failed to retrieve user information from remote server.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Task<OAuthTokenResponse> ExchangeCodeAsync(OAuthCodeExchangeContext context)
|
||||||
|
{
|
||||||
|
var token = _plexTvService.GetAuthToken(int.Parse(context.Code));
|
||||||
|
|
||||||
|
var result = !StringValues.IsNullOrEmpty(token) switch
|
||||||
|
{
|
||||||
|
true => OAuthTokenResponse.Success(JsonDocument.Parse(string.Format("{{\"access_token\": \"{0}\"}}", token))),
|
||||||
|
false => OAuthTokenResponse.Failed(new Exception("No token returned"))
|
||||||
|
};
|
||||||
|
|
||||||
|
return Task.FromResult(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static OAuthTokenResponse PrepareFailedOAuthTokenReponse(HttpResponseMessage response, string body)
|
||||||
|
{
|
||||||
|
var errorMessage = $"OAuth token endpoint failure: Status: {response.StatusCode};Headers: {response.Headers};Body: {body};";
|
||||||
|
return OAuthTokenResponse.Failed(new Exception(errorMessage));
|
||||||
|
}
|
||||||
|
|
||||||
|
private class PlexPinResponse
|
||||||
|
{
|
||||||
|
public int id { get; set; }
|
||||||
|
public string code { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
using Microsoft.AspNetCore.Authentication.OAuth;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
|
||||||
|
namespace Radarr.Http.Authentication.Plex
|
||||||
|
{
|
||||||
|
public class PlexOptions : OAuthOptions
|
||||||
|
{
|
||||||
|
public PlexOptions()
|
||||||
|
{
|
||||||
|
CallbackPath = new PathString("/signin-plex");
|
||||||
|
AuthorizationEndpoint = PlexDefaults.AuthorizationEndpoint;
|
||||||
|
TokenEndpoint = PlexDefaults.TokenEndpoint;
|
||||||
|
UserInformationEndpoint = PlexDefaults.UserInformationEndpoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Validate()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using NzbDrone.Core.Configuration;
|
||||||
|
using NzbDrone.Core.Configuration.Events;
|
||||||
|
using NzbDrone.Core.Messaging.Events;
|
||||||
|
|
||||||
|
namespace Radarr.Http.Authentication.Plex
|
||||||
|
{
|
||||||
|
public class PlexServerRequirement : IAuthorizationRequirement
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PlexServerHandler : AuthorizationHandler<PlexServerRequirement>, IHandle<ConfigSavedEvent>
|
||||||
|
{
|
||||||
|
private readonly IConfigService _configService;
|
||||||
|
private string _requiredServer;
|
||||||
|
private bool _requireOwner;
|
||||||
|
|
||||||
|
public PlexServerHandler(IConfigService configService)
|
||||||
|
{
|
||||||
|
_configService = configService;
|
||||||
|
_requiredServer = configService.PlexAuthServer;
|
||||||
|
_requireOwner = configService.PlexRequireOwner;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, PlexServerRequirement requirement)
|
||||||
|
{
|
||||||
|
var serverClaim = context.User.FindFirst(c => c.Type == PlexConstants.ServerOwnedClaim && c.Value == _requiredServer);
|
||||||
|
if (serverClaim != null)
|
||||||
|
{
|
||||||
|
context.Succeed(requirement);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_requireOwner)
|
||||||
|
{
|
||||||
|
serverClaim = context.User.FindFirst(c => c.Type == PlexConstants.ServerAccessClaim && c.Value == _requiredServer);
|
||||||
|
if (serverClaim != null)
|
||||||
|
{
|
||||||
|
context.Succeed(requirement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Handle(ConfigSavedEvent message)
|
||||||
|
{
|
||||||
|
_requiredServer = _configService.PlexAuthServer;
|
||||||
|
_requireOwner = _configService.PlexRequireOwner;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in new issue