diff --git a/PlexRequests.Core/Models/UserProperties.cs b/PlexRequests.Core/Models/UserProperties.cs index 8cd210d57..3e313bb83 100644 --- a/PlexRequests.Core/Models/UserProperties.cs +++ b/PlexRequests.Core/Models/UserProperties.cs @@ -1,35 +1,34 @@ -#region Copyright -// /************************************************************************ -// Copyright (c) 2016 Jamie Rees -// File: UserProperties.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 -namespace PlexRequests.Core.Models -{ - public class UserProperties - { - public string EmailAddress { get; set; } - public bool NotifyOnRelease { get; set; } - public bool NotifyOnApprove { get; set; } - } +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: UserProperties.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 +namespace PlexRequests.Core.Models +{ + public class UserProperties + { + public string EmailAddress { get; set; } + public string UserAlias { get; set; } + } } \ No newline at end of file diff --git a/PlexRequests.Core/UserMapper.cs b/PlexRequests.Core/UserMapper.cs index 909c3a34c..5be4a868a 100644 --- a/PlexRequests.Core/UserMapper.cs +++ b/PlexRequests.Core/UserMapper.cs @@ -1,183 +1,191 @@ -#region Copyright -// /************************************************************************ -// Copyright (c) 2016 Jamie Rees -// File: UserMapper.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; -using System.Security; - -using Nancy; -using Nancy.Authentication.Forms; -using Nancy.Security; - -using PlexRequests.Core.Models; -using PlexRequests.Helpers; -using PlexRequests.Store; -using PlexRequests.Store.Repository; - -namespace PlexRequests.Core -{ - public class UserMapper : IUserMapper, ICustomUserMapper - { - public UserMapper(IRepository repo) - { - Repo = repo; - } - private static IRepository Repo { get; set; } - public IUserIdentity GetUserFromIdentifier(Guid identifier, NancyContext context) - { - var user = Repo.Get(identifier.ToString()); - - if (user == null) - { - return null; - } - - return new UserIdentity - { - UserName = user.UserName, - Claims = ByteConverterHelper.ReturnObject(user.Claims) - }; - } - - public Guid? ValidateUser(string username, string password) - { - var users = Repo.GetAll(); - - foreach (var u in users) - { - if (username == u.UserName) - { - var passwordMatch = PasswordHasher.VerifyPassword(password, u.Salt, u.Hash); - if (passwordMatch) - { - return new Guid(u.UserGuid); - } - } - } - return null; - } - - public UsersModel EditUser(UsersModel user) - { - var existingUser = Repo.Get(user.UserGuid); - - user.Id = existingUser.Id; - user.UserGuid = existingUser.UserGuid; - Repo.Update(user); - return user; - } - - public bool DoUsersExist() - { - var users = Repo.GetAll(); - - return users.Any(); - } - - private Guid? CreateUser(string username, string password, string[] claims = default(string[])) - { - var salt = PasswordHasher.GenerateSalt(); - - var userModel = new UsersModel - { - UserName = username, - UserGuid = Guid.NewGuid().ToString(), - Salt = salt, - Hash = PasswordHasher.ComputeHash(password, salt), - Claims = ByteConverterHelper.ReturnBytes(claims), - UserProperties = ByteConverterHelper.ReturnBytes(new UserProperties()) - }; - Repo.Insert(userModel); - - var userRecord = Repo.Get(userModel.UserGuid); - - return new Guid(userRecord.UserGuid); - } - - public Guid? CreateAdmin(string username, string password) - { - return CreateUser(username, password, new[] { UserClaims.User, UserClaims.PowerUser, UserClaims.Admin }); - } - - public Guid? CreatePowerUser(string username, string password) - { - return CreateUser(username, password, new[] { UserClaims.User, UserClaims.PowerUser }); - } - - public Guid? CreateRegularUser(string username, string password) - { - return CreateUser(username, password, new[] { UserClaims.User }); - } - - public bool UpdatePassword(string username, string oldPassword, string newPassword) - { - var users = Repo.GetAll(); - var userToChange = users.FirstOrDefault(x => x.UserName == username); - if (userToChange == null) - return false; - - var passwordMatch = PasswordHasher.VerifyPassword(oldPassword, userToChange.Salt, userToChange.Hash); - if (!passwordMatch) - { - throw new SecurityException("Password does not match"); - } - - var newSalt = PasswordHasher.GenerateSalt(); - var newHash = PasswordHasher.ComputeHash(newPassword, newSalt); - - userToChange.Hash = newHash; - userToChange.Salt = newSalt; - - return Repo.Update(userToChange); - } - - public IEnumerable GetUsers() - { - return Repo.GetAll(); - } - - public UsersModel GetUser(Guid userId) - { - var user = Repo.Get(userId.ToString()); - return user; - } - } - - public interface ICustomUserMapper - { - IEnumerable GetUsers(); - UsersModel GetUser(Guid userId); - UsersModel EditUser(UsersModel user); - bool DoUsersExist(); - Guid? ValidateUser(string username, string password); - bool UpdatePassword(string username, string oldPassword, string newPassword); - Guid? CreateAdmin(string username, string password); - Guid? CreatePowerUser(string username, string password); - Guid? CreateRegularUser(string username, string password); - - } -} +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: UserMapper.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; +using System.Security; +using System.Threading.Tasks; + +using Nancy; +using Nancy.Authentication.Forms; +using Nancy.Security; + +using PlexRequests.Core.Models; +using PlexRequests.Helpers; +using PlexRequests.Store; +using PlexRequests.Store.Repository; + +namespace PlexRequests.Core +{ + public class UserMapper : IUserMapper, ICustomUserMapper + { + public UserMapper(IRepository repo) + { + Repo = repo; + } + private static IRepository Repo { get; set; } + public IUserIdentity GetUserFromIdentifier(Guid identifier, NancyContext context) + { + var user = Repo.Get(identifier.ToString()); + + if (user == null) + { + return null; + } + + return new UserIdentity + { + UserName = user.UserName, + Claims = ByteConverterHelper.ReturnObject(user.Claims) + }; + } + + public Guid? ValidateUser(string username, string password) + { + var users = Repo.GetAll(); + + foreach (var u in users) + { + if (username == u.UserName) + { + var passwordMatch = PasswordHasher.VerifyPassword(password, u.Salt, u.Hash); + if (passwordMatch) + { + return new Guid(u.UserGuid); + } + } + } + return null; + } + + public UsersModel EditUser(UsersModel user) + { + var existingUser = Repo.Get(user.UserGuid); + + user.Id = existingUser.Id; + user.UserGuid = existingUser.UserGuid; + Repo.Update(user); + return user; + } + + public bool DoUsersExist() + { + var users = Repo.GetAll(); + + return users.Any(); + } + + private Guid? CreateUser(string username, string password, string[] claims = default(string[]), UserProperties properties = null) + { + var salt = PasswordHasher.GenerateSalt(); + + var userModel = new UsersModel + { + UserName = username, + UserGuid = Guid.NewGuid().ToString(), + Salt = salt, + Hash = PasswordHasher.ComputeHash(password, salt), + Claims = ByteConverterHelper.ReturnBytes(claims), + UserProperties = ByteConverterHelper.ReturnBytes(properties ?? new UserProperties()) + }; + Repo.Insert(userModel); + + var userRecord = Repo.Get(userModel.UserGuid); + + return new Guid(userRecord.UserGuid); + } + + public Guid? CreateAdmin(string username, string password, UserProperties properties = null) + { + return CreateUser(username, password, new[] { UserClaims.User, UserClaims.PowerUser, UserClaims.Admin }, properties); + } + + public Guid? CreatePowerUser(string username, string password, UserProperties properties = null) + { + return CreateUser(username, password, new[] { UserClaims.User, UserClaims.PowerUser }, properties); + } + + public Guid? CreateRegularUser(string username, string password, UserProperties properties = null) + { + return CreateUser(username, password, new[] { UserClaims.User }, properties); + } + + public bool UpdatePassword(string username, string oldPassword, string newPassword) + { + var users = Repo.GetAll(); + var userToChange = users.FirstOrDefault(x => x.UserName == username); + if (userToChange == null) + return false; + + var passwordMatch = PasswordHasher.VerifyPassword(oldPassword, userToChange.Salt, userToChange.Hash); + if (!passwordMatch) + { + throw new SecurityException("Password does not match"); + } + + var newSalt = PasswordHasher.GenerateSalt(); + var newHash = PasswordHasher.ComputeHash(newPassword, newSalt); + + userToChange.Hash = newHash; + userToChange.Salt = newSalt; + + return Repo.Update(userToChange); + } + + public async Task> GetUsersAsync() + { + return await Repo.GetAllAsync(); + } + + public IEnumerable GetUsers() + { + return Repo.GetAll(); + } + + public UsersModel GetUser(Guid userId) + { + var user = Repo.Get(userId.ToString()); + return user; + } + } + + public interface ICustomUserMapper + { + IEnumerable GetUsers(); + Task> GetUsersAsync(); + UsersModel GetUser(Guid userId); + UsersModel EditUser(UsersModel user); + bool DoUsersExist(); + Guid? ValidateUser(string username, string password); + bool UpdatePassword(string username, string oldPassword, string newPassword); + Guid? CreateAdmin(string username, string password, UserProperties properties = null); + Guid? CreatePowerUser(string username, string password, UserProperties properties = null); + Guid? CreateRegularUser(string username, string password, UserProperties properties = null); + + + } +} diff --git a/PlexRequests.UI/Content/app/app.js b/PlexRequests.UI/Content/app/app.js new file mode 100644 index 000000000..4008b2d6c --- /dev/null +++ b/PlexRequests.UI/Content/app/app.js @@ -0,0 +1,3 @@ +(function() { + module = angular.module('PlexRequests', []); +}()); \ No newline at end of file diff --git a/PlexRequests.UI/Content/app/controllers/userManagement/userManagementController.js b/PlexRequests.UI/Content/app/controllers/userManagement/userManagementController.js new file mode 100644 index 000000000..85bf40d0f --- /dev/null +++ b/PlexRequests.UI/Content/app/controllers/userManagement/userManagementController.js @@ -0,0 +1,36 @@ +(function () { + + var controller = function ($scope, userManagementService) { + + $scope.user = {}; // The local user to create + $scope.users = []; // list of users + + $scope.error = false; + $scope.errorMessage = {}; + + $scope.getUsers = function () { + $scope.users = userManagementService.getUsers() + .then(function (data) { + $scope.users = data.data; + }); + }; + + $scope.addUser = function () { + if ($scope.users.length === 0) { + $scope.getUsers(); + } + userManagementService.addUser($scope.user).then(function (data) { + if (data.message) { + $scope.error = true; + $scope.errorMessage = data.message; + } else { + $scope.users.push(data); + $scope.user = {}; + } + }); + }; + } + + angular.module('PlexRequests').controller('userManagementController', ["$scope", "userManagementService", controller]); + +}()); \ No newline at end of file diff --git a/PlexRequests.UI/Content/app/services/userManagement/userManagementService.js b/PlexRequests.UI/Content/app/services/userManagement/userManagementService.js new file mode 100644 index 000000000..c9de3272f --- /dev/null +++ b/PlexRequests.UI/Content/app/services/userManagement/userManagementService.js @@ -0,0 +1,29 @@ +(function () { + + var userManagementService = function ($http) { + + var getUsers = function () { + return $http.get('/usermanagement/users'); + }; + + var addUser = function (user) { + + return $http({ + url: '/usermanagement/createuser', + method: "POST", + data: $.param(user), + headers: { + 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' + } + }); + } + + return { + getUsers: getUsers, + addUser: addUser + }; + } + + angular.module('PlexRequests').factory('userManagementService', ["$http", userManagementService]); + +}()); \ No newline at end of file diff --git a/PlexRequests.UI/Helpers/AngularViewBase.cs b/PlexRequests.UI/Helpers/AngularViewBase.cs new file mode 100644 index 000000000..787109040 --- /dev/null +++ b/PlexRequests.UI/Helpers/AngularViewBase.cs @@ -0,0 +1,44 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: EmptyViewBase.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 Nancy.ViewEngines.Razor; + +namespace PlexRequests.UI.Helpers +{ + public class AngularViewBase : NancyRazorViewBase + { + + public AngularViewBase() + { + Layout = "Shared/_AngularLayout.cshtml"; + } + + public override void Execute() + { + + } + } +} \ No newline at end of file diff --git a/PlexRequests.UI/Helpers/BaseUrlHelper.cs b/PlexRequests.UI/Helpers/BaseUrlHelper.cs index ae84fb180..12b137079 100644 --- a/PlexRequests.UI/Helpers/BaseUrlHelper.cs +++ b/PlexRequests.UI/Helpers/BaseUrlHelper.cs @@ -58,17 +58,18 @@ namespace PlexRequests.UI.Helpers settings.ThemeName = Themes.PlexTheme; } if (settings.ThemeName == "PlexBootstrap.css") settings.ThemeName = Themes.PlexTheme; - if (settings.ThemeName == "OriginalBootstrap.css") settings.ThemeName = Themes.OriginalTheme; - - sb.AppendLine($""); + if (settings.ThemeName == "OriginalBootstrap.css") settings.ThemeName = Themes.OriginalTheme; + + sb.AppendLine($""); sb.AppendLine($""); sb.AppendLine($""); sb.AppendLine($""); - sb.AppendLine($""); + sb.AppendLine($""); sb.AppendLine($""); sb.AppendLine($""); sb.AppendLine($""); + sb.AppendLine($""); sb.AppendLine($""); sb.AppendLine($""); sb.AppendLine($""); @@ -104,8 +105,8 @@ namespace PlexRequests.UI.Helpers sb.AppendLine($""); return helper.Raw(sb.ToString()); - } - + } + public static IHtmlString LoadIssueAssets(this HtmlHelpers helper) { var sb = new StringBuilder(); @@ -116,8 +117,8 @@ namespace PlexRequests.UI.Helpers sb.AppendLine($""); return helper.Raw(sb.ToString()); - } - + } + public static IHtmlString LoadIssueDetailsAssets(this HtmlHelpers helper) { var assetLocation = GetBaseUrl(); @@ -147,8 +148,8 @@ namespace PlexRequests.UI.Helpers if (!settings.CollectAnalyticData) { return helper.Raw(string.Empty); - } - + } + var assetLocation = GetBaseUrl(); var content = GetContentUrl(assetLocation); @@ -164,19 +165,19 @@ namespace PlexRequests.UI.Helpers if (!string.IsNullOrEmpty(content)) { url = $"/{content}{url}"; - } + } if (context.Request.Path == url) { returnString = $"{title}"; - } + } else { returnString = $"{title}"; } return helper.Raw(returnString); - } - + } + public static IHtmlString GetNavbarUrl(this HtmlHelpers helper, NancyContext context, string url, string title, string fontIcon) { var returnString = string.Empty; @@ -184,19 +185,19 @@ namespace PlexRequests.UI.Helpers if (!string.IsNullOrEmpty(content)) { url = $"/{content}{url}"; - } + } if (context.Request.Path == url) { returnString = $"
  • {title}
  • "; - } + } else { returnString = $"
  • {title}
  • "; } return helper.Raw(returnString); - } - + } + public static IHtmlString GetNavbarUrl(this HtmlHelpers helper, NancyContext context, string url, string title, string fontIcon, string extraHtml) { var returnString = string.Empty; @@ -204,12 +205,12 @@ namespace PlexRequests.UI.Helpers if (!string.IsNullOrEmpty(content)) { url = $"/{content}{url}"; - } - + } + if (context.Request.Path == url) { returnString = $"
  • {title} {extraHtml}
  • "; - } + } else { returnString = $"
  • {title} {extraHtml}
  • "; @@ -218,33 +219,33 @@ namespace PlexRequests.UI.Helpers return helper.Raw(returnString); } - public static IHtmlString GetBaseUrl(this HtmlHelpers helper) - { - return helper.Raw(GetBaseUrl()); - } - + public static IHtmlString GetBaseUrl(this HtmlHelpers helper) + { + return helper.Raw(GetBaseUrl()); + } + private static string GetBaseUrl() { return GetSettings().BaseUrl; } private static PlexRequestSettings GetSettings() - { + { var returnValue = Cache.GetOrSet(CacheKeys.GetPlexRequestSettings, () => - { + { var settings = Locator.Resolve>().GetSettings(); return settings; }); return returnValue; - } - - private static string GetLinkUrl(string assetLocation) - { - return string.IsNullOrEmpty(assetLocation) ? string.Empty : $"{assetLocation}"; - } - private static string GetContentUrl(string assetLocation) - { - return string.IsNullOrEmpty(assetLocation) ? string.Empty : $"/{assetLocation}"; + } + + private static string GetLinkUrl(string assetLocation) + { + return string.IsNullOrEmpty(assetLocation) ? string.Empty : $"{assetLocation}"; + } + private static string GetContentUrl(string assetLocation) + { + return string.IsNullOrEmpty(assetLocation) ? string.Empty : $"/{assetLocation}"; } } } \ No newline at end of file diff --git a/PlexRequests.UI/Models/UserManagementUsersViewModel.cs b/PlexRequests.UI/Models/UserManagementUsersViewModel.cs index 66bc9974e..5a885ef9d 100644 --- a/PlexRequests.UI/Models/UserManagementUsersViewModel.cs +++ b/PlexRequests.UI/Models/UserManagementUsersViewModel.cs @@ -1,20 +1,19 @@ -using System; - -namespace PlexRequests.UI +namespace PlexRequests.UI.Models { - public class UserManagementUsersViewModel - { - public string Username{get;set;} - public string Claims{get;set;} - public int Id {get;set;} - public string Alias {get;set;} - public UserType Type { get; set;} - } + public class UserManagementUsersViewModel + { + public string Username { get; set; } + public string Claims { get; set; } + public int Id { get; set; } + public string Alias { get; set; } + public UserType Type { get; set; } + public string EmailAddress { get; set; } + } - public enum UserType - { - PlexUser, - LocalUser - } + public enum UserType + { + PlexUser, + LocalUser + } } diff --git a/PlexRequests.UI/Modules/UserManagementModule.cs b/PlexRequests.UI/Modules/UserManagementModule.cs index 0d6dee073..83e4db8b3 100644 --- a/PlexRequests.UI/Modules/UserManagementModule.cs +++ b/PlexRequests.UI/Modules/UserManagementModule.cs @@ -1,10 +1,14 @@ using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; using Nancy; using Nancy.Responses.Negotiation; using Nancy.Security; +using PlexRequests.Api.Interfaces; using PlexRequests.Core; +using PlexRequests.Core.Models; using PlexRequests.Core.SettingModels; using PlexRequests.Helpers; using PlexRequests.UI.Models; @@ -13,57 +17,88 @@ namespace PlexRequests.UI.Modules { public class UserManagementModule : BaseModule { - public UserManagementModule(ISettingsService pr, ICustomUserMapper m) : base("usermanagement",pr) + public UserManagementModule(ISettingsService pr, ICustomUserMapper m, IPlexApi plexApi, ISettingsService auth) : base("usermanagement", pr) { - this.RequiresClaims(UserClaims.Admin); - Get["/"] = x => Load(); + //this.RequiresClaims(UserClaims.Admin); - Get["/users"] = x => LoadUsers(); UserMapper = m; + PlexApi = plexApi; + AuthSettings = auth; + + Get["/"] = x => Load(); + + Get["/users", true] = async (x, ct) => await LoadUsers(); + Post["/createuser"] = x => CreateUser(Request.Form["userName"].ToString(), Request.Form["password"].ToString()); } + private ICustomUserMapper UserMapper { get; } + private IPlexApi PlexApi { get; } + private ISettingsService AuthSettings { get; } private Negotiator Load() { return View["Index"]; } - private Response LoadUsers() + private async Task LoadUsers() { - var users = UserMapper.GetUsers(); + var localUsers = await UserMapper.GetUsersAsync(); var model = new List(); - foreach (var user in users) + foreach (var user in localUsers) { + var claims = ByteConverterHelper.ReturnObject(user.Claims); + var claimsString = string.Join(", ", claims); + + var userProps = ByteConverterHelper.ReturnObject(user.UserProperties); + model.Add(new UserManagementUsersViewModel { - //Claims = ByteConverterHelper.ReturnObject(user.Claims), - Claims = "test", - Id = user.Id, + Claims = claimsString, Username = user.UserName, - //Type = UserType.LocalUser + Type = UserType.LocalUser, + EmailAddress = userProps.EmailAddress }); } - return Response.AsJson(users); + + var authSettings = await AuthSettings.GetSettingsAsync(); + if (!string.IsNullOrEmpty(authSettings.PlexAuthToken)) + { + //Get Plex Users + var plexUsers = PlexApi.GetUsers(authSettings.PlexAuthToken); + + foreach (var u in plexUsers.User) + { + model.Add(new UserManagementUsersViewModel + { + Username = u.Username, + Type = UserType.PlexUser, + //Alias = + Claims = "Requestor", + EmailAddress = u.Email + }); + } + } + return Response.AsJson(model); } - //private Response CreateUser(string username, string password, string claims) - //{ - // if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(password)) - // { - // return Response.AsJson(new JsonResponseModel - // { - // Result = true, - // Message = "Please enter in a valid Username and Password" - // }); - // } - // var user = UserMapper.CreateUser(username, password, new string[] {claims}); - // if (user.HasValue) - // { - // return Response.AsJson(new JsonResponseModel {Result = true}); - // } - - // return Response.AsJson(new JsonResponseModel {Result = false, Message = "Could not save user"}); - //} + private Response CreateUser(string username, string password) + { + if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(password)) + { + return Response.AsJson(new JsonResponseModel + { + Result = true, + Message = "Please enter in a valid Username and Password" + }); + } + var user = UserMapper.CreateRegularUser(username, password); + if (user.HasValue) + { + return Response.AsJson(user); + } + + return Response.AsJson(new JsonResponseModel { Result = false, Message = "Could not save user" }); + } } } diff --git a/PlexRequests.UI/PlexRequests.UI.csproj b/PlexRequests.UI/PlexRequests.UI.csproj index 3b63cf372..4b750251d 100644 --- a/PlexRequests.UI/PlexRequests.UI.csproj +++ b/PlexRequests.UI/PlexRequests.UI.csproj @@ -180,8 +180,9 @@ - + + @@ -246,6 +247,9 @@ + + Always + PreserveNewest @@ -298,6 +302,9 @@ PreserveNewest + + Always + PreserveNewest @@ -494,6 +501,9 @@ Always + + Always + Designer @@ -596,6 +606,18 @@ Always + + Always + + + Always + + + Always + + + Always + web.config @@ -698,6 +720,8 @@ --> - + + + \ No newline at end of file diff --git a/PlexRequests.UI/Views/Shared/Blank.cshtml b/PlexRequests.UI/Views/Shared/Blank.cshtml index cafdf0d63..3c61858c0 100644 --- a/PlexRequests.UI/Views/Shared/Blank.cshtml +++ b/PlexRequests.UI/Views/Shared/Blank.cshtml @@ -2,7 +2,7 @@ @using Nancy.Session @using PlexRequests.UI.Helpers @using PlexRequests.UI.Models -@inherits Nancy.ViewEngines.Razor.NancyRazorViewBase +@inherits Nancy.ViewEngines.Razor.NancyRazorViewBase @{ var baseUrl = Html.GetBaseUrl(); var url = string.Empty; diff --git a/PlexRequests.UI/Views/Shared/Partial/_Head.cshtml b/PlexRequests.UI/Views/Shared/Partial/_Head.cshtml new file mode 100644 index 000000000..d791e61da --- /dev/null +++ b/PlexRequests.UI/Views/Shared/Partial/_Head.cshtml @@ -0,0 +1,27 @@ +@using Nancy.Security +@using Nancy.Session +@using PlexRequests.UI.Helpers +@using PlexRequests.UI.Models +@using PlexRequests.UI.Resources +@inherits Nancy.ViewEngines.Razor.NancyRazorViewBase +@{ + + var baseUrl = Html.GetBaseUrl(); + var url = string.Empty; + if (!string.IsNullOrEmpty(baseUrl.ToHtmlString())) + { + url = "/" + baseUrl.ToHtmlString(); + } +} + + + + + + @UI.Layout_Title + + + + @Html.LoadAnalytics() + @Html.LoadAssets() + \ No newline at end of file diff --git a/PlexRequests.UI/Views/Shared/Partial/_LayoutScripts.cshtml b/PlexRequests.UI/Views/Shared/Partial/_LayoutScripts.cshtml new file mode 100644 index 000000000..808fc7a91 --- /dev/null +++ b/PlexRequests.UI/Views/Shared/Partial/_LayoutScripts.cshtml @@ -0,0 +1,83 @@ +@using Nancy.Security +@using Nancy.Session +@using PlexRequests.UI.Helpers +@using PlexRequests.UI.Models +@using PlexRequests.UI.Resources +@inherits Nancy.ViewEngines.Razor.NancyRazorViewBase + \ No newline at end of file diff --git a/PlexRequests.UI/Views/Shared/Partial/_Navbar.cshtml b/PlexRequests.UI/Views/Shared/Partial/_Navbar.cshtml new file mode 100644 index 000000000..3338e4d72 --- /dev/null +++ b/PlexRequests.UI/Views/Shared/Partial/_Navbar.cshtml @@ -0,0 +1,94 @@ +@using Nancy.Security +@using Nancy.Session +@using Nancy; +@using PlexRequests.UI.Helpers +@using PlexRequests.UI.Models +@using PlexRequests.UI.Resources +@inherits Nancy.ViewEngines.Razor.NancyRazorViewBase +@{ + + var baseUrl = Html.GetBaseUrl(); + var url = string.Empty; + if (!string.IsNullOrEmpty(baseUrl.ToHtmlString())) + { + url = "/" + baseUrl.ToHtmlString(); + } +} + + + + \ No newline at end of file diff --git a/PlexRequests.UI/Views/Shared/_AngularLayout.cshtml b/PlexRequests.UI/Views/Shared/_AngularLayout.cshtml new file mode 100644 index 000000000..0c4b5cded --- /dev/null +++ b/PlexRequests.UI/Views/Shared/_AngularLayout.cshtml @@ -0,0 +1,23 @@ +@using PlexRequests.UI.Helpers +@inherits Nancy.ViewEngines.Razor.NancyRazorViewBase + +@Html.Partial("Shared/Partial/_Head") + + + + @Html.Partial("Shared/Partial/_Navbar") + +
    + @RenderBody() +
    +
    + + + +
    + + +@Html.GetInformationalVersion() + + +@Html.Partial("Shared/Partial/_LayoutScripts") diff --git a/PlexRequests.UI/Views/Shared/_Layout.cshtml b/PlexRequests.UI/Views/Shared/_Layout.cshtml index 17caf127f..91f39611b 100644 --- a/PlexRequests.UI/Views/Shared/_Layout.cshtml +++ b/PlexRequests.UI/Views/Shared/_Layout.cshtml @@ -1,191 +1,23 @@ -@using Nancy.Security -@using Nancy.Session -@using PlexRequests.UI.Helpers -@using PlexRequests.UI.Models -@using PlexRequests.UI.Resources +@using PlexRequests.UI.Helpers @inherits Nancy.ViewEngines.Razor.NancyRazorViewBase -@{ - var baseUrl = Html.GetBaseUrl(); - var url = string.Empty; - if (!string.IsNullOrEmpty(baseUrl.ToHtmlString())) - { - url = "/" + baseUrl.ToHtmlString(); - } -} - - - @UI.Layout_Title - - - @Html.LoadAnalytics() - @Html.LoadAssets() - - - - - - -
    - @RenderBody() -
    -
    - +
    + -
    +
    -@Html.GetInformationalVersion(); - - \ No newline at end of file + +@Html.Partial("Shared/Partial/_LayoutScripts") diff --git a/PlexRequests.UI/Views/UserManagement/Index.cshtml b/PlexRequests.UI/Views/UserManagement/Index.cshtml index 3df2a104d..784c7cee0 100644 --- a/PlexRequests.UI/Views/UserManagement/Index.cshtml +++ b/PlexRequests.UI/Views/UserManagement/Index.cshtml @@ -1,4 +1,7 @@ @using PlexRequests.UI.Helpers + +@inherits PlexRequests.UI.Helpers.AngularViewBase + @Html.LoadTableAssets() @{ var baseUrl = Html.GetBaseUrl().ToHtmlString(); @@ -8,41 +11,53 @@ url = "/" + baseUrl; } } - - -

    User Management

    - - -
    -
    + + +
    + +
    +
    - +
    - - - - - + + + + + + + + + + + + + +
    IdUsernamePermissions
    IdUsernameEmailUser T
    + {{u.username}} + + {{u.emailAddress}} + + {{u.claims}} + + {{u.type == 0 ? 'Local User' : 'Plex User'}} + {{u.type}} +
    -
    - \ No newline at end of file