Skip to content

Commit

Permalink
Added the ability to get the apikey from the api if you provide a cor…
Browse files Browse the repository at this point in the history
…rect 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
  • Loading branch information
tidusjar committed May 20, 2016
1 parent 84dc451 commit cbfe88c
Show file tree
Hide file tree
Showing 14 changed files with 424 additions and 44 deletions.
52 changes: 28 additions & 24 deletions PlexRequests.Core/UserMapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,21 +36,20 @@
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<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)
{
var repo = new UserRepository<UsersModel>(Db);

var user = repo.Get(identifier.ToString());
var user = Repo.Get(identifier.ToString());

if (user == null)
{
Expand All @@ -64,10 +63,9 @@ public IUserIdentity GetUserFromIdentifier(Guid identifier, NancyContext context
};
}

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)
{
Expand All @@ -83,17 +81,15 @@ public IUserIdentity GetUserFromIdentifier(Guid identifier, NancyContext context
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();
}

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 userModel = new UsersModel
Expand All @@ -105,17 +101,16 @@ public static bool DoUsersExist()
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<UsersModel>(Db);
var users = repo.GetAll();
var users = Repo.GetAll();
var userToChange = users.FirstOrDefault(x => x.UserName == username);
if (userToChange == null)
return false;
Expand All @@ -132,13 +127,22 @@ public static bool UpdatePassword(string username, string oldPassword, string ne
userToChange.Hash = newHash;
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);

}
}
170 changes: 165 additions & 5 deletions PlexRequests.UI.Tests/ApiModuleTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
using Moq;

using Nancy;
using Nancy.Bootstrapper;
using Nancy.Testing;
using Nancy.Validation;
using Nancy.Validation.FluentValidation;
Expand All @@ -46,6 +45,7 @@
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;
Expand All @@ -62,31 +62,48 @@ public class ApiModuleTests
[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 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"});
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<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 =>
{
with.Module<ApiModule>();
with.Dependency(requestMock.Object);
with.Dependency(settingsMock.Object);
with.Dependency(userRepoMock.Object);
with.Dependency(mapperMock.Object);
with.RootPathProvider<TestRootPathProvider>();
with.ModelValidatorLocator(
new DefaultValidatorLocator(
new List<IModelValidatorFactory>()
new List<IModelValidatorFactory>
{
new FluentValidationValidatorFactory(
new DefaultFluentAdapterFactory(new List<IFluentAdapter>()),
new List<IValidator> { new RequestedModelValidator() })
new List<IValidator> { new RequestedModelValidator(), new UserViewModelValidator() })
}));
});

}

private Action<BrowserContext> GetBrowser()
Expand Down Expand Up @@ -192,6 +209,7 @@ public void DeleteARequestThatDoesNotExist()
}

[Test]
[Description("Should file the validation")]
public void CreateAEmptyRequest()
{
var browser = new Browser(Bootstrapper);
Expand All @@ -204,5 +222,147 @@ public void CreateAEmptyRequest()
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<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);
}
}
}
2 changes: 0 additions & 2 deletions PlexRequests.UI.Tests/UserLoginModuleTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@

using Nancy;
using Nancy.Testing;
using Nancy.TinyIoc;

using Newtonsoft.Json;

Expand All @@ -40,7 +39,6 @@
using PlexRequests.Api.Models.Plex;
using PlexRequests.Core;
using PlexRequests.Core.SettingModels;
using PlexRequests.UI.Helpers;
using PlexRequests.UI.Models;
using PlexRequests.UI.Modules;

Expand Down
2 changes: 2 additions & 0 deletions PlexRequests.UI/Bootstrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ public class Bootstrapper : DefaultNancyBootstrapper
protected override void ConfigureRequestContainer(TinyIoCContainer container, NancyContext context)
{
container.Register<IUserMapper, UserMapper>();
container.Register<ICustomUserMapper, UserMapper>();
container.Register<ISqliteConfiguration, DbConfiguration>(new DbConfiguration(new SqliteFactory()));
container.Register<ICacheProvider, MemoryCacheProvider>().AsSingleton();

Expand All @@ -87,6 +88,7 @@ protected override void ConfigureRequestContainer(TinyIoCContainer container, Na

// Repo's
container.Register<IRepository<LogEntity>, GenericRepository<LogEntity>>();
container.Register<IRepository<UsersModel>, UserRepository<UsersModel>>();
container.Register<IRepository<ScheduledJobs>, GenericRepository<ScheduledJobs>>();
container.Register<IRequestService, JsonRequestService>();
container.Register<ISettingsRepository, SettingsJsonRepository>();
Expand Down
Loading

0 comments on commit cbfe88c

Please sign in to comment.