diff options
Diffstat (limited to 'MediaBrowser.Server.Implementations')
7 files changed, 206 insertions, 35 deletions
diff --git a/MediaBrowser.Server.Implementations/Connect/ConnectManager.cs b/MediaBrowser.Server.Implementations/Connect/ConnectManager.cs index f2ab64a52..f0d3b76e5 100644 --- a/MediaBrowser.Server.Implementations/Connect/ConnectManager.cs +++ b/MediaBrowser.Server.Implementations/Connect/ConnectManager.cs @@ -1027,7 +1027,7 @@ namespace MediaBrowser.Server.Implementations.Connect { var user = e.Argument; - //await TryUploadUserPreferences(user, CancellationToken.None).ConfigureAwait(false); + await TryUploadUserPreferences(user, CancellationToken.None).ConfigureAwait(false); } private async Task TryUploadUserPreferences(User user, CancellationToken cancellationToken) diff --git a/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs b/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs index c8e923b12..feda361c8 100644 --- a/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs +++ b/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs @@ -129,6 +129,15 @@ namespace MediaBrowser.Server.Implementations.Drawing } } + public ImageOutputFormat[] GetSupportedImageOutputFormats() + { + if (_webpAvailable) + { + return new[] { ImageOutputFormat.Webp, ImageOutputFormat.Gif, ImageOutputFormat.Jpg, ImageOutputFormat.Png }; + } + return new[] { ImageOutputFormat.Gif, ImageOutputFormat.Jpg, ImageOutputFormat.Png }; + } + public async Task<string> ProcessImage(ImageProcessingOptions options) { if (options == null) @@ -212,9 +221,9 @@ namespace MediaBrowser.Server.Implementations.Drawing var newWidth = Convert.ToInt32(newSize.Width); var newHeight = Convert.ToInt32(newSize.Height); - var selectedOutputFormat = options.OutputFormat == ImageOutputFormat.Webp && !_webpAvailable - ? GetFallbackImageFormat(originalImagePath) - : options.OutputFormat; + var selectedOutputFormat = options.OutputFormat; + + _logger.Debug("Processing image to {0}", selectedOutputFormat); // Graphics.FromImage will throw an exception if the PixelFormat is Indexed, so we need to handle that here // Also, Webp only supports Format32bppArgb and Format32bppRgb @@ -279,11 +288,6 @@ namespace MediaBrowser.Server.Implementations.Drawing } } - private ImageOutputFormat GetFallbackImageFormat(string originalImagePath) - { - return ImageOutputFormat.Png; - } - private void SaveToWebP(Bitmap thumbnail, Stream toStream, int quality) { new SimpleEncoder().Encode(thumbnail, toStream, quality); @@ -479,10 +483,7 @@ namespace MediaBrowser.Server.Implementations.Drawing filename += "datemodified=" + dateModified.Ticks; - if (format != ImageOutputFormat.Original) - { - filename += "f=" + format; - } + filename += "f=" + format; var hasIndicator = false; diff --git a/MediaBrowser.Server.Implementations/HttpServer/Security/AuthService.cs b/MediaBrowser.Server.Implementations/HttpServer/Security/AuthService.cs index 295f0c775..dde61079b 100644 --- a/MediaBrowser.Server.Implementations/HttpServer/Security/AuthService.cs +++ b/MediaBrowser.Server.Implementations/HttpServer/Security/AuthService.cs @@ -63,17 +63,14 @@ namespace MediaBrowser.Server.Implementations.HttpServer.Security // This code is executed before the service var auth = AuthorizationContext.GetAuthorizationInfo(req); - if (!authAttribtues.AllowLocal || !req.IsLocal) + if (!string.IsNullOrWhiteSpace(auth.Token) || + !_config.Configuration.InsecureApps6.Contains(auth.Client ?? string.Empty, StringComparer.OrdinalIgnoreCase)) { - if (!string.IsNullOrWhiteSpace(auth.Token) || - !_config.Configuration.InsecureApps6.Contains(auth.Client ?? string.Empty, StringComparer.OrdinalIgnoreCase)) - { - var valid = IsValidConnectKey(auth.Token); + var valid = IsValidConnectKey(auth.Token); - if (!valid) - { - SessionManager.ValidateSecurityToken(auth.Token); - } + if (!valid) + { + SessionManager.ValidateSecurityToken(auth.Token); } } diff --git a/MediaBrowser.Server.Implementations/Library/UserManager.cs b/MediaBrowser.Server.Implementations/Library/UserManager.cs index 55311ed8d..9e3fd3f17 100644 --- a/MediaBrowser.Server.Implementations/Library/UserManager.cs +++ b/MediaBrowser.Server.Implementations/Library/UserManager.cs @@ -2,6 +2,7 @@ using MediaBrowser.Common.Events; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; +using MediaBrowser.Controller; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Connect; using MediaBrowser.Controller.Drawing; @@ -17,9 +18,11 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.Events; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Serialization; +using MediaBrowser.Model.Users; using MediaBrowser.Server.Implementations.Security; using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Security.Cryptography; @@ -65,7 +68,7 @@ namespace MediaBrowser.Server.Implementations.Library private readonly Func<IImageProcessor> _imageProcessorFactory; private readonly Func<IDtoService> _dtoServiceFactory; private readonly Func<IConnectManager> _connectFactory; - private readonly IApplicationHost _appHost; + private readonly IServerApplicationHost _appHost; /// <summary> /// Initializes a new instance of the <see cref="UserManager" /> class. @@ -73,7 +76,7 @@ namespace MediaBrowser.Server.Implementations.Library /// <param name="logger">The logger.</param> /// <param name="configurationManager">The configuration manager.</param> /// <param name="userRepository">The user repository.</param> - public UserManager(ILogger logger, IServerConfigurationManager configurationManager, IUserRepository userRepository, IXmlSerializer xmlSerializer, INetworkManager networkManager, Func<IImageProcessor> imageProcessorFactory, Func<IDtoService> dtoServiceFactory, Func<IConnectManager> connectFactory, IApplicationHost appHost) + public UserManager(ILogger logger, IServerConfigurationManager configurationManager, IUserRepository userRepository, IXmlSerializer xmlSerializer, INetworkManager networkManager, Func<IImageProcessor> imageProcessorFactory, Func<IDtoService> dtoServiceFactory, Func<IConnectManager> connectFactory, IServerApplicationHost appHost) { _logger = logger; UserRepository = userRepository; @@ -85,6 +88,8 @@ namespace MediaBrowser.Server.Implementations.Library _appHost = appHost; ConfigurationManager = configurationManager; Users = new List<User>(); + + DeletePinFile(); } #region UserUpdated Event @@ -145,6 +150,16 @@ namespace MediaBrowser.Server.Implementations.Library return GetUserById(new Guid(id)); } + public User GetUserByName(string name) + { + if (string.IsNullOrWhiteSpace(name)) + { + throw new ArgumentNullException("name"); + } + + return Users.FirstOrDefault(u => string.Equals(u.Name, name, StringComparison.OrdinalIgnoreCase)); + } + public async Task Initialize() { Users = await LoadUsers().ConfigureAwait(false); @@ -599,5 +614,157 @@ namespace MediaBrowser.Server.Implementations.Library EventHelper.FireEventIfNotNull(UserConfigurationUpdated, this, new GenericEventArgs<User> { Argument = user }, _logger); } + + private string PasswordResetFile + { + get { return Path.Combine(ConfigurationManager.ApplicationPaths.ProgramDataPath, "passwordreset.txt"); } + } + + private string _lastPin; + private PasswordPinCreationResult _lastPasswordPinCreationResult; + private int _pinAttempts; + + private PasswordPinCreationResult CreatePasswordResetPin() + { + var num = new Random().Next(1, 9999); + + var path = PasswordResetFile; + + var pin = num.ToString("0000", CultureInfo.InvariantCulture); + _lastPin = pin; + + var time = TimeSpan.FromMinutes(5); + var expiration = DateTime.UtcNow.Add(time); + + var text = new StringBuilder(); + + var info = _appHost.GetSystemInfo(); + var localAddress = info.LocalAddress ?? string.Empty; + + text.AppendLine("Use your web browser to visit:"); + text.AppendLine(string.Empty); + text.AppendLine(localAddress + "/mediabrowser/web/forgotpasswordpin.html"); + text.AppendLine(string.Empty); + text.AppendLine("Enter the following pin code:"); + text.AppendLine(string.Empty); + text.AppendLine(pin); + text.AppendLine(string.Empty); + text.AppendLine("The pin code will expire at " + expiration.ToLocalTime().ToShortDateString() + " " + expiration.ToLocalTime().ToShortTimeString()); + + File.WriteAllText(path, text.ToString(), Encoding.UTF8); + + var result = new PasswordPinCreationResult + { + PinFile = path, + ExpirationDate = expiration + }; + + _lastPasswordPinCreationResult = result; + _pinAttempts = 0; + + return result; + } + + public ForgotPasswordResult StartForgotPasswordProcess(string enteredUsername, bool isInNetwork) + { + DeletePinFile(); + + var user = string.IsNullOrWhiteSpace(enteredUsername) ? + null : + GetUserByName(enteredUsername); + + if (user != null && user.ConnectLinkType.HasValue && user.ConnectLinkType.Value == UserLinkType.Guest) + { + throw new ArgumentException("Unable to process forgot password request for guests."); + } + + var action = ForgotPasswordAction.InNetworkRequired; + string pinFile = null; + DateTime? expirationDate = null; + + if (user != null && !user.Configuration.IsAdministrator) + { + action = ForgotPasswordAction.ContactAdmin; + } + else + { + if (isInNetwork) + { + action = ForgotPasswordAction.PinCode; + } + + var result = CreatePasswordResetPin(); + pinFile = result.PinFile; + expirationDate = result.ExpirationDate; + } + + return new ForgotPasswordResult + { + Action = action, + PinFile = pinFile, + PinExpirationDate = expirationDate + }; + } + + public async Task<PinRedeemResult> RedeemPasswordResetPin(string pin) + { + DeletePinFile(); + + var usersReset = new List<string>(); + + var valid = !string.IsNullOrWhiteSpace(_lastPin) && + string.Equals(_lastPin, pin, StringComparison.OrdinalIgnoreCase) && + _lastPasswordPinCreationResult != null && + _lastPasswordPinCreationResult.ExpirationDate > DateTime.UtcNow; + + if (valid) + { + _lastPin = null; + _lastPasswordPinCreationResult = null; + + var users = Users.Where(i => !i.ConnectLinkType.HasValue || i.ConnectLinkType.Value != UserLinkType.Guest) + .ToList(); + + foreach (var user in users) + { + await ResetPassword(user).ConfigureAwait(false); + usersReset.Add(user.Name); + } + } + else + { + _pinAttempts++; + if (_pinAttempts >= 3) + { + _lastPin = null; + _lastPasswordPinCreationResult = null; + } + } + + return new PinRedeemResult + { + Success = valid, + UsersReset = usersReset.ToArray() + }; + } + + private void DeletePinFile() + { + try + { + File.Delete(PasswordResetFile); + } + catch + { + + } + } + + class PasswordPinCreationResult + { + public string PinFile { get; set; } + public DateTime ExpirationDate { get; set; } + } + } } diff --git a/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json b/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json index 5920ab6f0..a484cbbb8 100644 --- a/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json +++ b/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json @@ -624,5 +624,12 @@ "MessageLoggedOutParentalControl": "Access is currently restricted. Please try again later.", "DefaultErrorMessage": "There was an error processing the request. Please try again later.", "ButtonAccept": "Accept", - "ButtonReject": "Reject" + "ButtonReject": "Reject", + "HeaderForgotPassword": "Forgot Password", + "MessageContactAdminToResetPassword": "Please contact your system administrator to reset your password.", + "MessageForgotPasswordInNetworkRequired": "Please try again within your home network to initiate the password reset process.", + "MessageForgotPasswordFileCreated": "The following file has been created on your server and contains instructions on how to proceed:", + "MessageForgotPasswordFileExpiration": "The reset pin will expire at {0}.", + "MessageInvalidForgotPasswordPin": "An invalid or expired pin was entered. Please try again.", + "MessagePasswordResetForUsers": "Passwords have been reset for the following users:" } diff --git a/MediaBrowser.Server.Implementations/Localization/Server/server.json b/MediaBrowser.Server.Implementations/Localization/Server/server.json index 099f3f75b..307490a19 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/server.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/server.json @@ -1270,5 +1270,11 @@ "LabelSelectLastestItemsFolders": "Include media from the following sections in Latest Items", "HeaderShareMediaFolders": "Share Media Folders", "MessageGuestSharingPermissionsHelp": "Most features are initially unavailable to guests but can be enabled as needed.", - "HeaderInvitations": "Invitations" + "HeaderInvitations": "Invitations", + "LabelForgotPasswordUsernameHelp": "Enter your username, if you remember it.", + "HeaderForgotPassword": "Forgot Password", + "TitleForgotPassword": "Forgot Password", + "TitlePasswordReset": "Password Reset", + "LabelPasswordRecoveryPinCode": "Pin code:", + "HeaderPasswordReset": "Password Reset" } diff --git a/MediaBrowser.Server.Implementations/Session/SessionManager.cs b/MediaBrowser.Server.Implementations/Session/SessionManager.cs index ac8068744..d6db836cc 100644 --- a/MediaBrowser.Server.Implementations/Session/SessionManager.cs +++ b/MediaBrowser.Server.Implementations/Session/SessionManager.cs @@ -1300,23 +1300,16 @@ namespace MediaBrowser.Server.Implementations.Session /// Authenticates the new session. /// </summary> /// <param name="request">The request.</param> - /// <param name="isLocal">if set to <c>true</c> [is local].</param> /// <returns>Task{SessionInfo}.</returns> /// <exception cref="AuthenticationException">Invalid user or password entered.</exception> /// <exception cref="System.UnauthorizedAccessException">Invalid user or password entered.</exception> /// <exception cref="UnauthorizedAccessException">Invalid user or password entered.</exception> - public async Task<AuthenticationResult> AuthenticateNewSession(AuthenticationRequest request, - bool isLocal) + public async Task<AuthenticationResult> AuthenticateNewSession(AuthenticationRequest request) { var user = _userManager.Users .FirstOrDefault(i => string.Equals(request.Username, i.Name, StringComparison.OrdinalIgnoreCase)); - var allowWithoutPassword = isLocal && - string.Equals(request.App, "Dashboard", StringComparison.OrdinalIgnoreCase) - && !(user != null && user.ConnectLinkType.HasValue && user.ConnectLinkType.Value == UserLinkType.Guest); - - var result = allowWithoutPassword || - await _userManager.AuthenticateUser(request.Username, request.PasswordSha1, request.PasswordMd5, request.RemoteEndPoint).ConfigureAwait(false); + var result = await _userManager.AuthenticateUser(request.Username, request.PasswordSha1, request.PasswordMd5, request.RemoteEndPoint).ConfigureAwait(false); if (!result) { |
