From cbfe88cd6ddb970978cf7a99ba9fce0c9e3c80eb Mon Sep 17 00:00:00 2001 From: tidusjar Date: Fri, 20 May 2016 10:44:02 +0100 Subject: [PATCH] Added the ability to get the apikey from the api if you provide a correct username and password. Added more unit tests Added the ability to change a users password using the api refactored the Usermapper and made it unit testsable. Also api documentation for the new endpoints too. #222 #205 --- PlexRequests.Core/UserMapper.cs | 52 +++--- PlexRequests.UI.Tests/ApiModuleTests.cs | 170 +++++++++++++++++- PlexRequests.UI.Tests/UserLoginModuleTests.cs | 2 - PlexRequests.UI/Bootstrapper.cs | 2 + .../ModelDataProviders/UserUpdateViewModel.cs | 55 ++++++ PlexRequests.UI/Models/UserUpdateViewModel.cs | 34 ++++ PlexRequests.UI/Modules/AdminModule.cs | 13 +- PlexRequests.UI/Modules/ApiMetadataModule.cs | 24 ++- PlexRequests.UI/Modules/ApiModule.cs | 60 ++++++- PlexRequests.UI/Modules/BaseApiModule.cs | 4 + PlexRequests.UI/Modules/LoginModule.cs | 4 +- .../Modules/UserManagementModule.cs | 4 +- PlexRequests.UI/PlexRequests.UI.csproj | 3 + .../Validators/UserViewModelValidator.cs | 41 +++++ 14 files changed, 424 insertions(+), 44 deletions(-) create mode 100644 PlexRequests.UI/ModelDataProviders/UserUpdateViewModel.cs create mode 100644 PlexRequests.UI/Models/UserUpdateViewModel.cs create mode 100644 PlexRequests.UI/Validators/UserViewModelValidator.cs diff --git a/PlexRequests.Core/UserMapper.cs b/PlexRequests.Core/UserMapper.cs index a846652c5..eb66cbea3 100644 --- a/PlexRequests.Core/UserMapper.cs +++ b/PlexRequests.Core/UserMapper.cs @@ -36,21 +36,20 @@ using Nancy.Security; using PlexRequests.Core.Models; using PlexRequests.Helpers; using PlexRequests.Store; +using PlexRequests.Store.Repository; namespace PlexRequests.Core { - public class UserMapper : IUserMapper + public class UserMapper : IUserMapper, ICustomUserMapper { - public UserMapper(ISqliteConfiguration db) + public UserMapper(IRepository repo) { - Db = db; + Repo = repo; } - private static ISqliteConfiguration Db { get; set; } + private static IRepository Repo { get; set; } public IUserIdentity GetUserFromIdentifier(Guid identifier, NancyContext context) { - var repo = new UserRepository(Db); - - var user = repo.Get(identifier.ToString()); + var user = Repo.Get(identifier.ToString()); if (user == null) { @@ -64,10 +63,9 @@ namespace PlexRequests.Core }; } - public static Guid? ValidateUser(string username, string password) + public Guid? ValidateUser(string username, string password) { - var repo = new UserRepository(Db); - var users = repo.GetAll(); + var users = Repo.GetAll(); foreach (var u in users) { @@ -83,17 +81,15 @@ namespace PlexRequests.Core return null; } - public static bool DoUsersExist() + public bool DoUsersExist() { - var repo = new UserRepository(Db); - var users = repo.GetAll(); + var users = Repo.GetAll(); return users.Any(); } - public static Guid? CreateUser(string username, string password, string[] claims = default(string[])) + public Guid? CreateUser(string username, string password, string[] claims = default(string[])) { - var repo = new UserRepository(Db); var salt = PasswordHasher.GenerateSalt(); var userModel = new UsersModel @@ -105,17 +101,16 @@ namespace PlexRequests.Core Claims = ByteConverterHelper.ReturnBytes(claims), UserProperties = ByteConverterHelper.ReturnBytes(new UserProperties()) }; - repo.Insert(userModel); + Repo.Insert(userModel); - var userRecord = repo.Get(userModel.UserGuid); + var userRecord = Repo.Get(userModel.UserGuid); return new Guid(userRecord.UserGuid); } - public static bool UpdatePassword(string username, string oldPassword, string newPassword) + public bool UpdatePassword(string username, string oldPassword, string newPassword) { - var repo = new UserRepository(Db); - var users = repo.GetAll(); + var users = Repo.GetAll(); var userToChange = users.FirstOrDefault(x => x.UserName == username); if (userToChange == null) return false; @@ -132,13 +127,22 @@ namespace PlexRequests.Core userToChange.Hash = newHash; userToChange.Salt = newSalt; - return repo.Update(userToChange); + return Repo.Update(userToChange); } - public static IEnumerable GetUsers() + public IEnumerable GetUsers() { - var repo = new UserRepository(Db); - return repo.GetAll(); + return Repo.GetAll(); } } + + public interface ICustomUserMapper + { + IEnumerable GetUsers(); + Guid? CreateUser(string username, string password, string[] claims = default(string[])); + bool DoUsersExist(); + Guid? ValidateUser(string username, string password); + bool UpdatePassword(string username, string oldPassword, string newPassword); + + } } diff --git a/PlexRequests.UI.Tests/ApiModuleTests.cs b/PlexRequests.UI.Tests/ApiModuleTests.cs index 28ed4b52a..26d7dca52 100644 --- a/PlexRequests.UI.Tests/ApiModuleTests.cs +++ b/PlexRequests.UI.Tests/ApiModuleTests.cs @@ -34,7 +34,6 @@ using FluentValidation; using Moq; using Nancy; -using Nancy.Bootstrapper; using Nancy.Testing; using Nancy.Validation; using Nancy.Validation.FluentValidation; @@ -46,6 +45,7 @@ using NUnit.Framework; using PlexRequests.Core; using PlexRequests.Core.SettingModels; using PlexRequests.Store; +using PlexRequests.Store.Repository; using PlexRequests.UI.Models; using PlexRequests.UI.Modules; using PlexRequests.UI.Validators; @@ -62,31 +62,48 @@ namespace PlexRequests.UI.Tests [SetUp] public void Setup() { - var requests = new Fixture().CreateMany(); + var fixture = new Fixture(); + var requests = fixture.CreateMany(); var requestMock = new Mock(); var settingsMock = new Mock>(); + var userRepoMock = new Mock>(); + var mapperMock = new Mock(); + + var userModels = fixture.CreateMany().ToList(); + userModels.Add(new UsersModel + { + UserName = "user1" + }); + settingsMock.Setup(x => x.GetSettings()).Returns(new PlexRequestSettings {ApiKey = "api"}); requestMock.Setup(x => x.GetAll()).Returns(requests); requestMock.Setup(x => x.Get(1)).Returns(requests.FirstOrDefault()); requestMock.Setup(x => x.Get(99)).Returns(new RequestedModel()); requestMock.Setup(x => x.DeleteRequest(It.IsAny())); + userRepoMock.Setup(x => x.GetAll()).Returns(userModels); + userRepoMock.Setup(x => x.Update(It.IsAny())).Returns(true); + + mapperMock.Setup(x => x.ValidateUser("user1", It.IsAny())).Returns(Guid.NewGuid()); + mapperMock.Setup(x => x.UpdatePassword("user1", "password", "newpassword")).Returns(true); + Bootstrapper = new ConfigurableBootstrapper(with => { with.Module(); with.Dependency(requestMock.Object); with.Dependency(settingsMock.Object); + with.Dependency(userRepoMock.Object); + with.Dependency(mapperMock.Object); with.RootPathProvider(); with.ModelValidatorLocator( new DefaultValidatorLocator( - new List() + new List { new FluentValidationValidatorFactory( new DefaultFluentAdapterFactory(new List()), - new List { new RequestedModelValidator() }) + new List { new RequestedModelValidator(), new UserViewModelValidator() }) })); }); - } private Action GetBrowser() @@ -192,6 +209,7 @@ namespace PlexRequests.UI.Tests } [Test] + [Description("Should file the validation")] public void CreateAEmptyRequest() { var browser = new Browser(Bootstrapper); @@ -204,5 +222,147 @@ namespace PlexRequests.UI.Tests Assert.That(body.Error, Is.True); Assert.That(body.ErrorMessage, Is.Not.Null.Or.Empty); } + + [Test] + public void UpdateUsersPassword() + { + var model = new UserUpdateViewModel + { + CurrentPassword = "password", + NewPassword = "newpassword" + }; + var browser = new Browser(Bootstrapper); + var result = browser.Put("/api/credentials/user1", with => + { + with.HttpRequest(); + with.Header("Accept", "application/json"); + with.Query("apikey", "api"); + with.JsonBody(model); + }); + + Assert.That(HttpStatusCode.OK, Is.EqualTo(result.StatusCode)); + + var body = JsonConvert.DeserializeObject>(result.Body.AsString()); + Assert.That(body.Data, Is.Not.Null.Or.Empty); + Assert.That(body.Error, Is.False); + Assert.That(body.ErrorMessage, Is.Null.Or.Empty); + } + + [Test] + public void UpdateInvalidUsersPassword() + { + var model = new UserUpdateViewModel + { + CurrentPassword = "password", + NewPassword = "newpassword" + }; + var browser = new Browser(Bootstrapper); + var result = browser.Put("/api/credentials/user99", with => + { + with.HttpRequest(); + with.Header("Accept", "application/json"); + with.Query("apikey", "api"); + with.JsonBody(model); + }); + + Assert.That(HttpStatusCode.OK, Is.EqualTo(result.StatusCode)); + + var body = JsonConvert.DeserializeObject>(result.Body.AsString()); + Assert.That(body.Data, Is.Null.Or.Empty); + Assert.That(body.Error, Is.True); + Assert.That(body.ErrorMessage, Is.Not.Null.Or.Empty); + } + + [Test] + public void UpdateUsersInvalidPassword() + { + var model = new UserUpdateViewModel + { + CurrentPassword = "password", + NewPassword = "password2" + }; + var browser = new Browser(Bootstrapper); + var result = browser.Put("/api/credentials/user1", with => + { + with.HttpRequest(); + with.Header("Accept", "application/json"); + with.Query("apikey", "api"); + with.JsonBody(model); + }); + + Assert.That(HttpStatusCode.OK, Is.EqualTo(result.StatusCode)); + + var body = JsonConvert.DeserializeObject>(result.Body.AsString()); + Assert.That(body.Data, Is.Null.Or.Empty); + Assert.That(body.Error, Is.True); + Assert.That(body.ErrorMessage, Is.Not.Null.Or.Empty); + } + + [Test] + public void UpdateUsersWithBadModel() + { + var model = new UserUpdateViewModel + { + CurrentPassword = null, + NewPassword = "password2" + }; + var browser = new Browser(Bootstrapper); + var result = browser.Put("/api/credentials/user1", with => + { + with.HttpRequest(); + with.Header("Accept", "application/json"); + with.Query("apikey", "api"); + with.JsonBody(model); + }); + + Assert.That(HttpStatusCode.OK, Is.EqualTo(result.StatusCode)); + + var body = JsonConvert.DeserializeObject>(result.Body.AsString()); + Assert.That(body.Data.Length, Is.GreaterThan(0)); + Assert.That(body.Error, Is.True); + Assert.That(body.ErrorMessage, Is.Not.Null.Or.Empty); + } + + [Test] + public void GetApiKey() + { + var browser = new Browser(Bootstrapper); + var result = browser.Get("/api/apikey", with => + { + with.HttpRequest(); + with.Header("Accept", "application/json"); + with.Query("apikey", "api"); + with.Query("username","user1"); + with.Query("password","password"); + }); + + Assert.That(HttpStatusCode.OK, Is.EqualTo(result.StatusCode)); + + var body = JsonConvert.DeserializeObject>(result.Body.AsString()); + Assert.That(body.Data, Is.Not.Null.Or.Empty); + Assert.That(body.Error, Is.False); + Assert.That(body.ErrorMessage, Is.Null.Or.Empty); + } + + [Test] + public void GetApiKeyWithBadCredentials() + { + var browser = new Browser(Bootstrapper); + var result = browser.Get("/api/apikey", with => + { + with.HttpRequest(); + with.Header("Accept", "application/json"); + with.Query("apikey", "api"); + with.Query("username", "user"); + with.Query("password", "password"); + }); + + Assert.That(HttpStatusCode.OK, Is.EqualTo(result.StatusCode)); + + var body = JsonConvert.DeserializeObject>(result.Body.AsString()); + Assert.That(body.Data, Is.Null.Or.Empty); + Assert.That(body.Error, Is.True); + Assert.That(body.ErrorMessage, Is.Not.Null.Or.Empty); + } } } \ No newline at end of file diff --git a/PlexRequests.UI.Tests/UserLoginModuleTests.cs b/PlexRequests.UI.Tests/UserLoginModuleTests.cs index f38f17ce0..cc4db6843 100644 --- a/PlexRequests.UI.Tests/UserLoginModuleTests.cs +++ b/PlexRequests.UI.Tests/UserLoginModuleTests.cs @@ -30,7 +30,6 @@ using Moq; using Nancy; using Nancy.Testing; -using Nancy.TinyIoc; using Newtonsoft.Json; @@ -40,7 +39,6 @@ using PlexRequests.Api.Interfaces; using PlexRequests.Api.Models.Plex; using PlexRequests.Core; using PlexRequests.Core.SettingModels; -using PlexRequests.UI.Helpers; using PlexRequests.UI.Models; using PlexRequests.UI.Modules; diff --git a/PlexRequests.UI/Bootstrapper.cs b/PlexRequests.UI/Bootstrapper.cs index 28d8905da..c2e31d5dd 100644 --- a/PlexRequests.UI/Bootstrapper.cs +++ b/PlexRequests.UI/Bootstrapper.cs @@ -69,6 +69,7 @@ namespace PlexRequests.UI protected override void ConfigureRequestContainer(TinyIoCContainer container, NancyContext context) { container.Register(); + container.Register(); container.Register(new DbConfiguration(new SqliteFactory())); container.Register().AsSingleton(); @@ -87,6 +88,7 @@ namespace PlexRequests.UI // Repo's container.Register, GenericRepository>(); + container.Register, UserRepository>(); container.Register, GenericRepository>(); container.Register(); container.Register(); diff --git a/PlexRequests.UI/ModelDataProviders/UserUpdateViewModel.cs b/PlexRequests.UI/ModelDataProviders/UserUpdateViewModel.cs new file mode 100644 index 000000000..4c626a555 --- /dev/null +++ b/PlexRequests.UI/ModelDataProviders/UserUpdateViewModel.cs @@ -0,0 +1,55 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: RequestedModelDataProvider.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 Nancy.Swagger; +using Nancy.Swagger.Services; + +using PlexRequests.Store; + +namespace PlexRequests.UI.ModelDataProviders +{ + public class UserUpdateViewModel : ISwaggerModelDataProvider + { + /// + /// Gets the model data for the api documentation. + /// + /// + public SwaggerModelData GetModelData() + { + return SwaggerModelData.ForType(with => + { + with.Property(x => x.CurrentPassword) + .Description("The users current password") + .Required(true); + + with.Property(x => x.NewPassword) + .Description("The users new password that we will change it to") + .Required(true); + }); + } + } +} \ No newline at end of file diff --git a/PlexRequests.UI/Models/UserUpdateViewModel.cs b/PlexRequests.UI/Models/UserUpdateViewModel.cs new file mode 100644 index 000000000..550c2381b --- /dev/null +++ b/PlexRequests.UI/Models/UserUpdateViewModel.cs @@ -0,0 +1,34 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: UserUpdateViewModel.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.UI.Models +{ + public class UserUpdateViewModel + { + public string CurrentPassword { get; set; } + public string NewPassword { get; set; } + } +} \ No newline at end of file diff --git a/PlexRequests.UI/Modules/AdminModule.cs b/PlexRequests.UI/Modules/AdminModule.cs index 56bc64df8..62348a269 100644 --- a/PlexRequests.UI/Modules/AdminModule.cs +++ b/PlexRequests.UI/Modules/AdminModule.cs @@ -709,17 +709,14 @@ namespace PlexRequests.UI.Modules private Response CreateApiKey() { - this.RequiresClaims (UserClaims.Admin); - - var apiKey = Guid.NewGuid ().ToString ("N"); - - var settings = PrService.GetSettings (); + this.RequiresClaims(UserClaims.Admin); + var apiKey = Guid.NewGuid().ToString("N"); + var settings = PrService.GetSettings(); settings.ApiKey = apiKey; + PrService.SaveSettings(settings); - PrService.SaveSettings (settings); - - return Response.AsJson (apiKey); + return Response.AsJson(apiKey); } } } \ No newline at end of file diff --git a/PlexRequests.UI/Modules/ApiMetadataModule.cs b/PlexRequests.UI/Modules/ApiMetadataModule.cs index 6ea6ea8a5..6418ca158 100644 --- a/PlexRequests.UI/Modules/ApiMetadataModule.cs +++ b/PlexRequests.UI/Modules/ApiMetadataModule.cs @@ -30,6 +30,7 @@ using Nancy.Metadata.Modules; using Nancy.Swagger; using PlexRequests.Store; +using PlexRequests.UI.Models; namespace PlexRequests.UI.Modules { @@ -83,11 +84,32 @@ namespace PlexRequests.UI.Modules with.ResourcePath("/requests/{id}"); with.Summary("Deletes an existing request"); with.Model>(); - with.PathParam("id"); + with.PathParam("id", required:true); with.QueryParam("apikey", "The Api Key found in the settings", true); with.Notes("Deletes an existing request. If the request doesn't exist we will return an error."); }); + Describe["GetApiKey"] = description => description.AsSwagger(with => + { + with.ResourcePath("/apikey"); + with.Summary("Gets the Api Key for Plex Requests"); + with.Model>(); + with.QueryParam("username", required:true ); + with.QueryParam("password", required: true ); + with.Notes("Get's the current api key for the application"); + }); + + Describe["PutCredentials"] = description => description.AsSwagger(with => + { + with.ResourcePath("/credentials/{username}"); + with.Summary("Sets a new password for the user"); + with.Model>(); + with.PathParam("username", required:true); + with.QueryParam("apikey", "The Api Key found in the settings", true); + with.BodyParam("User update view model", true); + with.Notes("Sets a new password for the user"); + }); + } } } \ No newline at end of file diff --git a/PlexRequests.UI/Modules/ApiModule.cs b/PlexRequests.UI/Modules/ApiModule.cs index e45981a9f..441d32485 100644 --- a/PlexRequests.UI/Modules/ApiModule.cs +++ b/PlexRequests.UI/Modules/ApiModule.cs @@ -26,7 +26,6 @@ #endregion using System; using System.Collections.Generic; -using System.Diagnostics; using Nancy; using Nancy.ModelBinding; @@ -34,12 +33,13 @@ using Nancy.ModelBinding; using PlexRequests.Core; using PlexRequests.Core.SettingModels; using PlexRequests.Store; +using PlexRequests.UI.Models; namespace PlexRequests.UI.Modules { public class ApiModule : BaseApiModule { - public ApiModule(IRequestService service, ISettingsService pr) : base("api", pr) + public ApiModule(IRequestService service, ISettingsService pr, ICustomUserMapper m) : base("api", pr) { Get["GetRequests","/requests"] = x => GetRequests(); Get["GetRequest","/requests/{id}"] = x => GetSingleRequests(x); @@ -47,10 +47,18 @@ namespace PlexRequests.UI.Modules Put["PutRequests", "/requests"] = x => UpdateRequest(); Delete["DeleteRequests", "/requests/{id}"] = x => DeleteRequest(x); + Get["GetApiKey", "/apikey"] = x => GetApiKey(); + + Put["PutCredentials", "/credentials/{username}"] = x => ChangePassword(x); + RequestService = service; + SettingsService = pr; + UserMapper = m; } private IRequestService RequestService { get; } + private ISettingsService SettingsService { get; } + private ICustomUserMapper UserMapper { get; } public Response GetRequests() { @@ -156,6 +164,54 @@ namespace PlexRequests.UI.Modules } } + public Response GetApiKey() + { + var user = Request.Query["username"]; + var password = Request.Query["password"]; + var result = UserMapper.ValidateUser(user, password); + var model = new ApiModel(); + if (result == null) + { + model.Error = true; + model.ErrorMessage = "Incorrect username or password"; + return ReturnReponse(model); + } + + var settings = SettingsService.GetSettings(); + model.Data = settings.ApiKey; + + return ReturnReponse(model); + } + + public Response ChangePassword(dynamic x) + { + var username = (string)x.username; + var userModel = this.BindAndValidate(); + + if (!ModelValidationResult.IsValid) + { + return ReturnValidationReponse(ModelValidationResult); + } + + var valid = UserMapper.ValidateUser(username, userModel.CurrentPassword); + if (valid == null) + { + var errorModel = new ApiModel { Error = true, ErrorMessage = "Incorrect username or password" }; + return ReturnReponse(errorModel); + } + var result = UserMapper.UpdatePassword(username, userModel.CurrentPassword, userModel.NewPassword); + + if (!result) + { + var errorModel = new ApiModel { Error = true, ErrorMessage = "Could not update the password. " }; + return ReturnReponse(errorModel); + } + + + var model = new ApiModel { Data = "Successfully updated the password"}; + return ReturnReponse(model); + } + } } \ No newline at end of file diff --git a/PlexRequests.UI/Modules/BaseApiModule.cs b/PlexRequests.UI/Modules/BaseApiModule.cs index 4ed4b8031..c7c1f4fc8 100644 --- a/PlexRequests.UI/Modules/BaseApiModule.cs +++ b/PlexRequests.UI/Modules/BaseApiModule.cs @@ -86,6 +86,10 @@ namespace PlexRequests.UI.Modules private Response CheckAuth() { + if (Request.Path.Contains("api/apikey")) // We do not need the apikey for this call + { + return null; + } var settings = Settings.GetSettings(); var apiModel = new ApiModel> { Data = new List() }; if (!Authenticated(settings)) diff --git a/PlexRequests.UI/Modules/LoginModule.cs b/PlexRequests.UI/Modules/LoginModule.cs index 14fbf49d0..326b7ec13 100644 --- a/PlexRequests.UI/Modules/LoginModule.cs +++ b/PlexRequests.UI/Modules/LoginModule.cs @@ -41,8 +41,9 @@ namespace PlexRequests.UI.Modules { public class LoginModule : BaseModule { - public LoginModule(ISettingsService pr) : base(pr) + public LoginModule(ISettingsService pr, ICustomUserMapper m) : base(pr) { + UserMapper = m; Get["/login"] = _ => { { @@ -110,6 +111,7 @@ namespace PlexRequests.UI.Modules Get["/changepassword"] = _ => ChangePassword(); Post["/changepassword"] = _ => ChangePasswordPost(); } + private ICustomUserMapper UserMapper { get; } private Negotiator ChangePassword() { diff --git a/PlexRequests.UI/Modules/UserManagementModule.cs b/PlexRequests.UI/Modules/UserManagementModule.cs index b2d8a6a2d..c93977cd7 100644 --- a/PlexRequests.UI/Modules/UserManagementModule.cs +++ b/PlexRequests.UI/Modules/UserManagementModule.cs @@ -13,13 +13,15 @@ namespace PlexRequests.UI.Modules { public class UserManagementModule : BaseModule { - public UserManagementModule(ISettingsService pr) : base("usermanagement",pr) + public UserManagementModule(ISettingsService pr, ICustomUserMapper m) : base("usermanagement",pr) { this.RequiresClaims(UserClaims.Admin); Get["/"] = x => Load(); Get["/users"] = x => LoadUsers(); + UserMapper = m; } + private ICustomUserMapper UserMapper { get; } private Negotiator Load() { diff --git a/PlexRequests.UI/PlexRequests.UI.csproj b/PlexRequests.UI/PlexRequests.UI.csproj index e74ce7fc1..67e2135b3 100644 --- a/PlexRequests.UI/PlexRequests.UI.csproj +++ b/PlexRequests.UI/PlexRequests.UI.csproj @@ -164,6 +164,7 @@ + @@ -172,6 +173,7 @@ + @@ -179,6 +181,7 @@ + diff --git a/PlexRequests.UI/Validators/UserViewModelValidator.cs b/PlexRequests.UI/Validators/UserViewModelValidator.cs new file mode 100644 index 000000000..0e4388adc --- /dev/null +++ b/PlexRequests.UI/Validators/UserViewModelValidator.cs @@ -0,0 +1,41 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: SonarrValidator.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 FluentValidation; + +using PlexRequests.UI.Models; + +namespace PlexRequests.UI.Validators +{ + public class UserViewModelValidator : AbstractValidator + { + public UserViewModelValidator() + { + RuleFor(request => request.CurrentPassword).NotEmpty().NotNull(); + RuleFor(request => request.NewPassword).NotEmpty().NotNull(); + } + } +} \ No newline at end of file