aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Server.Implementations/Library/UserManager.cs
diff options
context:
space:
mode:
Diffstat (limited to 'MediaBrowser.Server.Implementations/Library/UserManager.cs')
-rw-r--r--MediaBrowser.Server.Implementations/Library/UserManager.cs528
1 files changed, 490 insertions, 38 deletions
diff --git a/MediaBrowser.Server.Implementations/Library/UserManager.cs b/MediaBrowser.Server.Implementations/Library/UserManager.cs
index 16a1dc516..503af4970 100644
--- a/MediaBrowser.Server.Implementations/Library/UserManager.cs
+++ b/MediaBrowser.Server.Implementations/Library/UserManager.cs
@@ -1,22 +1,28 @@
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;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;
+using MediaBrowser.Model.Connect;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Events;
using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Serialization;
-using MediaBrowser.Server.Implementations.Security;
+using MediaBrowser.Model.Users;
using System;
using System.Collections.Generic;
+using System.Globalization;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
@@ -56,19 +62,16 @@ namespace MediaBrowser.Server.Implementations.Library
public event EventHandler<GenericEventArgs<User>> UserPasswordChanged;
private readonly IXmlSerializer _xmlSerializer;
+ private readonly IJsonSerializer _jsonSerializer;
private readonly INetworkManager _networkManager;
private readonly Func<IImageProcessor> _imageProcessorFactory;
private readonly Func<IDtoService> _dtoServiceFactory;
+ private readonly Func<IConnectManager> _connectFactory;
+ private readonly IServerApplicationHost _appHost;
- /// <summary>
- /// Initializes a new instance of the <see cref="UserManager" /> class.
- /// </summary>
- /// <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)
+ public UserManager(ILogger logger, IServerConfigurationManager configurationManager, IUserRepository userRepository, IXmlSerializer xmlSerializer, INetworkManager networkManager, Func<IImageProcessor> imageProcessorFactory, Func<IDtoService> dtoServiceFactory, Func<IConnectManager> connectFactory, IServerApplicationHost appHost, IJsonSerializer jsonSerializer)
{
_logger = logger;
UserRepository = userRepository;
@@ -76,8 +79,13 @@ namespace MediaBrowser.Server.Implementations.Library
_networkManager = networkManager;
_imageProcessorFactory = imageProcessorFactory;
_dtoServiceFactory = dtoServiceFactory;
+ _connectFactory = connectFactory;
+ _appHost = appHost;
+ _jsonSerializer = jsonSerializer;
ConfigurationManager = configurationManager;
Users = new List<User>();
+
+ DeletePinFile();
}
#region UserUpdated Event
@@ -138,30 +146,94 @@ 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);
+
+ foreach (var user in Users.ToList())
+ {
+ await DoPolicyMigration(user).ConfigureAwait(false);
+ }
}
- public async Task<bool> AuthenticateUser(string username, string password, string remoteEndPoint)
+ public Task<bool> AuthenticateUser(string username, string passwordSha1, string remoteEndPoint)
+ {
+ return AuthenticateUser(username, passwordSha1, null, remoteEndPoint);
+ }
+
+ public bool IsValidUsername(string username)
+ {
+ // Usernames can contain letters (a-z), numbers (0-9), dashes (-), underscores (_), apostrophes ('), and periods (.)
+ return username.All(IsValidCharacter);
+ }
+
+ private bool IsValidCharacter(char i)
+ {
+ return char.IsLetterOrDigit(i) || char.Equals(i, '-') || char.Equals(i, '_') || char.Equals(i, '\'') ||
+ char.Equals(i, '.');
+ }
+
+ public string MakeValidUsername(string username)
+ {
+ if (IsValidUsername(username))
+ {
+ return username;
+ }
+
+ // Usernames can contain letters (a-z), numbers (0-9), dashes (-), underscores (_), apostrophes ('), and periods (.)
+ var builder = new StringBuilder();
+
+ foreach (var c in username)
+ {
+ if (IsValidCharacter(c))
+ {
+ builder.Append(c);
+ }
+ }
+ return builder.ToString();
+ }
+
+ public async Task<bool> AuthenticateUser(string username, string passwordSha1, string passwordMd5, string remoteEndPoint)
{
if (string.IsNullOrWhiteSpace(username))
{
throw new ArgumentNullException("username");
}
- var user = Users.First(i => string.Equals(username, i.Name, StringComparison.OrdinalIgnoreCase));
+ var user = Users
+ .FirstOrDefault(i => string.Equals(username, i.Name, StringComparison.OrdinalIgnoreCase));
+
+ if (user == null)
+ {
+ throw new SecurityException("Invalid username or password entered.");
+ }
- if (user.Configuration.IsDisabled)
+ if (user.Policy.IsDisabled)
{
- throw new AuthenticationException(string.Format("The {0} account is currently disabled. Please consult with your administrator.", user.Name));
+ throw new SecurityException(string.Format("The {0} account is currently disabled. Please consult with your administrator.", user.Name));
}
- var success = string.Equals(GetPasswordHash(user), password.Replace("-", string.Empty), StringComparison.OrdinalIgnoreCase);
+ var success = false;
- if (!success && _networkManager.IsInLocalNetwork(remoteEndPoint) && user.Configuration.EnableLocalPassword)
+ // Authenticate using local credentials if not a guest
+ if (!user.ConnectLinkType.HasValue || user.ConnectLinkType.Value != UserLinkType.Guest)
{
- success = string.Equals(GetLocalPasswordHash(user), password.Replace("-", string.Empty), StringComparison.OrdinalIgnoreCase);
+ success = string.Equals(GetPasswordHash(user), passwordSha1.Replace("-", string.Empty), StringComparison.OrdinalIgnoreCase);
+
+ if (!success && _networkManager.IsInLocalNetwork(remoteEndPoint) && user.Configuration.EnableLocalPassword)
+ {
+ success = string.Equals(GetLocalPasswordHash(user), passwordSha1.Replace("-", string.Empty), StringComparison.OrdinalIgnoreCase);
+ }
}
// Update LastActivityDate and LastLoginDate, then save
@@ -220,9 +292,9 @@ namespace MediaBrowser.Server.Implementations.Library
// There always has to be at least one user.
if (users.Count == 0)
{
- var name = Environment.UserName;
+ var name = MakeValidUsername(Environment.UserName);
- var user = InstantiateNewUser(name);
+ var user = InstantiateNewUser(name, false);
user.DateLastSaved = DateTime.UtcNow;
@@ -230,13 +302,42 @@ namespace MediaBrowser.Server.Implementations.Library
users.Add(user);
- user.Configuration.IsAdministrator = true;
- UpdateConfiguration(user, user.Configuration);
+ user.Policy.IsAdministrator = true;
+ user.Policy.EnableRemoteControlOfOtherUsers = true;
+ await UpdateUserPolicy(user, user.Policy, false).ConfigureAwait(false);
}
return users;
}
+ private async Task DoPolicyMigration(User user)
+ {
+ if (!user.Configuration.HasMigratedToPolicy)
+ {
+ user.Policy.AccessSchedules = user.Configuration.AccessSchedules;
+ user.Policy.BlockedChannels = user.Configuration.BlockedChannels;
+ user.Policy.BlockedMediaFolders = user.Configuration.BlockedMediaFolders;
+ user.Policy.BlockedTags = user.Configuration.BlockedTags;
+ user.Policy.BlockUnratedItems = user.Configuration.BlockUnratedItems;
+ user.Policy.EnableContentDeletion = user.Configuration.EnableContentDeletion;
+ user.Policy.EnableLiveTvAccess = user.Configuration.EnableLiveTvAccess;
+ user.Policy.EnableLiveTvManagement = user.Configuration.EnableLiveTvManagement;
+ user.Policy.EnableMediaPlayback = user.Configuration.EnableMediaPlayback;
+ user.Policy.EnableRemoteControlOfOtherUsers = user.Configuration.EnableRemoteControlOfOtherUsers;
+ user.Policy.EnableSharedDeviceControl = user.Configuration.EnableSharedDeviceControl;
+ user.Policy.EnableUserPreferenceAccess = user.Configuration.EnableUserPreferenceAccess;
+ user.Policy.IsAdministrator = user.Configuration.IsAdministrator;
+ user.Policy.IsDisabled = user.Configuration.IsDisabled;
+ user.Policy.IsHidden = user.Configuration.IsHidden;
+ user.Policy.MaxParentalRating = user.Configuration.MaxParentalRating;
+
+ await UpdateUserPolicy(user.Id.ToString("N"), user.Policy);
+
+ user.Configuration.HasMigratedToPolicy = true;
+ await UpdateConfiguration(user, user.Configuration, true).ConfigureAwait(false);
+ }
+ }
+
public UserDto GetUserDto(User user, string remoteEndPoint = null)
{
if (user == null)
@@ -263,7 +364,9 @@ namespace MediaBrowser.Server.Implementations.Library
Configuration = user.Configuration,
ConnectLinkType = user.ConnectLinkType,
ConnectUserId = user.ConnectUserId,
- ConnectUserName = user.ConnectUserName
+ ConnectUserName = user.ConnectUserName,
+ ServerId = _appHost.SystemId,
+ Policy = user.Policy
};
var image = user.GetImageInfo(ImageType.Primary, 0);
@@ -274,7 +377,10 @@ namespace MediaBrowser.Server.Implementations.Library
try
{
- _dtoServiceFactory().AttachPrimaryImageAspectRatio(dto, user);
+ _dtoServiceFactory().AttachPrimaryImageAspectRatio(dto, user, new List<ItemFields>
+ {
+ ItemFields.PrimaryImageAspectRatio
+ });
}
catch (Exception ex)
{
@@ -390,6 +496,11 @@ namespace MediaBrowser.Server.Implementations.Library
throw new ArgumentNullException("name");
}
+ if (!IsValidUsername(name))
+ {
+ throw new ArgumentException("Usernames can contain letters (a-z), numbers (0-9), dashes (-), underscores (_), apostrophes ('), and periods (.)");
+ }
+
if (Users.Any(u => u.Name.Equals(name, StringComparison.OrdinalIgnoreCase)))
{
throw new ArgumentException(string.Format("A user with the name '{0}' already exists.", name));
@@ -399,7 +510,7 @@ namespace MediaBrowser.Server.Implementations.Library
try
{
- var user = InstantiateNewUser(name);
+ var user = InstantiateNewUser(name, true);
var list = Users.ToList();
list.Add(user);
@@ -433,6 +544,11 @@ namespace MediaBrowser.Server.Implementations.Library
throw new ArgumentNullException("user");
}
+ if (user.ConnectLinkType.HasValue)
+ {
+ await _connectFactory().RemoveConnect(user.Id.ToString("N")).ConfigureAwait(false);
+ }
+
var allUsers = Users.ToList();
if (allUsers.FirstOrDefault(u => u.Id == user.Id) == null)
@@ -445,7 +561,7 @@ namespace MediaBrowser.Server.Implementations.Library
throw new ArgumentException(string.Format("The user '{0}' cannot be deleted because there must be at least one user in the system.", user.Name));
}
- if (user.Configuration.IsAdministrator && allUsers.Count(i => i.Configuration.IsAdministrator) == 1)
+ if (user.Policy.IsAdministrator && allUsers.Count(i => i.Policy.IsAdministrator) == 1)
{
throw new ArgumentException(string.Format("The user '{0}' cannot be deleted because there must be at least one admin user in the system.", user.Name));
}
@@ -454,19 +570,21 @@ namespace MediaBrowser.Server.Implementations.Library
try
{
- await UserRepository.DeleteUser(user, CancellationToken.None).ConfigureAwait(false);
+ var configPath = GetConfigurationFilePath(user);
- var path = user.ConfigurationFilePath;
+ await UserRepository.DeleteUser(user, CancellationToken.None).ConfigureAwait(false);
try
{
- File.Delete(path);
+ File.Delete(configPath);
}
catch (IOException ex)
{
- _logger.ErrorException("Error deleting file {0}", ex, path);
+ _logger.ErrorException("Error deleting file {0}", ex, configPath);
}
+ DeleteUserPolicy(user);
+
// Force this to be lazy loaded again
Users = await LoadUsers().ConfigureAwait(false);
@@ -484,23 +602,38 @@ namespace MediaBrowser.Server.Implementations.Library
/// <returns>Task.</returns>
public Task ResetPassword(User user)
{
- return ChangePassword(user, string.Empty);
+ return ChangePassword(user, GetSha1String(string.Empty));
}
/// <summary>
/// Changes the password.
/// </summary>
/// <param name="user">The user.</param>
- /// <param name="newPassword">The new password.</param>
+ /// <param name="newPasswordSha1">The new password sha1.</param>
/// <returns>Task.</returns>
- public async Task ChangePassword(User user, string newPassword)
+ /// <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)
{
if (user == null)
{
throw new ArgumentNullException("user");
}
+ if (string.IsNullOrWhiteSpace(newPasswordSha1))
+ {
+ throw new ArgumentNullException("newPasswordSha1");
+ }
- user.Password = string.IsNullOrEmpty(newPassword) ? GetSha1String(string.Empty) : GetSha1String(newPassword);
+ if (user.ConnectLinkType.HasValue && user.ConnectLinkType.Value == UserLinkType.Guest)
+ {
+ throw new ArgumentException("Passwords for guests cannot be changed.");
+ }
+
+ user.Password = newPasswordSha1;
await UpdateUser(user).ConfigureAwait(false);
@@ -511,26 +644,345 @@ namespace MediaBrowser.Server.Implementations.Library
/// Instantiates the new user.
/// </summary>
/// <param name="name">The name.</param>
+ /// <param name="checkId">if set to <c>true</c> [check identifier].</param>
/// <returns>User.</returns>
- private User InstantiateNewUser(string name)
+ private User InstantiateNewUser(string name, bool checkId)
{
+ var id = ("MBUser" + name).GetMD5();
+
+ if (checkId && Users.Select(i => i.Id).Contains(id))
+ {
+ id = Guid.NewGuid();
+ }
+
return new User
{
Name = name,
- Id = ("MBUser" + name).GetMD5(),
+ Id = id,
DateCreated = DateTime.UtcNow,
DateModified = DateTime.UtcNow,
UsesIdForConfigurationPath = true
};
}
- public void UpdateConfiguration(User user, UserConfiguration newConfiguration)
+ 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)
{
- var xmlPath = user.ConfigurationFilePath;
- Directory.CreateDirectory(Path.GetDirectoryName(xmlPath));
- _xmlSerializer.SerializeToFile(newConfiguration, xmlPath);
+ DeletePinFile();
- EventHelper.FireEventIfNotNull(UserConfigurationUpdated, this, new GenericEventArgs<User> { Argument = user }, _logger);
+ 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.Policy.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; }
+ }
+
+ public UserPolicy GetUserPolicy(User user)
+ {
+ var path = GetPolifyFilePath(user);
+
+ try
+ {
+ lock (_policySyncLock)
+ {
+ return (UserPolicy)_xmlSerializer.DeserializeFromFile(typeof(UserPolicy), path);
+ }
+ }
+ catch (DirectoryNotFoundException)
+ {
+ return GetDefaultPolicy(user);
+ }
+ catch (FileNotFoundException)
+ {
+ return GetDefaultPolicy(user);
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error reading policy file: {0}", ex, path);
+
+ return GetDefaultPolicy(user);
+ }
+ }
+
+ private UserPolicy GetDefaultPolicy(User user)
+ {
+ return new UserPolicy
+ {
+ EnableSync = true
+ };
+ }
+
+ private readonly object _policySyncLock = new object();
+ public Task UpdateUserPolicy(string userId, UserPolicy userPolicy)
+ {
+ var user = GetUserById(userId);
+ return UpdateUserPolicy(user, userPolicy, true);
+ }
+
+ private async Task UpdateUserPolicy(User user, UserPolicy userPolicy, bool fireEvent)
+ {
+ // The xml serializer will output differently if the type is not exact
+ if (userPolicy.GetType() != typeof(UserPolicy))
+ {
+ var json = _jsonSerializer.SerializeToString(userPolicy);
+ userPolicy = _jsonSerializer.DeserializeFromString<UserPolicy>(json);
+ }
+
+ var updateConfig = user.Policy.IsAdministrator != userPolicy.IsAdministrator ||
+ user.Policy.EnableLiveTvManagement != userPolicy.EnableLiveTvManagement ||
+ user.Policy.EnableLiveTvAccess != userPolicy.EnableLiveTvAccess ||
+ user.Policy.EnableMediaPlayback != userPolicy.EnableMediaPlayback ||
+ user.Policy.EnableContentDeletion != userPolicy.EnableContentDeletion;
+
+ var path = GetPolifyFilePath(user);
+
+ Directory.CreateDirectory(Path.GetDirectoryName(path));
+
+ lock (_policySyncLock)
+ {
+ _xmlSerializer.SerializeToFile(userPolicy, path);
+ user.Policy = userPolicy;
+ }
+
+ if (updateConfig)
+ {
+ user.Configuration.IsAdministrator = user.Policy.IsAdministrator;
+ user.Configuration.EnableLiveTvManagement = user.Policy.EnableLiveTvManagement;
+ user.Configuration.EnableLiveTvAccess = user.Policy.EnableLiveTvAccess;
+ user.Configuration.EnableMediaPlayback = user.Policy.EnableMediaPlayback;
+ user.Configuration.EnableContentDeletion = user.Policy.EnableContentDeletion;
+
+ await UpdateConfiguration(user, user.Configuration, true).ConfigureAwait(false);
+ }
+ }
+
+ private void DeleteUserPolicy(User user)
+ {
+ var path = GetPolifyFilePath(user);
+
+ try
+ {
+ lock (_policySyncLock)
+ {
+ File.Delete(path);
+ }
+ }
+ catch (IOException)
+ {
+
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error deleting policy file", ex);
+ }
+ }
+
+ private string GetPolifyFilePath(User user)
+ {
+ return Path.Combine(user.ConfigurationDirectoryPath, "policy.xml");
+ }
+
+ private string GetConfigurationFilePath(User user)
+ {
+ return Path.Combine(user.ConfigurationDirectoryPath, "config.xml");
+ }
+
+ public UserConfiguration GetUserConfiguration(User user)
+ {
+ var path = GetConfigurationFilePath(user);
+
+ try
+ {
+ lock (_configSyncLock)
+ {
+ return (UserConfiguration)_xmlSerializer.DeserializeFromFile(typeof(UserConfiguration), path);
+ }
+ }
+ catch (DirectoryNotFoundException)
+ {
+ return new UserConfiguration();
+ }
+ catch (FileNotFoundException)
+ {
+ return new UserConfiguration();
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error reading policy file: {0}", ex, path);
+
+ return new UserConfiguration();
+ }
+ }
+
+ private readonly object _configSyncLock = new object();
+ public Task UpdateConfiguration(string userId, UserConfiguration config)
+ {
+ var user = GetUserById(userId);
+ return UpdateConfiguration(user, config, true);
+ }
+
+ private async Task UpdateConfiguration(User user, UserConfiguration config, bool fireEvent)
+ {
+ var path = GetConfigurationFilePath(user);
+
+ // The xml serializer will output differently if the type is not exact
+ if (config.GetType() != typeof (UserConfiguration))
+ {
+ var json = _jsonSerializer.SerializeToString(config);
+ config = _jsonSerializer.DeserializeFromString<UserConfiguration>(json);
+ }
+
+ Directory.CreateDirectory(Path.GetDirectoryName(path));
+
+ lock (_configSyncLock)
+ {
+ _xmlSerializer.SerializeToFile(config, path);
+ user.Configuration = config;
+ }
+
+ if (fireEvent)
+ {
+ EventHelper.FireEventIfNotNull(UserConfigurationUpdated, this, new GenericEventArgs<User> { Argument = user }, _logger);
+ }
}
}
}