mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-07-10 07:22:35 -07:00
parent
55f1309140
commit
c064bc6d44
13 changed files with 219 additions and 65 deletions
|
@ -76,6 +76,9 @@ namespace PlexRequests.Core.Migration.Migrations
|
|||
con.AlterTable("Users", "ADD", "Permissions", true, "INTEGER");
|
||||
con.AlterTable("Users", "ADD", "Features", true, "INTEGER");
|
||||
|
||||
con.AlterTable("PlexUsers", "ADD", "Permissions", true, "INTEGER");
|
||||
con.AlterTable("PlexUsers", "ADD", "Features", true, "INTEGER");
|
||||
|
||||
//https://image.tmdb.org/t/p/w150/https://image.tmdb.org/t/p/w150//aqhAqttDq7zgsTaBHtCD8wmTk6k.jpg
|
||||
|
||||
// UI = https://image.tmdb.org/t/p/w150/{{posterPath}}
|
||||
|
|
|
@ -50,5 +50,15 @@ namespace PlexRequests.Helpers.Permissions
|
|||
|
||||
[Display(Name = "Read Only User")]
|
||||
ReadOnlyUser = 32,
|
||||
|
||||
[Display(Name = "Auto Approve Movie Requests")]
|
||||
AutoApproveMovie = 64,
|
||||
|
||||
[Display(Name = "Auto Approve TV Show Requests")]
|
||||
AutoApproveTv = 128,
|
||||
|
||||
[Display(Name = "Auto Approve Album Requests")]
|
||||
AutoApproveAlbum = 256
|
||||
|
||||
}
|
||||
}
|
|
@ -471,10 +471,10 @@ namespace PlexRequests.Services.Jobs
|
|||
{
|
||||
var sections = PlexApi.GetLibrarySections(plexSettings.PlexAuthToken, plexSettings.FullUri);
|
||||
|
||||
List<PlexSearch> libs = new List<PlexSearch>();
|
||||
var libs = new List<PlexSearch>();
|
||||
if (sections != null)
|
||||
{
|
||||
foreach (var dir in sections.Directories)
|
||||
foreach (var dir in sections.Directories ?? new List<Directory>())
|
||||
{
|
||||
var lib = PlexApi.GetLibrary(plexSettings.PlexAuthToken, plexSettings.FullUri, dir.Key);
|
||||
if (lib != null)
|
||||
|
|
|
@ -24,11 +24,17 @@
|
|||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
|
||||
using Dapper.Contrib.Extensions;
|
||||
|
||||
namespace PlexRequests.Store.Models
|
||||
{
|
||||
[Table(nameof(PlexUsers))]
|
||||
public class PlexUsers : Entity
|
||||
{
|
||||
public int PlexUserId { get; set; }
|
||||
public string PlexUserId { get; set; }
|
||||
public string UserAlias { get; set; }
|
||||
public int Permissions { get; set; }
|
||||
public int Features { get; set; }
|
||||
}
|
||||
}
|
|
@ -114,8 +114,10 @@ CREATE UNIQUE INDEX IF NOT EXISTS RequestLimit_Id ON RequestLimit (Id);
|
|||
CREATE TABLE IF NOT EXISTS PlexUsers
|
||||
(
|
||||
Id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
PlexUserId INTEGER NOT NULL,
|
||||
UserAlias varchar(100) NOT NULL
|
||||
PlexUserId varchar(100) NOT NULL,
|
||||
UserAlias varchar(100) NOT NULL,
|
||||
Permissions INTEGER,
|
||||
Features INTEGER
|
||||
);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS PlexUsers_Id ON PlexUsers (Id);
|
||||
|
||||
|
|
|
@ -120,29 +120,30 @@
|
|||
|
||||
$scope.updateUser = function () {
|
||||
var u = $scope.selectedUser;
|
||||
userManagementService.updateUser(u.id, u.permissions, u.alias, u.emailAddress)
|
||||
.then(function (data) {
|
||||
if (data) {
|
||||
$scope.selectedUser = data;
|
||||
userManagementService.updateUser(u.id, u.permissions, u.features, u.alias, u.emailAddress)
|
||||
.then(function success(data) {
|
||||
if (data.data) {
|
||||
$scope.selectedUser = data.data;
|
||||
|
||||
if (open) {
|
||||
open = false;
|
||||
$("#wrapper").toggleClass("toggled");
|
||||
}
|
||||
closeSidebar();
|
||||
return successCallback("Updated User", "success");
|
||||
}
|
||||
}, function errorCallback(response) {
|
||||
successCallback(response, "danger");
|
||||
});
|
||||
}
|
||||
|
||||
$scope.deleteUser = function () {
|
||||
var u = $scope.selectedUser;
|
||||
var result = userManagementService.deleteUser(u.id);
|
||||
|
||||
result.success(function (data) {
|
||||
if (data.result) {
|
||||
userManagementService.deleteUser(u.id)
|
||||
.then(function sucess(data) {
|
||||
if (data.data.result) {
|
||||
removeUser(u.id, true);
|
||||
closeSidebar();
|
||||
return successCallback("Deleted User", "success");
|
||||
}
|
||||
}, function errorCallback(response) {
|
||||
successCallback(response, "danger");
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -170,6 +171,13 @@
|
|||
$scope.selectedUser = null;
|
||||
}
|
||||
}
|
||||
|
||||
function closeSidebar() {
|
||||
if (open) {
|
||||
open = false;
|
||||
$("#wrapper").toggleClass("toggled");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function successCallback(message, type) {
|
||||
|
|
|
@ -28,11 +28,11 @@
|
|||
return $http.get('/usermanagement/permissions');
|
||||
}
|
||||
|
||||
var updateUser = function (id, permissions, alias, email) {
|
||||
var updateUser = function (id, permissions, features, alias, email) {
|
||||
return $http({
|
||||
url: '/usermanagement/updateUser',
|
||||
method: "POST",
|
||||
data: { id: id, permissions: permissions, alias: alias, emailAddress: email }
|
||||
data: { id: id, permissions: permissions, features: features, alias: alias, emailAddress: email },
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -75,6 +75,8 @@ namespace PlexRequests.UI.Models
|
|||
public string Id { get; set; }
|
||||
[JsonProperty("permissions")]
|
||||
public List<CheckBox> Permissions { get; set; }
|
||||
[JsonProperty("features")]
|
||||
public List<CheckBox> Features { get; set; }
|
||||
public string Alias { get; set; }
|
||||
public string EmailAddress { get; set; }
|
||||
}
|
||||
|
|
|
@ -6,15 +6,16 @@ using System.Threading.Tasks;
|
|||
using Nancy;
|
||||
using Nancy.Extensions;
|
||||
using Nancy.Responses.Negotiation;
|
||||
using Nancy.Security;
|
||||
using Newtonsoft.Json;
|
||||
using PlexRequests.Api.Interfaces;
|
||||
using PlexRequests.Api.Models.Plex;
|
||||
using PlexRequests.Core;
|
||||
using PlexRequests.Core.Models;
|
||||
using PlexRequests.Core.SettingModels;
|
||||
using PlexRequests.Helpers;
|
||||
using PlexRequests.Helpers.Permissions;
|
||||
using PlexRequests.Store;
|
||||
using PlexRequests.Store.Models;
|
||||
using PlexRequests.Store.Repository;
|
||||
using PlexRequests.UI.Models;
|
||||
|
||||
|
@ -22,15 +23,16 @@ namespace PlexRequests.UI.Modules
|
|||
{
|
||||
public class UserManagementModule : BaseModule
|
||||
{
|
||||
public UserManagementModule(ISettingsService<PlexRequestSettings> pr, ICustomUserMapper m, IPlexApi plexApi, ISettingsService<PlexSettings> plex, IRepository<UserLogins> userLogins) : base("usermanagement", pr)
|
||||
public UserManagementModule(ISettingsService<PlexRequestSettings> pr, ICustomUserMapper m, IPlexApi plexApi, ISettingsService<PlexSettings> plex, IRepository<UserLogins> userLogins, IRepository<PlexUsers> plexRepo) : base("usermanagement", pr)
|
||||
{
|
||||
#if !DEBUG
|
||||
this.RequiresAnyClaim(UserClaims.Admin);
|
||||
Before += (ctx) => Security.AdminLoginRedirect(Permissions.Administrator, ctx);
|
||||
#endif
|
||||
UserMapper = m;
|
||||
PlexApi = plexApi;
|
||||
PlexSettings = plex;
|
||||
UserLoginsRepo = userLogins;
|
||||
PlexUsersRepository = plexRepo;
|
||||
|
||||
Get["/"] = x => Load();
|
||||
|
||||
|
@ -40,7 +42,7 @@ namespace PlexRequests.UI.Modules
|
|||
Get["/plex/{id}", true] = async (x, ct) => await PlexDetails(x.id);
|
||||
Get["/permissions"] = x => GetEnum<Permissions>();
|
||||
Get["/features"] = x => GetEnum<Features>();
|
||||
Post["/updateuser"] = x => UpdateUser();
|
||||
Post["/updateuser", true] = async (x, ct) => await UpdateUser();
|
||||
Post["/deleteuser"] = x => DeleteUser();
|
||||
}
|
||||
|
||||
|
@ -48,6 +50,7 @@ namespace PlexRequests.UI.Modules
|
|||
private IPlexApi PlexApi { get; }
|
||||
private ISettingsService<PlexSettings> PlexSettings { get; }
|
||||
private IRepository<UserLogins> UserLoginsRepo { get; }
|
||||
private IRepository<PlexUsers> PlexUsersRepository { get; }
|
||||
|
||||
private Negotiator Load()
|
||||
{
|
||||
|
@ -57,13 +60,14 @@ namespace PlexRequests.UI.Modules
|
|||
private async Task<Response> LoadUsers()
|
||||
{
|
||||
var localUsers = await UserMapper.GetUsersAsync();
|
||||
var plexDbUsers = await PlexUsersRepository.GetAllAsync();
|
||||
var model = new List<UserManagementUsersViewModel>();
|
||||
|
||||
var usersDb = UserLoginsRepo.GetAll().ToList();
|
||||
var userLogins = UserLoginsRepo.GetAll().ToList();
|
||||
|
||||
foreach (var user in localUsers)
|
||||
{
|
||||
var userDb = usersDb.FirstOrDefault(x => x.UserId == user.UserGuid);
|
||||
var userDb = userLogins.FirstOrDefault(x => x.UserId == user.UserGuid);
|
||||
model.Add(MapLocalUser(user, userDb?.LastLoggedIn ?? DateTime.MinValue));
|
||||
}
|
||||
|
||||
|
@ -75,20 +79,18 @@ namespace PlexRequests.UI.Modules
|
|||
|
||||
foreach (var u in plexUsers.User)
|
||||
{
|
||||
var userDb = usersDb.FirstOrDefault(x => x.UserId == u.Id);
|
||||
model.Add(new UserManagementUsersViewModel
|
||||
var dbUser = plexDbUsers.FirstOrDefault(x => x.PlexUserId == u.Id);
|
||||
var userDb = userLogins.FirstOrDefault(x => x.UserId == u.Id);
|
||||
// We don't have the user in the database yet
|
||||
if (dbUser == null)
|
||||
{
|
||||
Username = u.Username,
|
||||
Type = UserType.PlexUser,
|
||||
Id = u.Id,
|
||||
FeaturesFormattedString = "Requestor",
|
||||
EmailAddress = u.Email,
|
||||
PlexInfo = new UserManagementPlexInformation
|
||||
model.Add(MapPlexUser(u, null, userDb?.LastLoggedIn ?? DateTime.MinValue));
|
||||
}
|
||||
else
|
||||
{
|
||||
Thumb = u.Thumb
|
||||
},
|
||||
LastLoggedIn = userDb?.LastLoggedIn ?? DateTime.MinValue,
|
||||
});
|
||||
// The Plex User is in the database
|
||||
model.Add(MapPlexUser(u, dbUser, userDb?.LastLoggedIn ?? DateTime.MinValue));
|
||||
}
|
||||
}
|
||||
}
|
||||
return Response.AsJson(model);
|
||||
|
@ -128,7 +130,7 @@ namespace PlexRequests.UI.Modules
|
|||
permissionsVal += f;
|
||||
}
|
||||
|
||||
var user = UserMapper.CreateUser(model.Username, model.Password, featuresVal, permissionsVal, new UserProperties { EmailAddress = model.EmailAddress });
|
||||
var user = UserMapper.CreateUser(model.Username, model.Password, permissionsVal, featuresVal, new UserProperties { EmailAddress = model.EmailAddress });
|
||||
if (user.HasValue)
|
||||
{
|
||||
return Response.AsJson(MapLocalUser(UserMapper.GetUser(user.Value), DateTime.MinValue));
|
||||
|
@ -137,7 +139,7 @@ namespace PlexRequests.UI.Modules
|
|||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "Could not save user" });
|
||||
}
|
||||
|
||||
private Response UpdateUser()
|
||||
private async Task<Response> UpdateUser()
|
||||
{
|
||||
var body = Request.Body.AsString();
|
||||
if (string.IsNullOrEmpty(body))
|
||||
|
@ -156,24 +158,70 @@ namespace PlexRequests.UI.Modules
|
|||
});
|
||||
}
|
||||
|
||||
var val = model.Permissions.Where(c => c.Selected).Sum(c => c.Value);
|
||||
var permissionsValue = model.Permissions.Where(c => c.Selected).Sum(c => c.Value);
|
||||
var featuresValue = model.Features.Where(c => c.Selected).Sum(c => c.Value);
|
||||
|
||||
var userFound = UserMapper.GetUser(new Guid(model.Id));
|
||||
Guid outId;
|
||||
Guid.TryParse(model.Id, out outId);
|
||||
var localUser = UserMapper.GetUser(outId);
|
||||
|
||||
userFound.Permissions = val;
|
||||
// Update Local User
|
||||
if (localUser != null)
|
||||
{
|
||||
localUser.Permissions = permissionsValue;
|
||||
localUser.Features = featuresValue;
|
||||
|
||||
var currentProps = ByteConverterHelper.ReturnObject<UserProperties>(userFound.UserProperties);
|
||||
var currentProps = ByteConverterHelper.ReturnObject<UserProperties>(localUser.UserProperties);
|
||||
currentProps.UserAlias = model.Alias;
|
||||
currentProps.EmailAddress = model.EmailAddress;
|
||||
|
||||
userFound.UserProperties = ByteConverterHelper.ReturnBytes(currentProps);
|
||||
localUser.UserProperties = ByteConverterHelper.ReturnBytes(currentProps);
|
||||
|
||||
var user = UserMapper.EditUser(userFound);
|
||||
var user = UserMapper.EditUser(localUser);
|
||||
var dbUser = UserLoginsRepo.GetAll().FirstOrDefault(x => x.UserId == user.UserGuid);
|
||||
var retUser = MapLocalUser(user, dbUser?.LastLoggedIn ?? DateTime.MinValue);
|
||||
return Response.AsJson(retUser);
|
||||
}
|
||||
|
||||
var plexSettings = await PlexSettings.GetSettingsAsync();
|
||||
var plexDbUsers = await PlexUsersRepository.GetAllAsync();
|
||||
var plexUsers = PlexApi.GetUsers(plexSettings.PlexAuthToken);
|
||||
var plexDbUser = plexDbUsers.FirstOrDefault(x => x.PlexUserId == model.Id);
|
||||
var plexUser = plexUsers.User.FirstOrDefault(x => x.Id == model.Id);
|
||||
var userLogin = UserLoginsRepo.GetAll().FirstOrDefault(x => x.UserId == model.Id);
|
||||
if (plexDbUser != null && plexUser != null)
|
||||
{
|
||||
// We have a user in the DB for this Plex Account
|
||||
plexDbUser.Permissions = permissionsValue;
|
||||
plexDbUser.Features = featuresValue;
|
||||
|
||||
plexDbUser.UserAlias = model.Alias;
|
||||
|
||||
await PlexUsersRepository.UpdateAsync(plexDbUser);
|
||||
|
||||
var retUser = MapPlexUser(plexUser, plexDbUser, userLogin?.LastLoggedIn ?? DateTime.MinValue);
|
||||
return Response.AsJson(retUser);
|
||||
}
|
||||
|
||||
// We have a Plex Account but he's not in the DB
|
||||
if (plexUser != null)
|
||||
{
|
||||
var user = new PlexUsers
|
||||
{
|
||||
Permissions = permissionsValue,
|
||||
Features = featuresValue,
|
||||
UserAlias = model.Alias,
|
||||
PlexUserId = plexUser.Id
|
||||
};
|
||||
|
||||
await PlexUsersRepository.InsertAsync(user);
|
||||
|
||||
var retUser = MapPlexUser(plexUser, user, userLogin?.LastLoggedIn ?? DateTime.MinValue);
|
||||
return Response.AsJson(retUser);
|
||||
}
|
||||
return null; // We should never end up here.
|
||||
}
|
||||
|
||||
private Response DeleteUser()
|
||||
{
|
||||
var body = Request.Body.AsString();
|
||||
|
@ -297,6 +345,64 @@ namespace PlexRequests.UI.Modules
|
|||
|
||||
return m;
|
||||
}
|
||||
|
||||
private UserManagementUsersViewModel MapPlexUser(UserFriends plexInfo, PlexUsers dbUser, DateTime lastLoggedIn)
|
||||
{
|
||||
if (dbUser == null)
|
||||
{
|
||||
dbUser = new PlexUsers();
|
||||
}
|
||||
var features = (Features)dbUser?.Features;
|
||||
var permissions = (Permissions)dbUser?.Permissions;
|
||||
|
||||
var m = new UserManagementUsersViewModel
|
||||
{
|
||||
Id = plexInfo.Id,
|
||||
PermissionsFormattedString = permissions == 0 ? "None" : permissions.ToString(),
|
||||
FeaturesFormattedString = features.ToString(),
|
||||
Username = plexInfo.Username,
|
||||
Type = UserType.PlexUser,
|
||||
EmailAddress = plexInfo.Email,
|
||||
Alias = dbUser?.UserAlias ?? string.Empty,
|
||||
LastLoggedIn = lastLoggedIn,
|
||||
PlexInfo = new UserManagementPlexInformation
|
||||
{
|
||||
Thumb = plexInfo.Thumb
|
||||
},
|
||||
};
|
||||
|
||||
// Add permissions
|
||||
foreach (var p in Enum.GetValues(typeof(Permissions)))
|
||||
{
|
||||
var perm = (Permissions)p;
|
||||
var displayValue = EnumHelper<Permissions>.GetDisplayValue(perm);
|
||||
var pm = new CheckBox
|
||||
{
|
||||
Name = displayValue,
|
||||
Selected = permissions.HasFlag(perm),
|
||||
Value = (int)perm
|
||||
};
|
||||
|
||||
m.Permissions.Add(pm);
|
||||
}
|
||||
|
||||
// Add features
|
||||
foreach (var p in Enum.GetValues(typeof(Features)))
|
||||
{
|
||||
var perm = (Features)p;
|
||||
var displayValue = EnumHelper<Features>.GetDisplayValue(perm);
|
||||
var pm = new CheckBox
|
||||
{
|
||||
Name = displayValue,
|
||||
Selected = features.HasFlag(perm),
|
||||
Value = (int)perm
|
||||
};
|
||||
|
||||
m.Features.Add(pm);
|
||||
}
|
||||
|
||||
return m;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -470,4 +470,7 @@
|
|||
<data name="Layout_CacherRunning" xml:space="preserve">
|
||||
<value>A background process is currently running, so there might be some unexpected behavior. This shouldn't take too long.</value>
|
||||
</data>
|
||||
<data name="Layout_Usermanagement" xml:space="preserve">
|
||||
<value>User Management</value>
|
||||
</data>
|
||||
</root>
|
9
PlexRequests.UI/Resources/UI1.Designer.cs
generated
9
PlexRequests.UI/Resources/UI1.Designer.cs
generated
|
@ -402,6 +402,15 @@ namespace PlexRequests.UI.Resources {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to User Management.
|
||||
/// </summary>
|
||||
public static string Layout_Usermanagement {
|
||||
get {
|
||||
return ResourceManager.GetString("Layout_Usermanagement", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Welcome.
|
||||
/// </summary>
|
||||
|
|
|
@ -35,6 +35,10 @@
|
|||
@Html.GetNavbarUrl(Context, "/search", UI.Layout_Search, "search")
|
||||
@Html.GetNavbarUrl(Context, "/requests", UI.Layout_Requests, "plus-circle")
|
||||
@Html.GetNavbarUrl(Context, "/issues", UI.Layout_Issues, "exclamation", "<span id=\"issueCount\"></span>")
|
||||
@if (Html.IsAdmin())
|
||||
{
|
||||
@Html.GetNavbarUrl(Context, "/usermanagement", UI.Layout_Usermanagement, "users")
|
||||
}
|
||||
@*@if (Context.CurrentUser.IsAuthenticated()) // TODO replace with IsAdmin*@
|
||||
@if (Html.IsAdmin())
|
||||
{
|
||||
|
|
|
@ -15,30 +15,31 @@
|
|||
<div ng-show="selectedUser.emailAddress">
|
||||
<strong>Email Address: </strong><span ng-bind="selectedUser.emailAddress"></span>
|
||||
</div>
|
||||
<div>
|
||||
<strong>Permissions: </strong><span ng-bind="selectedUser.permissionsFormattedString"></span>
|
||||
</div>
|
||||
<div>
|
||||
<strong>Features: </strong><span ng-bind="selectedUser.featuresFormattedString"></span>
|
||||
</div>
|
||||
<div>
|
||||
<strong>User Type: </strong><span ng-bind="selectedUser.type === 1 ? 'Local User' : 'Plex User'"></span>
|
||||
</div>
|
||||
<br />
|
||||
<br />
|
||||
<div ng-show="selectedUser.type === 1">
|
||||
<div ng-show="selectedUser">
|
||||
<!--Edit-->
|
||||
|
||||
<strong>Modify Roles:</strong>
|
||||
<!--Load all claims-->
|
||||
<strong>Modify Permissions:</strong>
|
||||
<!--Load all permissions-->
|
||||
<div class="checkbox" ng-repeat="p in selectedUser.permissions">
|
||||
<input id="permissionsCheckbox_{{$id}}" class="checkbox-custom" name="permissions[]" ng-checked="p.selected" ng-model="p.selected" type="checkbox" value="{{p.value}}" />
|
||||
<label for="permissionsCheckbox_{{$id}}">{{p.name}}</label>
|
||||
</div>
|
||||
|
||||
<strong>Modify Features:</strong>
|
||||
<!--Load all features-->
|
||||
<div class="checkbox" ng-repeat="p in selectedUser.features">
|
||||
<input id="featuresCheckbox_{{$id}}" class="checkbox-custom" name="features[]" ng-checked="p.selected" ng-model="p.selected" type="checkbox" value="{{p.value}}" />
|
||||
<label for="featuresCheckbox_{{$id}}">{{p.name}}</label>
|
||||
</div>
|
||||
|
||||
<strong>Email Address</strong>
|
||||
<div class="form-group">
|
||||
<input id="emailAddress" type="email" ng-model="selectedUser.emailAddress" class="form-control form-control-custom" />
|
||||
<input id="emailAddress" type="email" ng-model="selectedUser.emailAddress" ng-disabled="selectedUser.type === 0" class="form-control form-control-custom" />
|
||||
</div>
|
||||
|
||||
<strong>Alias</strong>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue