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
pull/226/head
tidusjar 9 years ago
parent 84dc4515fd
commit cbfe88cd6d

@ -36,21 +36,20 @@ using Nancy.Security;
using PlexRequests.Core.Models; using PlexRequests.Core.Models;
using PlexRequests.Helpers; using PlexRequests.Helpers;
using PlexRequests.Store; using PlexRequests.Store;
using PlexRequests.Store.Repository;
namespace PlexRequests.Core namespace PlexRequests.Core
{ {
public class UserMapper : IUserMapper public class UserMapper : IUserMapper, ICustomUserMapper
{ {
public UserMapper(ISqliteConfiguration db) public UserMapper(IRepository<UsersModel> repo)
{ {
Db = db; Repo = repo;
} }
private static ISqliteConfiguration Db { get; set; } private static IRepository<UsersModel> Repo { get; set; }
public IUserIdentity GetUserFromIdentifier(Guid identifier, NancyContext context) public IUserIdentity GetUserFromIdentifier(Guid identifier, NancyContext context)
{ {
var repo = new UserRepository<UsersModel>(Db); var user = Repo.Get(identifier.ToString());
var user = repo.Get(identifier.ToString());
if (user == null) 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<UsersModel>(Db); var users = Repo.GetAll();
var users = repo.GetAll();
foreach (var u in users) foreach (var u in users)
{ {
@ -83,17 +81,15 @@ namespace PlexRequests.Core
return null; return null;
} }
public static bool DoUsersExist() public bool DoUsersExist()
{ {
var repo = new UserRepository<UsersModel>(Db); var users = Repo.GetAll();
var users = repo.GetAll();
return users.Any(); 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<UsersModel>(Db);
var salt = PasswordHasher.GenerateSalt(); var salt = PasswordHasher.GenerateSalt();
var userModel = new UsersModel var userModel = new UsersModel
@ -105,17 +101,16 @@ namespace PlexRequests.Core
Claims = ByteConverterHelper.ReturnBytes(claims), Claims = ByteConverterHelper.ReturnBytes(claims),
UserProperties = ByteConverterHelper.ReturnBytes(new UserProperties()) 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); 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<UsersModel>(Db); var users = Repo.GetAll();
var users = repo.GetAll();
var userToChange = users.FirstOrDefault(x => x.UserName == username); var userToChange = users.FirstOrDefault(x => x.UserName == username);
if (userToChange == null) if (userToChange == null)
return false; return false;
@ -132,13 +127,22 @@ namespace PlexRequests.Core
userToChange.Hash = newHash; userToChange.Hash = newHash;
userToChange.Salt = newSalt; userToChange.Salt = newSalt;
return repo.Update(userToChange); return Repo.Update(userToChange);
} }
public static IEnumerable<UsersModel> GetUsers() public IEnumerable<UsersModel> GetUsers()
{ {
var repo = new UserRepository<UsersModel>(Db); return Repo.GetAll();
return repo.GetAll();
} }
} }
public interface ICustomUserMapper
{
IEnumerable<UsersModel> 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);
}
} }

@ -34,7 +34,6 @@ using FluentValidation;
using Moq; using Moq;
using Nancy; using Nancy;
using Nancy.Bootstrapper;
using Nancy.Testing; using Nancy.Testing;
using Nancy.Validation; using Nancy.Validation;
using Nancy.Validation.FluentValidation; using Nancy.Validation.FluentValidation;
@ -46,6 +45,7 @@ using NUnit.Framework;
using PlexRequests.Core; using PlexRequests.Core;
using PlexRequests.Core.SettingModels; using PlexRequests.Core.SettingModels;
using PlexRequests.Store; using PlexRequests.Store;
using PlexRequests.Store.Repository;
using PlexRequests.UI.Models; using PlexRequests.UI.Models;
using PlexRequests.UI.Modules; using PlexRequests.UI.Modules;
using PlexRequests.UI.Validators; using PlexRequests.UI.Validators;
@ -62,31 +62,48 @@ namespace PlexRequests.UI.Tests
[SetUp] [SetUp]
public void Setup() public void Setup()
{ {
var requests = new Fixture().CreateMany<RequestedModel>(); var fixture = new Fixture();
var requests = fixture.CreateMany<RequestedModel>();
var requestMock = new Mock<IRequestService>(); var requestMock = new Mock<IRequestService>();
var settingsMock = new Mock<ISettingsService<PlexRequestSettings>>(); var settingsMock = new Mock<ISettingsService<PlexRequestSettings>>();
var userRepoMock = new Mock<IRepository<UsersModel>>();
var mapperMock = new Mock<ICustomUserMapper>();
var userModels = fixture.CreateMany<UsersModel>().ToList();
userModels.Add(new UsersModel
{
UserName = "user1"
});
settingsMock.Setup(x => x.GetSettings()).Returns(new PlexRequestSettings {ApiKey = "api"}); settingsMock.Setup(x => x.GetSettings()).Returns(new PlexRequestSettings {ApiKey = "api"});
requestMock.Setup(x => x.GetAll()).Returns(requests); requestMock.Setup(x => x.GetAll()).Returns(requests);
requestMock.Setup(x => x.Get(1)).Returns(requests.FirstOrDefault()); requestMock.Setup(x => x.Get(1)).Returns(requests.FirstOrDefault());
requestMock.Setup(x => x.Get(99)).Returns(new RequestedModel()); requestMock.Setup(x => x.Get(99)).Returns(new RequestedModel());
requestMock.Setup(x => x.DeleteRequest(It.IsAny<RequestedModel>())); requestMock.Setup(x => x.DeleteRequest(It.IsAny<RequestedModel>()));
userRepoMock.Setup(x => x.GetAll()).Returns(userModels);
userRepoMock.Setup(x => x.Update(It.IsAny<UsersModel>())).Returns(true);
mapperMock.Setup(x => x.ValidateUser("user1", It.IsAny<string>())).Returns(Guid.NewGuid());
mapperMock.Setup(x => x.UpdatePassword("user1", "password", "newpassword")).Returns(true);
Bootstrapper = new ConfigurableBootstrapper(with => Bootstrapper = new ConfigurableBootstrapper(with =>
{ {
with.Module<ApiModule>(); with.Module<ApiModule>();
with.Dependency(requestMock.Object); with.Dependency(requestMock.Object);
with.Dependency(settingsMock.Object); with.Dependency(settingsMock.Object);
with.Dependency(userRepoMock.Object);
with.Dependency(mapperMock.Object);
with.RootPathProvider<TestRootPathProvider>(); with.RootPathProvider<TestRootPathProvider>();
with.ModelValidatorLocator( with.ModelValidatorLocator(
new DefaultValidatorLocator( new DefaultValidatorLocator(
new List<IModelValidatorFactory>() new List<IModelValidatorFactory>
{ {
new FluentValidationValidatorFactory( new FluentValidationValidatorFactory(
new DefaultFluentAdapterFactory(new List<IFluentAdapter>()), new DefaultFluentAdapterFactory(new List<IFluentAdapter>()),
new List<IValidator> { new RequestedModelValidator() }) new List<IValidator> { new RequestedModelValidator(), new UserViewModelValidator() })
})); }));
}); });
} }
private Action<BrowserContext> GetBrowser() private Action<BrowserContext> GetBrowser()
@ -192,6 +209,7 @@ namespace PlexRequests.UI.Tests
} }
[Test] [Test]
[Description("Should file the validation")]
public void CreateAEmptyRequest() public void CreateAEmptyRequest()
{ {
var browser = new Browser(Bootstrapper); var browser = new Browser(Bootstrapper);
@ -204,5 +222,147 @@ namespace PlexRequests.UI.Tests
Assert.That(body.Error, Is.True); Assert.That(body.Error, Is.True);
Assert.That(body.ErrorMessage, Is.Not.Null.Or.Empty); 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<ApiModel<string>>(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<ApiModel<string>>(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<ApiModel<string>>(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<ApiModel<string[]>>(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<ApiModel<string>>(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<ApiModel<string>>(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);
}
} }
} }

@ -30,7 +30,6 @@ using Moq;
using Nancy; using Nancy;
using Nancy.Testing; using Nancy.Testing;
using Nancy.TinyIoc;
using Newtonsoft.Json; using Newtonsoft.Json;
@ -40,7 +39,6 @@ using PlexRequests.Api.Interfaces;
using PlexRequests.Api.Models.Plex; using PlexRequests.Api.Models.Plex;
using PlexRequests.Core; using PlexRequests.Core;
using PlexRequests.Core.SettingModels; using PlexRequests.Core.SettingModels;
using PlexRequests.UI.Helpers;
using PlexRequests.UI.Models; using PlexRequests.UI.Models;
using PlexRequests.UI.Modules; using PlexRequests.UI.Modules;

@ -69,6 +69,7 @@ namespace PlexRequests.UI
protected override void ConfigureRequestContainer(TinyIoCContainer container, NancyContext context) protected override void ConfigureRequestContainer(TinyIoCContainer container, NancyContext context)
{ {
container.Register<IUserMapper, UserMapper>(); container.Register<IUserMapper, UserMapper>();
container.Register<ICustomUserMapper, UserMapper>();
container.Register<ISqliteConfiguration, DbConfiguration>(new DbConfiguration(new SqliteFactory())); container.Register<ISqliteConfiguration, DbConfiguration>(new DbConfiguration(new SqliteFactory()));
container.Register<ICacheProvider, MemoryCacheProvider>().AsSingleton(); container.Register<ICacheProvider, MemoryCacheProvider>().AsSingleton();
@ -87,6 +88,7 @@ namespace PlexRequests.UI
// Repo's // Repo's
container.Register<IRepository<LogEntity>, GenericRepository<LogEntity>>(); container.Register<IRepository<LogEntity>, GenericRepository<LogEntity>>();
container.Register<IRepository<UsersModel>, UserRepository<UsersModel>>();
container.Register<IRepository<ScheduledJobs>, GenericRepository<ScheduledJobs>>(); container.Register<IRepository<ScheduledJobs>, GenericRepository<ScheduledJobs>>();
container.Register<IRequestService, JsonRequestService>(); container.Register<IRequestService, JsonRequestService>();
container.Register<ISettingsRepository, SettingsJsonRepository>(); container.Register<ISettingsRepository, SettingsJsonRepository>();

@ -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
{
/// <summary>
/// Gets the model data for the api documentation.
/// </summary>
/// <returns></returns>
public SwaggerModelData GetModelData()
{
return SwaggerModelData.ForType<Models.UserUpdateViewModel>(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);
});
}
}
}

@ -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; }
}
}

@ -709,17 +709,14 @@ namespace PlexRequests.UI.Modules
private Response CreateApiKey() private Response CreateApiKey()
{ {
this.RequiresClaims (UserClaims.Admin); this.RequiresClaims(UserClaims.Admin);
var apiKey = Guid.NewGuid().ToString("N");
var apiKey = Guid.NewGuid ().ToString ("N"); var settings = PrService.GetSettings();
var settings = PrService.GetSettings ();
settings.ApiKey = apiKey; settings.ApiKey = apiKey;
PrService.SaveSettings(settings);
PrService.SaveSettings (settings); return Response.AsJson(apiKey);
return Response.AsJson (apiKey);
} }
} }
} }

@ -30,6 +30,7 @@ using Nancy.Metadata.Modules;
using Nancy.Swagger; using Nancy.Swagger;
using PlexRequests.Store; using PlexRequests.Store;
using PlexRequests.UI.Models;
namespace PlexRequests.UI.Modules namespace PlexRequests.UI.Modules
{ {
@ -83,11 +84,32 @@ namespace PlexRequests.UI.Modules
with.ResourcePath("/requests/{id}"); with.ResourcePath("/requests/{id}");
with.Summary("Deletes an existing request"); with.Summary("Deletes an existing request");
with.Model<ApiModel<bool>>(); with.Model<ApiModel<bool>>();
with.PathParam<int>("id"); with.PathParam<int>("id", required:true);
with.QueryParam<string>("apikey", "The Api Key found in the settings", true); with.QueryParam<string>("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."); 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<ApiModel<string>>();
with.QueryParam<string>("username", required:true );
with.QueryParam<string>("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<ApiModel<string>>();
with.PathParam<int>("username", required:true);
with.QueryParam<string>("apikey", "The Api Key found in the settings", true);
with.BodyParam<UserUpdateViewModel>("User update view model", true);
with.Notes("Sets a new password for the user");
});
} }
} }
} }

@ -26,7 +26,6 @@
#endregion #endregion
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using Nancy; using Nancy;
using Nancy.ModelBinding; using Nancy.ModelBinding;
@ -34,12 +33,13 @@ using Nancy.ModelBinding;
using PlexRequests.Core; using PlexRequests.Core;
using PlexRequests.Core.SettingModels; using PlexRequests.Core.SettingModels;
using PlexRequests.Store; using PlexRequests.Store;
using PlexRequests.UI.Models;
namespace PlexRequests.UI.Modules namespace PlexRequests.UI.Modules
{ {
public class ApiModule : BaseApiModule public class ApiModule : BaseApiModule
{ {
public ApiModule(IRequestService service, ISettingsService<PlexRequestSettings> pr) : base("api", pr) public ApiModule(IRequestService service, ISettingsService<PlexRequestSettings> pr, ICustomUserMapper m) : base("api", pr)
{ {
Get["GetRequests","/requests"] = x => GetRequests(); Get["GetRequests","/requests"] = x => GetRequests();
Get["GetRequest","/requests/{id}"] = x => GetSingleRequests(x); Get["GetRequest","/requests/{id}"] = x => GetSingleRequests(x);
@ -47,10 +47,18 @@ namespace PlexRequests.UI.Modules
Put["PutRequests", "/requests"] = x => UpdateRequest(); Put["PutRequests", "/requests"] = x => UpdateRequest();
Delete["DeleteRequests", "/requests/{id}"] = x => DeleteRequest(x); Delete["DeleteRequests", "/requests/{id}"] = x => DeleteRequest(x);
Get["GetApiKey", "/apikey"] = x => GetApiKey();
Put["PutCredentials", "/credentials/{username}"] = x => ChangePassword(x);
RequestService = service; RequestService = service;
SettingsService = pr;
UserMapper = m;
} }
private IRequestService RequestService { get; } private IRequestService RequestService { get; }
private ISettingsService<PlexRequestSettings> SettingsService { get; }
private ICustomUserMapper UserMapper { get; }
public Response GetRequests() 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<string>();
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<UserUpdateViewModel>();
if (!ModelValidationResult.IsValid)
{
return ReturnValidationReponse(ModelValidationResult);
}
var valid = UserMapper.ValidateUser(username, userModel.CurrentPassword);
if (valid == null)
{
var errorModel = new ApiModel<string> { 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<string> { Error = true, ErrorMessage = "Could not update the password. " };
return ReturnReponse(errorModel);
}
var model = new ApiModel<string> { Data = "Successfully updated the password"};
return ReturnReponse(model);
}
} }
} }

@ -86,6 +86,10 @@ namespace PlexRequests.UI.Modules
private Response CheckAuth() 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 settings = Settings.GetSettings();
var apiModel = new ApiModel<List<RequestedModel>> { Data = new List<RequestedModel>() }; var apiModel = new ApiModel<List<RequestedModel>> { Data = new List<RequestedModel>() };
if (!Authenticated(settings)) if (!Authenticated(settings))

@ -41,8 +41,9 @@ namespace PlexRequests.UI.Modules
{ {
public class LoginModule : BaseModule public class LoginModule : BaseModule
{ {
public LoginModule(ISettingsService<PlexRequestSettings> pr) : base(pr) public LoginModule(ISettingsService<PlexRequestSettings> pr, ICustomUserMapper m) : base(pr)
{ {
UserMapper = m;
Get["/login"] = _ => Get["/login"] = _ =>
{ {
{ {
@ -110,6 +111,7 @@ namespace PlexRequests.UI.Modules
Get["/changepassword"] = _ => ChangePassword(); Get["/changepassword"] = _ => ChangePassword();
Post["/changepassword"] = _ => ChangePasswordPost(); Post["/changepassword"] = _ => ChangePasswordPost();
} }
private ICustomUserMapper UserMapper { get; }
private Negotiator ChangePassword() private Negotiator ChangePassword()
{ {

@ -13,13 +13,15 @@ namespace PlexRequests.UI.Modules
{ {
public class UserManagementModule : BaseModule public class UserManagementModule : BaseModule
{ {
public UserManagementModule(ISettingsService<PlexRequestSettings> pr) : base("usermanagement",pr) public UserManagementModule(ISettingsService<PlexRequestSettings> pr, ICustomUserMapper m) : base("usermanagement",pr)
{ {
this.RequiresClaims(UserClaims.Admin); this.RequiresClaims(UserClaims.Admin);
Get["/"] = x => Load(); Get["/"] = x => Load();
Get["/users"] = x => LoadUsers(); Get["/users"] = x => LoadUsers();
UserMapper = m;
} }
private ICustomUserMapper UserMapper { get; }
private Negotiator Load() private Negotiator Load()
{ {

@ -164,6 +164,7 @@
<Compile Include="Helpers\ValidationHelper.cs" /> <Compile Include="Helpers\ValidationHelper.cs" />
<Compile Include="Jobs\CustomJobFactory.cs" /> <Compile Include="Jobs\CustomJobFactory.cs" />
<Compile Include="Jobs\Scheduler.cs" /> <Compile Include="Jobs\Scheduler.cs" />
<Compile Include="ModelDataProviders\UserUpdateViewModel.cs" />
<Compile Include="ModelDataProviders\RequestedModelDataProvider.cs" /> <Compile Include="ModelDataProviders\RequestedModelDataProvider.cs" />
<Compile Include="Models\DatatablesModel.cs" /> <Compile Include="Models\DatatablesModel.cs" />
<Compile Include="Models\JsonUpdateAvailableModel.cs" /> <Compile Include="Models\JsonUpdateAvailableModel.cs" />
@ -172,6 +173,7 @@
<Compile Include="Models\SearchViewModel.cs" /> <Compile Include="Models\SearchViewModel.cs" />
<Compile Include="Models\SearchMusicViewModel.cs" /> <Compile Include="Models\SearchMusicViewModel.cs" />
<Compile Include="Models\SearchMovieViewModel.cs" /> <Compile Include="Models\SearchMovieViewModel.cs" />
<Compile Include="Models\UserUpdateViewModel.cs" />
<Compile Include="Modules\ApiDocsModule.cs" /> <Compile Include="Modules\ApiDocsModule.cs" />
<Compile Include="Modules\ApiMetadataModule.cs" /> <Compile Include="Modules\ApiMetadataModule.cs" />
<Compile Include="Modules\BaseApiModule.cs" /> <Compile Include="Modules\BaseApiModule.cs" />
@ -179,6 +181,7 @@
<Compile Include="Modules\UpdateCheckerModule.cs" /> <Compile Include="Modules\UpdateCheckerModule.cs" />
<Compile Include="Start\StartupOptions.cs" /> <Compile Include="Start\StartupOptions.cs" />
<Compile Include="Start\UpdateValue.cs" /> <Compile Include="Start\UpdateValue.cs" />
<Compile Include="Validators\UserViewModelValidator.cs" />
<Compile Include="Validators\HeadphonesValidator.cs" /> <Compile Include="Validators\HeadphonesValidator.cs" />
<Compile Include="Validators\PushoverSettingsValidator.cs" /> <Compile Include="Validators\PushoverSettingsValidator.cs" />
<Compile Include="Validators\PushbulletSettingsValidator.cs" /> <Compile Include="Validators\PushbulletSettingsValidator.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<UserUpdateViewModel>
{
public UserViewModelValidator()
{
RuleFor(request => request.CurrentPassword).NotEmpty().NotNull();
RuleFor(request => request.NewPassword).NotEmpty().NotNull();
}
}
}
Loading…
Cancel
Save