Update plex token cache when password is changed

Fixed: New plex.tv password not taking effect immediately
Fixed: Prevent an incorrect plex.tv password from locking out account
This commit is contained in:
Mark McDowall 2015-07-05 22:45:51 -07:00
parent 84ae81efe3
commit 61de750e05
6 changed files with 53 additions and 21 deletions

View File

@ -0,0 +1,14 @@
namespace NzbDrone.Core.Notifications.Plex
{
public class PlexAuthenticationException : PlexException
{
public PlexAuthenticationException(string message) : base(message)
{
}
public PlexAuthenticationException(string message, params object[] args)
: base(message, args)
{
}
}
}

View File

@ -42,7 +42,7 @@ namespace NzbDrone.Core.Notifications.Plex
var response = client.Execute(request); var response = client.Execute(request);
_logger.Trace("Sections response: {0}", response.Content); _logger.Trace("Sections response: {0}", response.Content);
CheckForError(response); CheckForError(response, settings);
return Json.Deserialize<PlexMediaContainer>(response.Content) return Json.Deserialize<PlexMediaContainer>(response.Content)
.Directories .Directories
@ -58,7 +58,7 @@ namespace NzbDrone.Core.Notifications.Plex
var response = client.Execute(request); var response = client.Execute(request);
_logger.Trace("Update response: {0}", response.Content); _logger.Trace("Update response: {0}", response.Content);
CheckForError(response); CheckForError(response, settings);
} }
public void UpdateSeries(int metadataId, PlexServerSettings settings) public void UpdateSeries(int metadataId, PlexServerSettings settings)
@ -69,7 +69,7 @@ namespace NzbDrone.Core.Notifications.Plex
var response = client.Execute(request); var response = client.Execute(request);
_logger.Trace("Update Series response: {0}", response.Content); _logger.Trace("Update Series response: {0}", response.Content);
CheckForError(response); CheckForError(response, settings);
} }
public string Version(PlexServerSettings settings) public string Version(PlexServerSettings settings)
@ -79,7 +79,7 @@ namespace NzbDrone.Core.Notifications.Plex
var response = client.Execute(request); var response = client.Execute(request);
_logger.Trace("Version response: {0}", response.Content); _logger.Trace("Version response: {0}", response.Content);
CheckForError(response); CheckForError(response, settings);
return Json.Deserialize<PlexIdentity>(response.Content).Version; return Json.Deserialize<PlexIdentity>(response.Content).Version;
} }
@ -91,7 +91,7 @@ namespace NzbDrone.Core.Notifications.Plex
var response = client.Execute(request); var response = client.Execute(request);
_logger.Trace("Preferences response: {0}", response.Content); _logger.Trace("Preferences response: {0}", response.Content);
CheckForError(response); CheckForError(response, settings);
return Json.Deserialize<PlexPreferences>(response.Content).Preferences; return Json.Deserialize<PlexPreferences>(response.Content).Preferences;
} }
@ -105,7 +105,7 @@ namespace NzbDrone.Core.Notifications.Plex
var response = client.Execute(request); var response = client.Execute(request);
_logger.Trace("Sections response: {0}", response.Content); _logger.Trace("Sections response: {0}", response.Content);
CheckForError(response); CheckForError(response, settings);
var item = Json.Deserialize<PlexSectionResponse>(response.Content) var item = Json.Deserialize<PlexSectionResponse>(response.Content)
.Items .Items
@ -119,20 +119,18 @@ namespace NzbDrone.Core.Notifications.Plex
return item.Id; return item.Id;
} }
private String Authenticate(string username, string password) private String Authenticate(PlexServerSettings settings)
{ {
var request = GetMyPlexRequest("users/sign_in.json", Method.POST); var request = GetMyPlexRequest("users/sign_in.json", Method.POST);
var client = GetMyPlexClient(username, password); var client = GetMyPlexClient(settings.Username, settings.Password);
var response = client.Execute(request); var response = client.Execute(request);
_logger.Debug("Authentication Response: {0}", response.Content); _logger.Debug("Authentication Response: {0}", response.Content);
CheckForError(response); CheckForError(response, settings);
var user = Json.Deserialize<PlexUser>(JObject.Parse(response.Content).SelectToken("user").ToString()); var user = Json.Deserialize<PlexUser>(JObject.Parse(response.Content).SelectToken("user").ToString());
_authCache.Set(username, user.AuthenticationToken);
return user.AuthenticationToken; return user.AuthenticationToken;
} }
@ -170,26 +168,40 @@ namespace NzbDrone.Core.Notifications.Plex
var request = new RestRequest(resource, method); var request = new RestRequest(resource, method);
request.AddHeader("Accept", "application/json"); request.AddHeader("Accept", "application/json");
if (!settings.Username.IsNullOrWhiteSpace()) if (settings.Username.IsNotNullOrWhiteSpace())
{ {
request.AddParameter("X-Plex-Token", GetAuthenticationToken(settings.Username, settings.Password), ParameterType.HttpHeader); request.AddParameter("X-Plex-Token", GetAuthenticationToken(settings), ParameterType.HttpHeader);
} }
return request; return request;
} }
private string GetAuthenticationToken(string username, string password) private string GetAuthenticationToken(PlexServerSettings settings)
{ {
return _authCache.Get(username, () => Authenticate(username, password)); var token = _authCache.Get(settings.Username + settings.Password, () => Authenticate(settings));
if (token.IsNullOrWhiteSpace())
{
throw new PlexAuthenticationException("Invalid Token - Update your username and password");
}
return token;
} }
private void CheckForError(IRestResponse response) private void CheckForError(IRestResponse response, PlexServerSettings settings)
{ {
_logger.Trace("Checking for error"); _logger.Trace("Checking for error");
if (response.StatusCode == HttpStatusCode.Unauthorized) if (response.StatusCode == HttpStatusCode.Unauthorized)
{ {
throw new PlexException("Unauthorized"); if (settings.Username.IsNullOrWhiteSpace())
{
throw new PlexAuthenticationException("Unauthorized - Username and password required");
}
//Set the token to null in the cache so we don't keep trying with bad credentials
_authCache.Set(settings.Username + settings.Password, null);
throw new PlexAuthenticationException("Unauthorized - Username or password is incorrect");
} }
var error = Json.Deserialize<PlexError>(response.Content); var error = Json.Deserialize<PlexError>(response.Content);

View File

@ -151,6 +151,11 @@ namespace NzbDrone.Core.Notifications.Plex
return new ValidationFailure("Host", "At least one TV library is required"); return new ValidationFailure("Host", "At least one TV library is required");
} }
} }
catch(PlexAuthenticationException ex)
{
_logger.ErrorException("Unable to connect to Plex Server: " + ex.Message, ex);
return new ValidationFailure("Username", "Incorrect username or password");
}
catch (Exception ex) catch (Exception ex)
{ {
_logger.ErrorException("Unable to connect to Plex Server: " + ex.Message, ex); _logger.ErrorException("Unable to connect to Plex Server: " + ex.Message, ex);

View File

@ -706,6 +706,7 @@
<Compile Include="Notifications\Plex\Models\PlexPreferences.cs" /> <Compile Include="Notifications\Plex\Models\PlexPreferences.cs" />
<Compile Include="Notifications\Plex\Models\PlexSectionItem.cs" /> <Compile Include="Notifications\Plex\Models\PlexSectionItem.cs" />
<Compile Include="Notifications\Plex\Models\PlexSection.cs" /> <Compile Include="Notifications\Plex\Models\PlexSection.cs" />
<Compile Include="Notifications\Plex\PlexAuthenticationException.cs" />
<Compile Include="Notifications\Plex\PlexHomeTheater.cs" /> <Compile Include="Notifications\Plex\PlexHomeTheater.cs" />
<Compile Include="Notifications\Plex\PlexHomeTheaterSettings.cs" /> <Compile Include="Notifications\Plex\PlexHomeTheaterSettings.cs" />
<Compile Include="Notifications\Plex\PlexClientService.cs" /> <Compile Include="Notifications\Plex\PlexClientService.cs" />

View File

@ -2,9 +2,9 @@
<div class="modal-header"> <div class="modal-header">
<button type="button" class="close" aria-hidden="true" data-dismiss="modal">&times;</button> <button type="button" class="close" aria-hidden="true" data-dismiss="modal">&times;</button>
{{#if id}} {{#if id}}
<h3>Edit - {{implementation}}</h3> <h3>Edit - {{implementationName}}</h3>
{{else}} {{else}}
<h3>Add - {{implementation}}</h3> <h3>Add - {{implementationName}}</h3>
{{/if}} {{/if}}
</div> </div>
<div class="modal-body indexer-modal"> <div class="modal-body indexer-modal">

View File

@ -2,9 +2,9 @@
<div class="modal-header"> <div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button> <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
{{#if id}} {{#if id}}
<h3>Edit - {{implementation}}</h3> <h3>Edit - {{implementationName}}</h3>
{{else}} {{else}}
<h3>Add - {{implementation}}</h3> <h3>Add - {{implementationName}}</h3>
{{/if}} {{/if}}
</div> </div>
<div class="modal-body notification-modal"> <div class="modal-body notification-modal">