support in-home easy password

This commit is contained in:
Luke Pulverenti 2015-01-29 01:06:24 -05:00
parent 82fe244fa1
commit a6145e54d9
8 changed files with 165 additions and 28 deletions

View File

@ -11,7 +11,6 @@ using MediaBrowser.Model.Connect;
using MediaBrowser.Model.Dto; using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Users; using MediaBrowser.Model.Users;
using ServiceStack; using ServiceStack;
using ServiceStack.Text.Controller;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@ -148,6 +147,32 @@ namespace MediaBrowser.Api
public bool ResetPassword { get; set; } public bool ResetPassword { get; set; }
} }
/// <summary>
/// Class UpdateUserEasyPassword
/// </summary>
[Route("/Users/{Id}/EasyPassword", "POST", Summary = "Updates a user's easy password")]
[Authenticated]
public class UpdateUserEasyPassword : IReturnVoid
{
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
public string Id { get; set; }
/// <summary>
/// Gets or sets the new password.
/// </summary>
/// <value>The new password.</value>
public string NewPassword { get; set; }
/// <summary>
/// Gets or sets a value indicating whether [reset password].
/// </summary>
/// <value><c>true</c> if [reset password]; otherwise, <c>false</c>.</value>
public bool ResetPassword { get; set; }
}
/// <summary> /// <summary>
/// Class UpdateUser /// Class UpdateUser
/// </summary> /// </summary>
@ -410,6 +435,8 @@ namespace MediaBrowser.Api
public async Task PostAsync(UpdateUserPassword request) public async Task PostAsync(UpdateUserPassword request)
{ {
AssertCanUpdateUser(request.Id);
var user = _userManager.GetUserById(request.Id); var user = _userManager.GetUserById(request.Id);
if (user == null) if (user == null)
@ -434,6 +461,33 @@ namespace MediaBrowser.Api
} }
} }
public void Post(UpdateUserEasyPassword request)
{
var task = PostAsync(request);
Task.WaitAll(task);
}
public async Task PostAsync(UpdateUserEasyPassword request)
{
AssertCanUpdateUser(request.Id);
var user = _userManager.GetUserById(request.Id);
if (user == null)
{
throw new ResourceNotFoundException("User not found");
}
if (request.ResetPassword)
{
await _userManager.ResetEasyPassword(user).ConfigureAwait(false);
}
else
{
await _userManager.ChangeEasyPassword(user, request.NewPassword).ConfigureAwait(false);
}
}
/// <summary> /// <summary>
/// Posts the specified request. /// Posts the specified request.
/// </summary> /// </summary>
@ -449,7 +503,9 @@ namespace MediaBrowser.Api
{ {
// We need to parse this manually because we told service stack not to with IRequiresRequestStream // We need to parse this manually because we told service stack not to with IRequiresRequestStream
// https://code.google.com/p/servicestack/source/browse/trunk/Common/ServiceStack.Text/ServiceStack.Text/Controller/PathInfo.cs // https://code.google.com/p/servicestack/source/browse/trunk/Common/ServiceStack.Text/ServiceStack.Text/Controller/PathInfo.cs
var id = new Guid(GetPathValue(1)); var id = GetPathValue(1);
AssertCanUpdateUser(id);
var dtoUser = request; var dtoUser = request;
@ -499,11 +555,29 @@ namespace MediaBrowser.Api
public void Post(UpdateUserConfiguration request) public void Post(UpdateUserConfiguration request)
{ {
AssertCanUpdateUser(request.Id);
var task = _userManager.UpdateConfiguration(request.Id, request); var task = _userManager.UpdateConfiguration(request.Id, request);
Task.WaitAll(task); Task.WaitAll(task);
} }
private void AssertCanUpdateUser(string userId)
{
var auth = AuthorizationContext.GetAuthorizationInfo(Request);
// If they're going to update the record of another user, they must be an administrator
if (!string.Equals(userId, auth.UserId, StringComparison.OrdinalIgnoreCase))
{
var authenticatedUser = _userManager.GetUserById(auth.UserId);
if (!authenticatedUser.Policy.IsAdministrator)
{
throw new SecurityException("Unauthorized access.");
}
}
}
public void Post(UpdateUserPolicy request) public void Post(UpdateUserPolicy request)
{ {
var task = UpdateUserPolicy(request); var task = UpdateUserPolicy(request);

View File

@ -106,6 +106,11 @@ namespace MediaBrowser.Common.Implementations.Networking
// Private address space: // Private address space:
// http://en.wikipedia.org/wiki/Private_network // http://en.wikipedia.org/wiki/Private_network
if (endpoint.StartsWith("172.", StringComparison.OrdinalIgnoreCase))
{
return Is172AddressPrivate(endpoint);
}
return return
// If url was requested with computer name, we may see this // If url was requested with computer name, we may see this
@ -114,11 +119,23 @@ namespace MediaBrowser.Common.Implementations.Networking
endpoint.StartsWith("localhost", StringComparison.OrdinalIgnoreCase) || endpoint.StartsWith("localhost", StringComparison.OrdinalIgnoreCase) ||
endpoint.StartsWith("127.", StringComparison.OrdinalIgnoreCase) || endpoint.StartsWith("127.", StringComparison.OrdinalIgnoreCase) ||
endpoint.StartsWith("10.", StringComparison.OrdinalIgnoreCase) || endpoint.StartsWith("10.", StringComparison.OrdinalIgnoreCase) ||
endpoint.StartsWith("192.", StringComparison.OrdinalIgnoreCase) || endpoint.StartsWith("192.168", StringComparison.OrdinalIgnoreCase) ||
endpoint.StartsWith("172.", StringComparison.OrdinalIgnoreCase) ||
endpoint.StartsWith("169.", StringComparison.OrdinalIgnoreCase); endpoint.StartsWith("169.", StringComparison.OrdinalIgnoreCase);
} }
private bool Is172AddressPrivate(string endpoint)
{
for (var i = 16; i <= 31; i++)
{
if (endpoint.StartsWith("172." + i.ToString(CultureInfo.InvariantCulture) + ".", StringComparison.OrdinalIgnoreCase))
{
return true;
}
}
return false;
}
public bool IsInLocalNetwork(string endpoint) public bool IsInLocalNetwork(string endpoint)
{ {
return IsInLocalNetworkInternal(endpoint, true); return IsInLocalNetworkInternal(endpoint, true);

View File

@ -32,7 +32,7 @@ namespace MediaBrowser.Controller.Entities
/// </summary> /// </summary>
/// <value>The password.</value> /// <value>The password.</value>
public string Password { get; set; } public string Password { get; set; }
public string LocalPassword { get; set; } public string EasyPassword { get; set; }
public string ConnectUserName { get; set; } public string ConnectUserName { get; set; }
public string ConnectUserId { get; set; } public string ConnectUserId { get; set; }

View File

@ -117,6 +117,13 @@ namespace MediaBrowser.Controller.Library
/// <returns>Task.</returns> /// <returns>Task.</returns>
Task ResetPassword(User user); Task ResetPassword(User user);
/// <summary>
/// Resets the easy password.
/// </summary>
/// <param name="user">The user.</param>
/// <returns>Task.</returns>
Task ResetEasyPassword(User user);
/// <summary> /// <summary>
/// Changes the password. /// Changes the password.
/// </summary> /// </summary>
@ -125,6 +132,14 @@ namespace MediaBrowser.Controller.Library
/// <returns>Task.</returns> /// <returns>Task.</returns>
Task ChangePassword(User user, string newPasswordSha1); Task ChangePassword(User user, string newPasswordSha1);
/// <summary>
/// Changes the easy password.
/// </summary>
/// <param name="user">The user.</param>
/// <param name="newPasswordSha1">The new password sha1.</param>
/// <returns>Task.</returns>
Task ChangeEasyPassword(User user, string newPasswordSha1);
/// <summary> /// <summary>
/// Gets the user dto. /// Gets the user dto.
/// </summary> /// </summary>

View File

@ -67,6 +67,12 @@ namespace MediaBrowser.Model.Dto
/// <value><c>true</c> if this instance has configured password; otherwise, <c>false</c>.</value> /// <value><c>true</c> if this instance has configured password; otherwise, <c>false</c>.</value>
public bool HasConfiguredPassword { get; set; } public bool HasConfiguredPassword { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance has configured easy password.
/// </summary>
/// <value><c>true</c> if this instance has configured easy password; otherwise, <c>false</c>.</value>
public bool HasConfiguredEasyPassword { get; set; }
/// <summary> /// <summary>
/// Gets or sets the last login date. /// Gets or sets the last login date.
/// </summary> /// </summary>

View File

@ -275,9 +275,9 @@ namespace MediaBrowser.Server.Implementations.Library
private string GetLocalPasswordHash(User user) private string GetLocalPasswordHash(User user)
{ {
return string.IsNullOrEmpty(user.LocalPassword) return string.IsNullOrEmpty(user.EasyPassword)
? GetSha1String(string.Empty) ? GetSha1String(string.Empty)
: user.LocalPassword; : user.EasyPassword;
} }
private bool IsPasswordEmpty(string passwordHash) private bool IsPasswordEmpty(string passwordHash)
@ -355,18 +355,20 @@ namespace MediaBrowser.Server.Implementations.Library
var passwordHash = GetPasswordHash(user); var passwordHash = GetPasswordHash(user);
var hasConfiguredDefaultPassword = !IsPasswordEmpty(passwordHash); var hasConfiguredPassword = !IsPasswordEmpty(passwordHash);
var hasConfiguredEasyPassword = !IsPasswordEmpty(GetLocalPasswordHash(user));
var hasPassword = user.Configuration.EnableLocalPassword && !string.IsNullOrEmpty(remoteEndPoint) && _networkManager.IsInLocalNetwork(remoteEndPoint) ? var hasPassword = user.Configuration.EnableLocalPassword && !string.IsNullOrEmpty(remoteEndPoint) && _networkManager.IsInLocalNetwork(remoteEndPoint) ?
!IsPasswordEmpty(GetLocalPasswordHash(user)) : hasConfiguredEasyPassword :
hasConfiguredDefaultPassword; hasConfiguredPassword;
var dto = new UserDto var dto = new UserDto
{ {
Id = user.Id.ToString("N"), Id = user.Id.ToString("N"),
Name = user.Name, Name = user.Name,
HasPassword = hasPassword, HasPassword = hasPassword,
HasConfiguredPassword = hasConfiguredDefaultPassword, HasConfiguredPassword = hasConfiguredPassword,
HasConfiguredEasyPassword = hasConfiguredEasyPassword,
LastActivityDate = user.LastActivityDate, LastActivityDate = user.LastActivityDate,
LastLoginDate = user.LastLoginDate, LastLoginDate = user.LastLoginDate,
Configuration = user.Configuration, Configuration = user.Configuration,
@ -613,18 +615,11 @@ namespace MediaBrowser.Server.Implementations.Library
return ChangePassword(user, GetSha1String(string.Empty)); return ChangePassword(user, GetSha1String(string.Empty));
} }
/// <summary> public Task ResetEasyPassword(User user)
/// Changes the password. {
/// </summary> return ChangeEasyPassword(user, GetSha1String(string.Empty));
/// <param name="user">The user.</param> }
/// <param name="newPasswordSha1">The new password sha1.</param>
/// <returns>Task.</returns>
/// <exception cref="System.ArgumentNullException">
/// user
/// or
/// newPassword
/// </exception>
/// <exception cref="System.ArgumentException">Passwords for guests cannot be changed.</exception>
public async Task ChangePassword(User user, string newPasswordSha1) public async Task ChangePassword(User user, string newPasswordSha1)
{ {
if (user == null) if (user == null)
@ -648,6 +643,29 @@ namespace MediaBrowser.Server.Implementations.Library
EventHelper.FireEventIfNotNull(UserPasswordChanged, this, new GenericEventArgs<User>(user), _logger); EventHelper.FireEventIfNotNull(UserPasswordChanged, this, new GenericEventArgs<User>(user), _logger);
} }
public async Task ChangeEasyPassword(User user, string newPasswordSha1)
{
if (user == null)
{
throw new ArgumentNullException("user");
}
if (string.IsNullOrWhiteSpace(newPasswordSha1))
{
throw new ArgumentNullException("newPasswordSha1");
}
if (user.ConnectLinkType.HasValue && user.ConnectLinkType.Value == UserLinkType.Guest)
{
throw new ArgumentException("Passwords for guests cannot be changed.");
}
user.EasyPassword = newPasswordSha1;
await UpdateUser(user).ConfigureAwait(false);
EventHelper.FireEventIfNotNull(UserPasswordChanged, this, new GenericEventArgs<User>(user), _logger);
}
/// <summary> /// <summary>
/// Instantiates the new user. /// Instantiates the new user.
/// </summary> /// </summary>

View File

@ -14,9 +14,12 @@
"FileReadError": "An error occurred while reading the file.", "FileReadError": "An error occurred while reading the file.",
"DeleteUser": "Delete User", "DeleteUser": "Delete User",
"DeleteUserConfirmation": "Are you sure you wish to delete this user?", "DeleteUserConfirmation": "Are you sure you wish to delete this user?",
"PasswordResetHeader": "Password Reset", "PasswordResetHeader": "Reset Password",
"PasswordResetComplete": "The password has been reset.", "PasswordResetComplete": "The password has been reset.",
"PinCodeResetComplete": "The pin code has been reset.",
"PasswordResetConfirmation": "Are you sure you wish to reset the password?", "PasswordResetConfirmation": "Are you sure you wish to reset the password?",
"PinCodeResetConfirmation": "Are you sure you wish to reset the pin code?",
"HeaderPinCodeReset": "Reset Pin Code",
"PasswordSaved": "Password saved.", "PasswordSaved": "Password saved.",
"PasswordMatchError": "Password and password confirmation must match.", "PasswordMatchError": "Password and password confirmation must match.",
"OptionRelease": "Official Release", "OptionRelease": "Official Release",

View File

@ -56,6 +56,7 @@
"HeaderVideo": "Video", "HeaderVideo": "Video",
"HeaderPaths": "Paths", "HeaderPaths": "Paths",
"CategorySync": "Sync", "CategorySync": "Sync",
"HeaderEasyPinCode": "Easy Pin Code",
"RegisterWithPayPal": "Register with PayPal", "RegisterWithPayPal": "Register with PayPal",
"HeaderSyncRequiresSupporterMembership": "Sync Requires a Supporter Membership", "HeaderSyncRequiresSupporterMembership": "Sync Requires a Supporter Membership",
"HeaderEnjoyDayTrial": "Enjoy a 14 Day Free Trial", "HeaderEnjoyDayTrial": "Enjoy a 14 Day Free Trial",
@ -1132,11 +1133,14 @@
"LabelDisplayFoldersView": "Display a folders view to show plain media folders", "LabelDisplayFoldersView": "Display a folders view to show plain media folders",
"ViewTypeLiveTvRecordingGroups": "Recordings", "ViewTypeLiveTvRecordingGroups": "Recordings",
"ViewTypeLiveTvChannels": "Channels", "ViewTypeLiveTvChannels": "Channels",
"LabelAllowLocalAccessWithoutPassword": "Allow local access without a password", "LabelEasyPinCode": "Easy pin code:",
"LabelAllowLocalAccessWithoutPasswordHelp": "When enabled, a password will not be required when signing in from within your home network.", "EasyPasswordHelp": "Your easy pin code is used for offline access with supported Media Browser apps, and can also be used for easy in-network sign in.",
"LabelInNetworkSignInWithEasyPassword": "Enable in-network sign in with my easy pin code",
"LabelInNetworkSignInWithEasyPasswordHelp": "If enabled, you'll be able to use your easy pin code to sign in to Media Browser apps from inside your home network. Your regular password will only be needed away from home. If the pin code is left blank, you won't need a password within your home network.",
"HeaderPassword": "Password", "HeaderPassword": "Password",
"HeaderLocalAccess": "Local Access", "HeaderLocalAccess": "Local Access",
"HeaderViewOrder": "View Order", "HeaderViewOrder": "View Order",
"ButtonResetEasyPassword": "Reset easy pin code",
"LabelSelectUserViewOrder": "Choose the order your views will be displayed in within Media Browser apps", "LabelSelectUserViewOrder": "Choose the order your views will be displayed in within Media Browser apps",
"LabelMetadataRefreshMode": "Metadata refresh mode:", "LabelMetadataRefreshMode": "Metadata refresh mode:",
"LabelImageRefreshMode": "Image refresh mode:", "LabelImageRefreshMode": "Image refresh mode:",