aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MediaBrowser.Api/UserService.cs80
-rw-r--r--MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj6
-rw-r--r--MediaBrowser.Model/Configuration/ManualLoginCategory.cs10
-rw-r--r--MediaBrowser.Model/Configuration/ServerConfiguration.cs8
-rw-r--r--MediaBrowser.Model/Configuration/UserConfiguration.cs10
-rw-r--r--MediaBrowser.Model/MediaBrowser.Model.csproj2
-rw-r--r--MediaBrowser.Model/Users/AuthenticationResult.cs9
-rw-r--r--MediaBrowser.Server.Implementations/Library/UserManager.cs12
-rw-r--r--MediaBrowser.Server.Implementations/Session/SessionManager.cs5
-rw-r--r--MediaBrowser.WebDashboard/Api/DashboardService.cs18
-rw-r--r--MediaBrowser.WebDashboard/ApiClient.js31
-rw-r--r--MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj6
-rw-r--r--MediaBrowser.WebDashboard/packages.config2
13 files changed, 180 insertions, 19 deletions
diff --git a/MediaBrowser.Api/UserService.cs b/MediaBrowser.Api/UserService.cs
index 54cf18396..87e1630aa 100644
--- a/MediaBrowser.Api/UserService.cs
+++ b/MediaBrowser.Api/UserService.cs
@@ -3,6 +3,7 @@ using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Serialization;
+using MediaBrowser.Model.Users;
using ServiceStack.ServiceHost;
using ServiceStack.Text.Controller;
using System;
@@ -19,6 +20,11 @@ namespace MediaBrowser.Api
[Api(Description = "Gets a list of users")]
public class GetUsers : IReturn<List<UserDto>>
{
+ [ApiMember(Name = "IsHidden", Description="Optional filter by IsHidden=true or false", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
+ public bool? IsHidden { get; set; }
+
+ [ApiMember(Name = "IsDisabled", Description = "Optional filter by IsDisabled=true or false", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
+ public bool? IsDisabled { get; set; }
}
/// <summary>
@@ -56,7 +62,7 @@ namespace MediaBrowser.Api
/// </summary>
[Route("/Users/{Id}/Authenticate", "POST")]
[Api(Description = "Authenticates a user")]
- public class AuthenticateUser : IReturnVoid
+ public class AuthenticateUser : IReturn<AuthenticationResult>
{
/// <summary>
/// Gets or sets the id.
@@ -74,6 +80,28 @@ namespace MediaBrowser.Api
}
/// <summary>
+ /// Class AuthenticateUser
+ /// </summary>
+ [Route("/Users/{Name}/AuthenticateByName", "POST")]
+ [Api(Description = "Authenticates a user")]
+ public class AuthenticateUserByName : IReturn<AuthenticationResult>
+ {
+ /// <summary>
+ /// Gets or sets the id.
+ /// </summary>
+ /// <value>The id.</value>
+ [ApiMember(Name = "Name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
+ public string Name { get; set; }
+
+ /// <summary>
+ /// Gets or sets the password.
+ /// </summary>
+ /// <value>The password.</value>
+ [ApiMember(Name = "Password", IsRequired = true, DataType = "string", ParameterType = "body", Verb = "POST")]
+ public string Password { get; set; }
+ }
+
+ /// <summary>
/// Class UpdateUserPassword
/// </summary>
[Route("/Users/{Id}/Password", "POST")]
@@ -168,11 +196,21 @@ namespace MediaBrowser.Api
{
var dtoBuilder = new UserDtoBuilder(Logger);
- var tasks = _userManager.Users.OrderBy(u => u.Name).Select(dtoBuilder.GetUserDto);
+ var users = _userManager.Users;
+
+ if (request.IsDisabled.HasValue)
+ {
+ users = users.Where(i => i.Configuration.IsDisabled == request.IsDisabled.Value);
+ }
+
+ if (request.IsHidden.HasValue)
+ {
+ users = users.Where(i => i.Configuration.IsHidden == request.IsHidden.Value);
+ }
- var users = tasks.Select(i => i.Result).ToList();
+ var tasks = users.OrderBy(u => u.Name).Select(dtoBuilder.GetUserDto).Select(i => i.Result);
- return ToOptimizedResult(users);
+ return ToOptimizedResult(tasks.ToList());
}
/// <summary>
@@ -218,7 +256,21 @@ namespace MediaBrowser.Api
/// Posts the specified request.
/// </summary>
/// <param name="request">The request.</param>
- public void Post(AuthenticateUser request)
+ public object Post(AuthenticateUser request)
+ {
+ var result = AuthenticateUser(request).Result;
+
+ return ToOptimizedResult(result);
+ }
+
+ public object Post(AuthenticateUserByName request)
+ {
+ var user = _userManager.Users.FirstOrDefault(i => string.Equals(request.Name, i.Name, StringComparison.OrdinalIgnoreCase));
+
+ return AuthenticateUser(new AuthenticateUser { Id = user.Id, Password = request.Password }).Result;
+ }
+
+ private async Task<object> AuthenticateUser(AuthenticateUser request)
{
var user = _userManager.GetUserById(request.Id);
@@ -227,13 +279,20 @@ namespace MediaBrowser.Api
throw new ResourceNotFoundException("User not found");
}
- var success = _userManager.AuthenticateUser(user, request.Password).Result;
+ var success = await _userManager.AuthenticateUser(user, request.Password).ConfigureAwait(false);
if (!success)
{
// Unauthorized
throw new UnauthorizedAccessException("Invalid user or password entered.");
}
+
+ var result = new AuthenticationResult
+ {
+ User = await new UserDtoBuilder(Logger).GetUserDto(user).ConfigureAwait(false)
+ };
+
+ return ToOptimizedResult(result);
}
/// <summary>
@@ -294,6 +353,15 @@ namespace MediaBrowser.Api
}
}
+ // If removing admin access
+ if (dtoUser.Configuration.IsDisabled && !user.Configuration.IsDisabled)
+ {
+ if (_userManager.Users.Count(i => !i.Configuration.IsDisabled) == 1)
+ {
+ throw new ArgumentException("There must be at least one enabled user in the system.");
+ }
+ }
+
var task = user.Name.Equals(dtoUser.Name, StringComparison.Ordinal) ? _userManager.UpdateUser(user) : _userManager.RenameUser(user, dtoUser.Name);
Task.WaitAll(task);
diff --git a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj
index 78ac12714..9c534f916 100644
--- a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj
+++ b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj
@@ -52,6 +52,9 @@
<Compile Include="..\MediaBrowser.Model\Configuration\BaseApplicationConfiguration.cs">
<Link>Configuration\BaseApplicationConfiguration.cs</Link>
</Compile>
+ <Compile Include="..\MediaBrowser.Model\Configuration\ManualLoginCategory.cs">
+ <Link>Configuration\ManualLoginCategory.cs</Link>
+ </Compile>
<Compile Include="..\MediaBrowser.Model\Configuration\ServerConfiguration.cs">
<Link>Configuration\ServerConfiguration.cs</Link>
</Compile>
@@ -331,6 +334,9 @@
<Compile Include="..\MediaBrowser.Model\Updates\PackageVersionInfo.cs">
<Link>Updates\PackageVersionInfo.cs</Link>
</Compile>
+ <Compile Include="..\MediaBrowser.Model\Users\AuthenticationResult.cs">
+ <Link>Users\AuthenticationResult.cs</Link>
+ </Compile>
<Compile Include="..\MediaBrowser.Model\Weather\WeatherUnits.cs">
<Link>Weather\WeatherUnits.cs</Link>
</Compile>
diff --git a/MediaBrowser.Model/Configuration/ManualLoginCategory.cs b/MediaBrowser.Model/Configuration/ManualLoginCategory.cs
new file mode 100644
index 000000000..20e873437
--- /dev/null
+++ b/MediaBrowser.Model/Configuration/ManualLoginCategory.cs
@@ -0,0 +1,10 @@
+
+namespace MediaBrowser.Model.Configuration
+{
+ public enum ManualLoginCategory
+ {
+ Mobile,
+ MediaBrowserTheater,
+ Roku
+ }
+}
diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs
index fadb4aefa..2cb0eef48 100644
--- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs
+++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs
@@ -1,6 +1,6 @@
using MediaBrowser.Model.Entities;
-using System;
using MediaBrowser.Model.Weather;
+using System;
namespace MediaBrowser.Model.Configuration
{
@@ -20,7 +20,7 @@ namespace MediaBrowser.Model.Configuration
/// </summary>
/// <value>The weather unit.</value>
public WeatherUnits WeatherUnit { get; set; }
-
+
/// <summary>
/// Gets or sets a value indicating whether [enable HTTP level logging].
/// </summary>
@@ -223,6 +223,8 @@ namespace MediaBrowser.Model.Configuration
/// <value>The dashboard source path.</value>
public string DashboardSourcePath { get; set; }
+ public ManualLoginCategory[] ManualLoginClients { get; set; }
+
/// <summary>
/// Initializes a new instance of the <see cref="ServerConfiguration" /> class.
/// </summary>
@@ -249,6 +251,8 @@ namespace MediaBrowser.Model.Configuration
EnableInternetProviders = true; //initial installs will need these
InternetProviderExcludeTypes = new string[] { };
+ ManualLoginClients = new ManualLoginCategory[] { };
+
MetadataRefreshDays = 30;
PreferredMetadataLanguage = "en";
MetadataCountryCode = "US";
diff --git a/MediaBrowser.Model/Configuration/UserConfiguration.cs b/MediaBrowser.Model/Configuration/UserConfiguration.cs
index 45ef03b8d..35e9b3b0f 100644
--- a/MediaBrowser.Model/Configuration/UserConfiguration.cs
+++ b/MediaBrowser.Model/Configuration/UserConfiguration.cs
@@ -47,7 +47,15 @@ namespace MediaBrowser.Model.Configuration
/// </summary>
/// <value><c>true</c> if [use forced subtitles only]; otherwise, <c>false</c>.</value>
public bool UseForcedSubtitlesOnly { get; set; }
-
+
+ /// <summary>
+ /// Gets or sets a value indicating whether this instance is hidden.
+ /// </summary>
+ /// <value><c>true</c> if this instance is hidden; otherwise, <c>false</c>.</value>
+ public bool IsHidden { get; set; }
+
+ public bool IsDisabled { get; set; }
+
/// <summary>
/// Initializes a new instance of the <see cref="UserConfiguration" /> class.
/// </summary>
diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj
index b59e54bcf..2e7d41f87 100644
--- a/MediaBrowser.Model/MediaBrowser.Model.csproj
+++ b/MediaBrowser.Model/MediaBrowser.Model.csproj
@@ -43,6 +43,7 @@
</Compile>
<Compile Include="ApiClient\IApiClient.cs" />
<Compile Include="Configuration\BaseApplicationConfiguration.cs" />
+ <Compile Include="Configuration\ManualLoginCategory.cs" />
<Compile Include="Configuration\ServerConfiguration.cs" />
<Compile Include="Dto\BaseItemPerson.cs" />
<Compile Include="Dto\ChapterInfoDto.cs" />
@@ -141,6 +142,7 @@
<Compile Include="Tasks\TaskTriggerInfo.cs" />
<Compile Include="Updates\PackageInfo.cs" />
<Compile Include="Updates\PackageVersionInfo.cs" />
+ <Compile Include="Users\AuthenticationResult.cs" />
<Compile Include="Weather\WeatherUnits.cs" />
<Compile Include="Web\QueryStringDictionary.cs" />
<None Include="FodyWeavers.xml" />
diff --git a/MediaBrowser.Model/Users/AuthenticationResult.cs b/MediaBrowser.Model/Users/AuthenticationResult.cs
new file mode 100644
index 000000000..ea6b57e06
--- /dev/null
+++ b/MediaBrowser.Model/Users/AuthenticationResult.cs
@@ -0,0 +1,9 @@
+using MediaBrowser.Model.Dto;
+
+namespace MediaBrowser.Model.Users
+{
+ public class AuthenticationResult
+ {
+ public UserDto User { get; set; }
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/Library/UserManager.cs b/MediaBrowser.Server.Implementations/Library/UserManager.cs
index 8aedca50f..0227f883a 100644
--- a/MediaBrowser.Server.Implementations/Library/UserManager.cs
+++ b/MediaBrowser.Server.Implementations/Library/UserManager.cs
@@ -1,10 +1,7 @@
-using System.IO;
-using MediaBrowser.Common.Events;
+using MediaBrowser.Common.Events;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Session;
@@ -12,12 +9,12 @@ using MediaBrowser.Model.Logging;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
+using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
-using MediaBrowser.Model.Session;
namespace MediaBrowser.Server.Implementations.Library
{
@@ -174,6 +171,11 @@ namespace MediaBrowser.Server.Implementations.Library
throw new ArgumentNullException("user");
}
+ if (user.Configuration.IsDisabled)
+ {
+ throw new UnauthorizedAccessException(string.Format("The {0} account is currently disabled. Please consult with your administrator.", user.Name));
+ }
+
var existingPasswordString = string.IsNullOrEmpty(user.Password) ? GetSha1String(string.Empty) : user.Password;
var success = string.Equals(existingPasswordString, password.Replace("-", string.Empty), StringComparison.OrdinalIgnoreCase);
diff --git a/MediaBrowser.Server.Implementations/Session/SessionManager.cs b/MediaBrowser.Server.Implementations/Session/SessionManager.cs
index dda9658d4..d9f28915a 100644
--- a/MediaBrowser.Server.Implementations/Session/SessionManager.cs
+++ b/MediaBrowser.Server.Implementations/Session/SessionManager.cs
@@ -101,6 +101,11 @@ namespace MediaBrowser.Server.Implementations.Session
/// <exception cref="System.ArgumentNullException">user</exception>
public Task LogConnectionActivity(string clientType, string deviceId, string deviceName, User user)
{
+ if (user != null && user.Configuration.IsDisabled)
+ {
+ throw new UnauthorizedAccessException(string.Format("The {0} account is currently disabled. Please consult with your administrator.", user.Name));
+ }
+
var activityDate = DateTime.UtcNow;
GetConnection(clientType, deviceId, deviceName, user).LastActivityDate = activityDate;
diff --git a/MediaBrowser.WebDashboard/Api/DashboardService.cs b/MediaBrowser.WebDashboard/Api/DashboardService.cs
index 0281ee6ad..89aaec4b2 100644
--- a/MediaBrowser.WebDashboard/Api/DashboardService.cs
+++ b/MediaBrowser.WebDashboard/Api/DashboardService.cs
@@ -5,7 +5,6 @@ using MediaBrowser.Common.ScheduledTasks;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dto;
-using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Plugins;
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Logging;
@@ -221,7 +220,21 @@ namespace MediaBrowser.WebDashboard.Api
/// <returns>System.Object.</returns>
public object Get(GetDashboardConfigurationPages request)
{
- var pages = ServerEntryPoint.Instance.PluginConfigurationPages;
+ const string unavilableMessage = "The server is still loading. Please try again momentarily.";
+
+ var instance = ServerEntryPoint.Instance;
+
+ if (instance == null)
+ {
+ throw new InvalidOperationException(unavilableMessage);
+ }
+
+ var pages = instance.PluginConfigurationPages;
+
+ if (pages == null)
+ {
+ throw new InvalidOperationException(unavilableMessage);
+ }
if (request.PageType.HasValue)
{
@@ -428,6 +441,7 @@ namespace MediaBrowser.WebDashboard.Api
"librarybrowser.js",
"aboutpage.js",
+ "allusersettings.js",
"alphapicker.js",
"addpluginpage.js",
"advancedconfigurationpage.js",
diff --git a/MediaBrowser.WebDashboard/ApiClient.js b/MediaBrowser.WebDashboard/ApiClient.js
index a90d2dcf8..4a0222139 100644
--- a/MediaBrowser.WebDashboard/ApiClient.js
+++ b/MediaBrowser.WebDashboard/ApiClient.js
@@ -1432,9 +1432,9 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout) {
/**
* Gets all users from the server
*/
- self.getUsers = function () {
+ self.getUsers = function (options) {
- var url = self.getUrl("users");
+ var url = self.getUrl("users", options || {});
return self.ajax({
type: "GET",
@@ -1872,6 +1872,32 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout) {
/**
* Authenticates a user
+ * @param {String} name
+ * @param {String} password
+ */
+ self.authenticateUserByName = function (name, password) {
+
+ if (!name) {
+ throw new Error("null name");
+ }
+
+ var url = self.getUrl("Users/" + name + "/authenticatebyname");
+
+ var postData = {
+ password: MediaBrowser.SHA1(password || "")
+ };
+
+ return self.ajax({
+ type: "POST",
+ url: url,
+ data: JSON.stringify(postData),
+ dataType: "json",
+ contentType: "application/json"
+ });
+ };
+
+ /**
+ * Authenticates a user
* @param {String} userId
* @param {String} password
*/
@@ -1891,6 +1917,7 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout) {
type: "POST",
url: url,
data: JSON.stringify(postData),
+ dataType: "json",
contentType: "application/json"
});
};
diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj
index 62dd59cc1..553c967f5 100644
--- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj
+++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj
@@ -81,6 +81,9 @@
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="ApiClient.js" />
+ <Content Include="dashboard-ui\allusersettings.html">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
<Content Include="dashboard-ui\boxsets.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -354,6 +357,9 @@
<Content Include="dashboard-ui\musicvideos.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
+ <Content Include="dashboard-ui\scripts\allusersettings.js">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
<Content Include="dashboard-ui\scripts\alphapicker.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
diff --git a/MediaBrowser.WebDashboard/packages.config b/MediaBrowser.WebDashboard/packages.config
index 439f8cb97..a8a737923 100644
--- a/MediaBrowser.WebDashboard/packages.config
+++ b/MediaBrowser.WebDashboard/packages.config
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
- <package id="MediaBrowser.ApiClient.Javascript" version="3.0.135" targetFramework="net45" />
+ <package id="MediaBrowser.ApiClient.Javascript" version="3.0.138" targetFramework="net45" />
<package id="ServiceStack.Common" version="3.9.54" targetFramework="net45" />
<package id="ServiceStack.Text" version="3.9.54" targetFramework="net45" />
</packages> \ No newline at end of file