diff --git a/PlexRequests.Core.Migration/MigrationRunner.cs b/PlexRequests.Core.Migration/MigrationRunner.cs index c8935031a..5a1a67f10 100644 --- a/PlexRequests.Core.Migration/MigrationRunner.cs +++ b/PlexRequests.Core.Migration/MigrationRunner.cs @@ -22,7 +22,7 @@ namespace PlexRequests.Core.Migration public void MigrateToLatest() { var con = Db.DbConnection(); - var versions = GetMigrations().OrderBy(x => x.Key); + var versions = GetMigrations(); var dbVersion = con.GetVersionInfo().OrderByDescending(x => x.Version).FirstOrDefault(); if (dbVersion == null) diff --git a/PlexRequests.Core.Migration/Migrations/Version1100.cs b/PlexRequests.Core.Migration/Migrations/Version1100.cs new file mode 100644 index 000000000..fe03daf88 --- /dev/null +++ b/PlexRequests.Core.Migration/Migrations/Version1100.cs @@ -0,0 +1,58 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: Version1100.cs +// Created By: Jamie Rees +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// ************************************************************************/ +#endregion + +using System.Data; +using PlexRequests.Store; + +namespace PlexRequests.Core.Migration.Migrations +{ + [Migration(11000, "v1.10.0.0")] + public class Version1100 : BaseMigration, IMigration + { + public Version1100() + { + + } + public int Version => 11000; + + + public void Start(IDbConnection con) + { + UpdateDb(con); + + UpdateSchema(con, Version); + } + + private void UpdateDb(IDbConnection con) + { + // Create the two new columns + con.AlterTable("Users", "ADD", "Permissions", true, "INTEGER"); + con.AlterTable("Users", "ADD", "Features", true, "INTEGER"); + + } + } +} \ No newline at end of file diff --git a/PlexRequests.Core.Migration/PlexRequests.Core.Migration.csproj b/PlexRequests.Core.Migration/PlexRequests.Core.Migration.csproj index a92ae74d5..2881f95ed 100644 --- a/PlexRequests.Core.Migration/PlexRequests.Core.Migration.csproj +++ b/PlexRequests.Core.Migration/PlexRequests.Core.Migration.csproj @@ -65,6 +65,7 @@ + diff --git a/PlexRequests.Core/UserMapper.cs b/PlexRequests.Core/UserMapper.cs index 24e44999f..8f705147e 100644 --- a/PlexRequests.Core/UserMapper.cs +++ b/PlexRequests.Core/UserMapper.cs @@ -36,6 +36,7 @@ using Nancy.Security; using PlexRequests.Core.Models; using PlexRequests.Helpers; +using PlexRequests.Helpers.Permissions; using PlexRequests.Store; using PlexRequests.Store.Repository; @@ -118,6 +119,27 @@ namespace PlexRequests.Core return new Guid(userRecord.UserGuid); } + public Guid? CreateUser(string username, string password, int permissions, int features, UserProperties properties = null) + { + var salt = PasswordHasher.GenerateSalt(); + + var userModel = new UsersModel + { + UserName = username, + UserGuid = Guid.NewGuid().ToString(), + Salt = salt, + Hash = PasswordHasher.ComputeHash(password, salt), + UserProperties = ByteConverterHelper.ReturnBytes(properties ?? new UserProperties()), + Permissions = permissions, + Features = features, + Claims = new byte[] {0} + }; + Repo.Insert(userModel); + var userRecord = Repo.Get(userModel.UserGuid); + + return new Guid(userRecord.UserGuid); + } + public void DeleteUser(string userId) { var user = Repo.Get(userId); @@ -187,6 +209,9 @@ namespace PlexRequests.Core public interface ICustomUserMapper { Guid? CreateUser(string username, string password, string[] claims, UserProperties props); + + Guid? CreateUser(string username, string password, int permissions, int features, + UserProperties properties = null); IEnumerable GetAllClaims(); IEnumerable GetUsers(); Task> GetUsersAsync(); diff --git a/PlexRequests.Helpers/EnumExtensions.cs b/PlexRequests.Helpers/EnumExtensions.cs new file mode 100644 index 000000000..72e1fcf68 --- /dev/null +++ b/PlexRequests.Helpers/EnumExtensions.cs @@ -0,0 +1,54 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: EnumExtensions.cs +// Created By: Jamie Rees +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// ************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace PlexRequests.Helpers +{ + public static class EnumExtensions + { + public static IEnumerable GetUniqueFlags(this Enum flags) + { + ulong flag = 1; + foreach (var value in Enum.GetValues(flags.GetType()).Cast()) + { + var bits = Convert.ToUInt64(value); + while (flag < bits) + { + flag <<= 1; + } + + if (flag == bits && flags.HasFlag(value)) + { + yield return value; + } + } + } + } +} \ No newline at end of file diff --git a/PlexRequests.Helpers/EnumHelper.cs b/PlexRequests.Helpers/EnumHelper.cs new file mode 100644 index 000000000..051c78508 --- /dev/null +++ b/PlexRequests.Helpers/EnumHelper.cs @@ -0,0 +1,112 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: EnumHelper.cs +// Created By: Jamie Rees +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// ************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Reflection; + +namespace PlexRequests.Helpers +{ + public static class EnumHelper + { + public static IList GetValues(Enum value) + { + return value.GetType().GetFields(BindingFlags.Static | BindingFlags.Public).Select(fi => (T) Enum.Parse(value.GetType(), fi.Name, false)).ToList(); + } + + public static T Parse(string value) + { + return (T)Enum.Parse(typeof(T), value, true); + } + + public static IList GetNames(Enum value) + { + return value.GetType().GetFields(BindingFlags.Static | BindingFlags.Public).Select(fi => fi.Name).ToList(); + } + + public static IList GetDisplayValues(Enum value) + { + return GetNames(value).Select(obj => GetDisplayValue(Parse(obj))).ToList(); + } + + private static string LookupResource(Type resourceManagerProvider, string resourceKey) + { + foreach (var staticProperty in resourceManagerProvider.GetProperties(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public)) + { + if (staticProperty.PropertyType == typeof(System.Resources.ResourceManager)) + { + System.Resources.ResourceManager resourceManager = (System.Resources.ResourceManager)staticProperty.GetValue(null, null); + return resourceManager.GetString(resourceKey); + } + } + + return resourceKey; // Fallback with the key name + } + + public static string GetDisplayValue(T value) + { + var fieldInfo = value.GetType().GetField(value.ToString()); + + var descriptionAttributes = fieldInfo.GetCustomAttributes( + typeof(DisplayAttribute), false) as DisplayAttribute[]; + + if (descriptionAttributes[0].ResourceType != null) + return LookupResource(descriptionAttributes[0].ResourceType, descriptionAttributes[0].Name); + + if (descriptionAttributes == null) return string.Empty; + return (descriptionAttributes.Length > 0) ? descriptionAttributes[0].Name : value.ToString(); + } + + public static T GetValueFromName(string name) + { + var type = typeof(T); + if (!type.IsEnum) throw new InvalidOperationException(); + + foreach (var field in type.GetFields()) + { + var attribute = Attribute.GetCustomAttribute(field, + typeof(DisplayAttribute)) as DisplayAttribute; + if (attribute != null) + { + if (attribute.Name == name) + { + return (T)field.GetValue(null); + } + } + else + { + if (field.Name == name) + return (T)field.GetValue(null); + } + } + + throw new ArgumentOutOfRangeException(nameof(name)); + } + } +} \ No newline at end of file diff --git a/PlexRequests.Helpers/Permissions/Features.cs b/PlexRequests.Helpers/Permissions/Features.cs new file mode 100644 index 000000000..a236a8d15 --- /dev/null +++ b/PlexRequests.Helpers/Permissions/Features.cs @@ -0,0 +1,42 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: Features.cs +// Created By: Jamie Rees +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// ************************************************************************/ +#endregion + +using System; +using System.ComponentModel.DataAnnotations; + +namespace PlexRequests.Helpers.Permissions +{ + [Flags] + public enum Features + { + [Display(Name = "Newsletter")] + Newsletter = 1, + [Display(Name = "Recently Added Notification")] + RecentlyAddedNotification = 2, + + } +} \ No newline at end of file diff --git a/PlexRequests.Helpers/Permissions/Permissions.cs b/PlexRequests.Helpers/Permissions/Permissions.cs new file mode 100644 index 000000000..ffc0f1cc1 --- /dev/null +++ b/PlexRequests.Helpers/Permissions/Permissions.cs @@ -0,0 +1,51 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: Permissions.cs +// Created By: Jamie Rees +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// ************************************************************************/ +#endregion + +using System; +using System.ComponentModel.DataAnnotations; + +namespace PlexRequests.Helpers.Permissions +{ + [Flags] + public enum Permissions + { + [Display(Name = "Access Administration Settings")] + AdminSettings = 1, + + [Display(Name = "Request Movie")] + RequestMovie = 2, + + [Display(Name = "Request TV Show")] + RequestTvShow = 4, + + [Display(Name = "Request Music")] + RequestMusic = 8, + + [Display(Name = "Report Issue")] + ReportIssue = 16 + } +} \ No newline at end of file diff --git a/PlexRequests.Helpers/PlexRequests.Helpers.csproj b/PlexRequests.Helpers/PlexRequests.Helpers.csproj index 0ffffb4c8..38c703c97 100644 --- a/PlexRequests.Helpers/PlexRequests.Helpers.csproj +++ b/PlexRequests.Helpers/PlexRequests.Helpers.csproj @@ -48,6 +48,7 @@ True + @@ -69,6 +70,7 @@ + @@ -78,6 +80,9 @@ + + + diff --git a/PlexRequests.Store/SqlTables.sql b/PlexRequests.Store/SqlTables.sql index c0bb6cb27..c0cf01231 100644 --- a/PlexRequests.Store/SqlTables.sql +++ b/PlexRequests.Store/SqlTables.sql @@ -7,8 +7,9 @@ CREATE TABLE IF NOT EXISTS Users UserName varchar(50) NOT NULL, Salt BLOB NOT NULL, Hash BLOB NOT NULL, - Claims BLOB NOT NULL, - UserProperties BLOB + UserProperties BLOB, + Permissions INTEGER, + Features INTEGER ); CREATE TABLE IF NOT EXISTS UserLogins diff --git a/PlexRequests.Store/TableCreation.cs b/PlexRequests.Store/TableCreation.cs index 295a3ca53..24ba97c94 100644 --- a/PlexRequests.Store/TableCreation.cs +++ b/PlexRequests.Store/TableCreation.cs @@ -57,12 +57,13 @@ namespace PlexRequests.Store } } - public static void AddColumn(this IDbConnection connection, string tableName, string alterType, string newColumn, bool allowNulls, string dataType) + public static void AlterTable(this IDbConnection connection, string tableName, string alterType, string newColumn, bool allowNulls, string dataType) { connection.Open(); var result = connection.Query($"PRAGMA table_info({tableName});"); if (result.Any(x => x.name == newColumn)) { + connection.Close(); return; } diff --git a/PlexRequests.Store/UsersModel.cs b/PlexRequests.Store/UsersModel.cs index 88e04b446..45da08fdc 100644 --- a/PlexRequests.Store/UsersModel.cs +++ b/PlexRequests.Store/UsersModel.cs @@ -35,7 +35,10 @@ namespace PlexRequests.Store { public byte[] Hash { get; set; } public byte[] Salt { get; set; } + [Obsolete] public byte[] Claims { get; set; } public byte[] UserProperties { get; set; } + public int Permissions { get; set; } + public int Features { get; set; } } } diff --git a/PlexRequests.UI/Content/app/userManagement/userManagementController.js b/PlexRequests.UI/Content/app/userManagement/userManagementController.js index 39d939b5d..3b67832aa 100644 --- a/PlexRequests.UI/Content/app/userManagement/userManagementController.js +++ b/PlexRequests.UI/Content/app/userManagement/userManagementController.js @@ -4,10 +4,14 @@ $scope.user = {}; // The local user $scope.users = []; // list of users - $scope.claims = []; // List of claims + + $scope.features = []; // List of features + $scope.permissions = []; // List of permissions $scope.selectedUser = {}; // User on the right side - $scope.selectedClaims = {}; + + $scope.selectedFeatures = {}; + $scope.selectedPermissions = {}; $scope.minDate = "0001-01-01T00:00:00.0000000+00:00"; @@ -44,11 +48,16 @@ }); }; - // Get the claims and populate the create dropdown - $scope.getClaims = function () { - userManagementService.getClaims() + // Get the permissions and features and populate the create dropdown + $scope.getFeaturesPermissions = function () { + userManagementService.getFeatures() + .then(function (data) { + $scope.features = data.data; + }); + + userManagementService.getPermissions() .then(function (data) { - $scope.claims = data.data; + $scope.permissions = data.data; }); } @@ -62,14 +71,14 @@ return; } - if (!$scope.selectedClaims) { + if ($scope.selectedPermissions.length === 0) { $scope.error.error = true; $scope.error.errorMessage = "Please select a permission"; generateNotify($scope.error.errorMessage, 'warning'); return; } - userManagementService.addUser($scope.user, $scope.selectedClaims) + userManagementService.addUser($scope.user, $scope.selectedPermissions, $scope.selectedFeatures) .then(function (data) { if (data.message) { $scope.error.error = true; @@ -77,27 +86,33 @@ } else { $scope.users.push(data.data); // Push the new user into the array to update the DOM $scope.user = {}; - $scope.selectedClaims = {}; - $scope.claims.forEach(function (entry) { + $scope.selectedPermissions = {}; // Clear the checkboxes + $scope.selectedFeatures = {}; + $scope.features.forEach(function (entry) { entry.selected = false; }); + $scope.permissions.forEach(function (entry) { + entry.selected = false; + }); + + } }); }; - $scope.hasClaim = function (claim) { - var claims = $scope.selectedUser.claimsArray; - - var result = claims.some(function (item) { - return item === claim.name; - }); - return result; - }; + // Watch the checkboxes for updates (Creating a user) + $scope.$watch('features|filter:{selected:true}', + function (nv) { + $scope.selectedFeatures = nv.map(function (f) { + return f.name; + }); + }, + true); - $scope.$watch('claims|filter:{selected:true}', + $scope.$watch('permissions|filter:{selected:true}', function (nv) { - $scope.selectedClaims = nv.map(function (claim) { - return claim.name; + $scope.selectedPermissions = nv.map(function (f) { + return f.name; }); }, true); @@ -105,10 +120,15 @@ $scope.updateUser = function () { var u = $scope.selectedUser; - userManagementService.updateUser(u.id, u.claimsItem, u.alias, u.emailAddress) + userManagementService.updateUser(u.id, u.permissions, u.alias, u.emailAddress) .then(function (data) { if (data) { $scope.selectedUser = data; + + if (open) { + open = false; + $("#wrapper").toggleClass("toggled"); + } return successCallback("Updated User", "success"); } }); @@ -118,7 +138,7 @@ var u = $scope.selectedUser; var result = userManagementService.deleteUser(u.id); - result.success(function(data) { + result.success(function (data) { if (data.result) { removeUser(u.id, true); return successCallback("Deleted User", "success"); @@ -138,7 +158,7 @@ // On page load $scope.init = function () { $scope.getUsers(); - $scope.getClaims(); + $scope.getFeaturesPermissions(); return; } @@ -157,5 +177,5 @@ }; - angular.module('PlexRequests').controller('userManagementController', ["$scope", "userManagementService","moment", controller]); + angular.module('PlexRequests').controller('userManagementController', ["$scope", "userManagementService", "moment", controller]); }()); \ No newline at end of file diff --git a/PlexRequests.UI/Content/app/userManagement/userManagementService.js b/PlexRequests.UI/Content/app/userManagement/userManagementService.js index 209ca20b9..72cd7f375 100644 --- a/PlexRequests.UI/Content/app/userManagement/userManagementService.js +++ b/PlexRequests.UI/Content/app/userManagement/userManagementService.js @@ -8,27 +8,31 @@ return $http.get('/usermanagement/users'); }; - var addUser = function (user, claims) { - if (!user || claims.length === 0) { + var addUser = function (user, permissions, features) { + if (!user || permissions.length === 0) { return null; } return $http({ url: '/usermanagement/createuser', method: "POST", - data: { username: user.username, password: user.password, claims: claims, email: user.email } + data: { username: user.username, password: user.password, permissions: permissions, features : features, email: user.email } }); } - var getClaims = function () { - return $http.get('/usermanagement/claims'); + var getFeatures = function () { + return $http.get('/usermanagement/features'); } - var updateUser = function (id, claims, alias, email) { + var getPermissions = function () { + return $http.get('/usermanagement/permissions'); + } + + var updateUser = function (id, permissions, alias, email) { return $http({ url: '/usermanagement/updateUser', method: "POST", - data: { id: id, claims: claims, alias: alias, emailAddress: email } + data: { id: id, permissions: permissions, alias: alias, emailAddress: email } }); } @@ -43,7 +47,8 @@ return { getUsers: getUsers, addUser: addUser, - getClaims: getClaims, + getFeatures: getFeatures, + getPermissions: getPermissions, updateUser: updateUser, deleteUser: deleteUser }; diff --git a/PlexRequests.UI/Models/UserManagement/UserManagementUsersViewModel.cs b/PlexRequests.UI/Models/UserManagement/UserManagementUsersViewModel.cs index 16abe0615..aad003c05 100644 --- a/PlexRequests.UI/Models/UserManagement/UserManagementUsersViewModel.cs +++ b/PlexRequests.UI/Models/UserManagement/UserManagementUsersViewModel.cs @@ -10,17 +10,20 @@ namespace PlexRequests.UI.Models public UserManagementUsersViewModel() { PlexInfo = new UserManagementPlexInformation(); + Permissions = new List(); + Features = new List(); } public string Username { get; set; } - public string Claims { get; set; } + public string FeaturesFormattedString { get; set; } + public string PermissionsFormattedString { get; set; } public string Id { get; set; } public string Alias { get; set; } public UserType Type { get; set; } public string EmailAddress { get; set; } public UserManagementPlexInformation PlexInfo { get; set; } - public string[] ClaimsArray { get; set; } - public List ClaimsItem { get; set; } public DateTime LastLoggedIn { get; set; } + public List Permissions { get; set; } + public List Features { get; set; } } public class UserManagementPlexInformation @@ -33,6 +36,13 @@ namespace PlexRequests.UI.Models public List Servers { get; set; } } + public class CheckBox + { + public string Name { get; set; } + public int Value { get; set; } + public bool Selected { get; set; } + } + public class UserManagementPlexServers { public int Id { get; set; } @@ -50,8 +60,10 @@ namespace PlexRequests.UI.Models public string Username { get; set; } [JsonProperty("password")] public string Password { get; set; } - [JsonProperty("claims")] - public string[] Claims { get; set; } + [JsonProperty("permissions")] + public List Permissions { get; set; } + [JsonProperty("features")] + public List Features { get; set; } [JsonProperty("email")] public string EmailAddress { get; set; } @@ -61,20 +73,10 @@ namespace PlexRequests.UI.Models { [JsonProperty("id")] public string Id { get; set; } - [JsonProperty("claims")] - public List Claims { get; set; } - + [JsonProperty("permissions")] + public List Permissions { get; set; } public string Alias { get; set; } - public string EmailAddress { get; set; } - - public class ClaimsModel - { - [JsonProperty("name")] - public string Name { get; set; } - [JsonProperty("selected")] - public bool Selected { get; set; } - } - + public string EmailAddress { get; set; } } } diff --git a/PlexRequests.UI/Modules/UserManagementModule.cs b/PlexRequests.UI/Modules/UserManagementModule.cs index 14f99254c..d5879dd72 100644 --- a/PlexRequests.UI/Modules/UserManagementModule.cs +++ b/PlexRequests.UI/Modules/UserManagementModule.cs @@ -13,6 +13,7 @@ using PlexRequests.Core; using PlexRequests.Core.Models; using PlexRequests.Core.SettingModels; using PlexRequests.Helpers; +using PlexRequests.Helpers.Permissions; using PlexRequests.Store; using PlexRequests.Store.Repository; using PlexRequests.UI.Models; @@ -24,7 +25,7 @@ namespace PlexRequests.UI.Modules public UserManagementModule(ISettingsService pr, ICustomUserMapper m, IPlexApi plexApi, ISettingsService plex, IRepository userLogins) : base("usermanagement", pr) { #if !DEBUG - this.RequiresClaims(UserClaims.Admin); + this.RequiresAnyClaims(UserClaims.Admin); #endif UserMapper = m; PlexApi = plexApi; @@ -37,7 +38,8 @@ namespace PlexRequests.UI.Modules Post["/createuser"] = x => CreateUser(); Get["/local/{id}"] = x => LocalDetails((Guid)x.id); Get["/plex/{id}", true] = async (x, ct) => await PlexDetails(x.id); - Get["/claims"] = x => GetClaims(); + Get["/permissions"] = x => GetEnum(); + Get["/features"] = x => GetEnum(); Post["/updateuser"] = x => UpdateUser(); Post["/deleteuser"] = x => DeleteUser(); } @@ -79,7 +81,7 @@ namespace PlexRequests.UI.Modules Username = u.Username, Type = UserType.PlexUser, Id = u.Id, - Claims = "Requestor", + FeaturesFormattedString = "Requestor", EmailAddress = u.Email, PlexInfo = new UserManagementPlexInformation { @@ -110,7 +112,23 @@ namespace PlexRequests.UI.Modules Message = "Please enter in a valid Username and Password" }); } - var user = UserMapper.CreateUser(model.Username, model.Password, model.Claims, new UserProperties { EmailAddress = model.EmailAddress }); + + var featuresVal = 0; + var permissionsVal = 0; + + foreach (var feature in model.Features) + { + var f = (int)EnumHelper.GetValueFromName(feature); + featuresVal += f; + } + + foreach (var permission in model.Permissions) + { + var f = (int)EnumHelper.GetValueFromName(permission); + permissionsVal += f; + } + + var user = UserMapper.CreateUser(model.Username, model.Password, featuresVal, permissionsVal, new UserProperties { EmailAddress = model.EmailAddress }); if (user.HasValue) { return Response.AsJson(MapLocalUser(UserMapper.GetUser(user.Value), DateTime.MinValue)); @@ -137,20 +155,13 @@ namespace PlexRequests.UI.Modules Message = "Couldn't find the user" }); } - - var claims = new List(); - - foreach (var c in model.Claims) - { - if (c.Selected) - { - claims.Add(c.Name); - } - } + + var val = model.Permissions.Where(c => c.Selected).Sum(c => c.Value); var userFound = UserMapper.GetUser(new Guid(model.Id)); - userFound.Claims = ByteConverterHelper.ReturnBytes(claims.ToArray()); + userFound.Permissions = val; + var currentProps = ByteConverterHelper.ReturnObject(userFound.UserProperties); currentProps.UserAlias = model.Alias; currentProps.EmailAddress = model.EmailAddress; @@ -221,53 +232,69 @@ namespace PlexRequests.UI.Modules /// Returns all claims for the users. /// /// - private Response GetClaims() + private Response GetEnum() { - var retVal = new List(); - var claims = UserMapper.GetAllClaims(); - foreach (var c in claims) + var retVal = new List(); + foreach (var p in Enum.GetValues(typeof(T))) { - retVal.Add(new { Name = c, Selected = false }); + var perm = (T)p; + var displayValue = EnumHelper.GetDisplayValue(perm); + + retVal.Add(new CheckBox{ Name = displayValue, Selected = false, Value = (int)p }); } + return Response.AsJson(retVal); } private UserManagementUsersViewModel MapLocalUser(UsersModel user, DateTime lastLoggedIn) { - var claims = ByteConverterHelper.ReturnObject(user.Claims); - var claimsString = string.Join(", ", claims); + var features = (Features) user.Features; + var permissions = (Permissions) user.Permissions; var userProps = ByteConverterHelper.ReturnObject(user.UserProperties); var m = new UserManagementUsersViewModel { Id = user.UserGuid, - Claims = claimsString, + PermissionsFormattedString = permissions == 0 ? "None" : permissions.ToString(), + FeaturesFormattedString = features.ToString(), Username = user.UserName, Type = UserType.LocalUser, EmailAddress = userProps.EmailAddress, Alias = userProps.UserAlias, - ClaimsArray = claims, - ClaimsItem = new List(), - LastLoggedIn = lastLoggedIn + LastLoggedIn = lastLoggedIn, }; - // Add all of the current claims - foreach (var c in claims) + // Add permissions + foreach (var p in Enum.GetValues(typeof(Permissions))) { - m.ClaimsItem.Add(new UserManagementUpdateModel.ClaimsModel { Name = c, Selected = true }); - } - - var allClaims = UserMapper.GetAllClaims(); + var perm = (Permissions)p; + var displayValue = EnumHelper.GetDisplayValue(perm); + var pm = new CheckBox + { + Name = displayValue, + Selected = permissions.HasFlag(perm), + Value = (int)perm + }; - // Get me the current claims that the user does not have - var missingClaims = allClaims.Except(claims); + m.Permissions.Add(pm); + } - // Add them into the view - foreach (var missingClaim in missingClaims) + // Add features + foreach (var p in Enum.GetValues(typeof(Features))) { - m.ClaimsItem.Add(new UserManagementUpdateModel.ClaimsModel { Name = missingClaim, Selected = false }); + var perm = (Features)p; + var displayValue = EnumHelper.GetDisplayValue(perm); + var pm = new CheckBox + { + Name = displayValue, + Selected = features.HasFlag(perm), + Value = (int)perm + }; + + m.Features.Add(pm); } + return m; } } diff --git a/PlexRequests.UI/Views/UserManagement/Index.cshtml b/PlexRequests.UI/Views/UserManagement/Index.cshtml index 495dc29d5..7002cb143 100644 --- a/PlexRequests.UI/Views/UserManagement/Index.cshtml +++ b/PlexRequests.UI/Views/UserManagement/Index.cshtml @@ -16,7 +16,10 @@ Email Address:
- Permissions: + Permissions: +
+
+ Features:
User Type: @@ -28,9 +31,9 @@ Modify Roles: -
- - +
+ +
Email Address @@ -71,13 +74,23 @@
+

Permissions:

+
+ + +
-
- - + +

Features:

+
+ +
+ + @@ -119,7 +132,7 @@ - Roles + Permissions @@ -150,7 +163,7 @@ {{u.emailAddress}} - {{u.claims}} + {{u.permissionsFormattedString}} {{u.type === 1 ? 'Local User' : 'Plex User'}}