Refactored how we check if the user has a valid api key
Added POST request, PUT and DELTE

Also some work on the updater #29
pull/226/head
tidusjar 9 years ago
parent 030c013862
commit 7266f20927

@ -1,71 +1,137 @@
using System; #region Copyright
using PlexRequests.UI.Modules; // /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: ApiModule.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 Nancy; using Nancy;
using Nancy.Extensions;
using Nancy.ModelBinding; using Nancy.ModelBinding;
using Nancy.Responses.Negotiation;
using Nancy.Validation; using Nancy.Validation;
using PlexRequests.Core; using PlexRequests.Core;
using System.Collections.Generic;
using PlexRequests.Store; using PlexRequests.Store;
using PlexRequests.Core.SettingModels;
namespace PlexRequests.UI.Modules namespace PlexRequests.UI.Modules
{ {
public class ApiModule : BaseModule public class ApiModule : BaseApiModule
{ {
public ApiModule (IRequestService service, ISettingsService<PlexRequestSettings> settings) : base("api") public ApiModule(IRequestService service) : base("api/v1/")
{ {
Get ["/requests"] = x => GetRequests (); Get["/requests"] = x => GetRequests();
Post["/requests"] = x => CreateRequest();
Put["/requests"] = x => UpdateRequest();
Delete["/requests"] = x => DeleteRequest();
RequestService = service; RequestService = service;
Settings = settings;
} }
private IRequestService RequestService{ get; } private IRequestService RequestService { get; }
private ISettingsService<PlexRequestSettings> Settings{get;}
public Response GetRequests() public Response GetRequests()
{ {
var apiModel = new ApiModel<List<RequestedModel>>{Data = new List<RequestedModel>()}; var apiModel = new ApiModel<List<RequestedModel>> { Data = new List<RequestedModel>() };
if (!Authenticated ()) {
apiModel.Error = true; var requests = RequestService.GetAll();
apiModel.ErrorMessage = "ApiKey is invalid or not present, Please use 'apikey' in the querystring."; apiModel.Data.AddRange(requests);
return ReturnReponse (apiModel);
return ReturnReponse(apiModel);
} }
var requests = RequestService.GetAll ();
apiModel.Data.AddRange (requests);
return ReturnReponse (apiModel); public Response CreateRequest()
{
var request = this.Bind<RequestedModel>();
var valid = this.Validate(request);
if (!valid.IsValid)
{
return ReturnValidationReponse(valid);
} }
private Response ReturnReponse(object result) var apiModel = new ApiModel<bool>();
var result = RequestService.AddRequest(request);
if (result == -1)
{ {
var queryString = (DynamicDictionary)Context.Request.Query; apiModel.Error = true;
dynamic value; apiModel.ErrorMessage = "Could not insert the new request into the database. Internal error.";
if (queryString.TryGetValue("xml", out value)) { return ReturnReponse(apiModel);
if ((bool)value) {
return Response.AsXml (result);
} }
apiModel.Data = true;
return ReturnReponse(apiModel);
} }
return Response.AsJson (result);
public Response UpdateRequest()
{
var request = this.Bind<RequestedModel>();
var valid = this.Validate(request);
if (!valid.IsValid)
{
return ReturnValidationReponse(valid);
} }
private bool Authenticated(){
var query = (DynamicDictionary)Context.Request.Query; var apiModel = new ApiModel<bool>();
dynamic key; var result = RequestService.UpdateRequest(request);
if (!query.TryGetValue ("apikey", out key)) {
return false; if (!result)
{
apiModel.Error = true;
apiModel.ErrorMessage = "Could not update the request into the database. Internal error.";
return ReturnReponse(apiModel);
} }
var settings = Settings.GetSettings (); apiModel.Data = true;
if ((string)key == settings.ApiKey) {
return true; return ReturnReponse(apiModel);
} }
return false;
public Response DeleteRequest()
{
var request = this.Bind<RequestedModel>();
var valid = this.Validate(request);
if (!valid.IsValid)
{
return ReturnValidationReponse(valid);
}
var apiModel = new ApiModel<bool>();
try
{
RequestService.DeleteRequest(request);
apiModel.Data = true;
return ReturnReponse(apiModel);
}
catch (Exception)
{
apiModel.Error = true;
apiModel.ErrorMessage = "Could not delete the request from the database. Internal error.";
return ReturnReponse(apiModel);
}
} }
} }
} }

@ -0,0 +1,112 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: BaseApiModule.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.Collections.Generic;
using System.Linq;
using Nancy;
using Nancy.Validation;
using PlexRequests.Core.SettingModels;
using PlexRequests.Store;
namespace PlexRequests.UI.Modules
{
public abstract class BaseApiModule : BaseModule
{
protected BaseApiModule()
{
Before += (ctx) => CheckAuth();
}
protected BaseApiModule(string modulePath) : base(modulePath)
{
Before += (ctx) => CheckAuth();
}
protected Response ReturnReponse(object result)
{
var queryString = (DynamicDictionary)Context.Request.Query;
dynamic value;
if (queryString.TryGetValue("xml", out value))
{
if ((bool)value)
{
return Response.AsXml(result);
}
}
return Response.AsJson(result);
}
protected Response ReturnValidationReponse(ModelValidationResult result)
{
var errors = result.Errors;
var model = new ApiModel<List<string>>
{
Error = true,
ErrorMessage = "Please view the error messages inside the data node",
Data = new List<string>()
};
foreach (var error in errors)
{
model.Data.AddRange(error.Value.Select(x => x.ErrorMessage));
}
return ReturnReponse(model);
}
private Response CheckAuth()
{
var settings = Settings.GetSettings();
var apiModel = new ApiModel<List<RequestedModel>> { Data = new List<RequestedModel>() };
if (!Authenticated(settings))
{
apiModel.Error = true;
apiModel.ErrorMessage = "ApiKey is invalid or not present, Please use 'apikey' in the querystring.";
return ReturnReponse(apiModel);
}
return null;
}
private bool Authenticated(PlexRequestSettings settings)
{
var query = (DynamicDictionary)Context.Request.Query;
dynamic key;
if (!query.TryGetValue("apikey", out key))
{
return false;
}
if ((string)key == settings.ApiKey)
{
return true;
}
return false;
}
}
}

@ -33,15 +33,13 @@ using Nancy.Extensions;
using PlexRequests.UI.Models; using PlexRequests.UI.Models;
using System; using System;
using Nancy.Security;
using PlexRequests.Core; using PlexRequests.Core;
using PlexRequests.Core.SettingModels; using PlexRequests.Core.SettingModels;
using PlexRequests.Helpers; using PlexRequests.Helpers;
namespace PlexRequests.UI.Modules namespace PlexRequests.UI.Modules
{ {
public class BaseAuthModule : BaseModule public abstract class BaseAuthModule : BaseModule
{ {
private string _username; private string _username;
private int _dateTimeOffset = -1; private int _dateTimeOffset = -1;
@ -77,12 +75,12 @@ namespace PlexRequests.UI.Modules
} }
} }
public BaseAuthModule() protected BaseAuthModule()
{ {
Before += (ctx) => CheckAuth(); Before += (ctx) => CheckAuth();
} }
public BaseAuthModule(string modulePath) : base(modulePath) protected BaseAuthModule(string modulePath) : base(modulePath)
{ {
Before += (ctx) => CheckAuth(); Before += (ctx) => CheckAuth();
} }
@ -99,8 +97,5 @@ namespace PlexRequests.UI.Modules
? Context.GetRedirect(redirectPath) ? Context.GetRedirect(redirectPath)
: null; : null;
} }
} }
} }

@ -32,13 +32,15 @@ using PlexRequests.UI.Helpers;
namespace PlexRequests.UI.Modules namespace PlexRequests.UI.Modules
{ {
public class BaseModule : NancyModule public abstract class BaseModule : NancyModule
{ {
protected ServiceLocator Locator => ServiceLocator.Instance; protected ServiceLocator Locator => ServiceLocator.Instance;
protected ISettingsService<PlexRequestSettings> Settings => Locator.Resolve<ISettingsService<PlexRequestSettings>>();
protected string BaseUrl { get; set; } protected string BaseUrl { get; set; }
public BaseModule()
protected BaseModule()
{ {
var settings = Locator.Resolve<ISettingsService<PlexRequestSettings>>().GetSettings(); var settings = Settings.GetSettings();
var baseUrl = settings.BaseUrl; var baseUrl = settings.BaseUrl;
BaseUrl = baseUrl; BaseUrl = baseUrl;
@ -47,9 +49,9 @@ namespace PlexRequests.UI.Modules
ModulePath = modulePath; ModulePath = modulePath;
} }
public BaseModule(string modulePath) protected BaseModule(string modulePath)
{ {
var settings = Locator.Resolve<ISettingsService<PlexRequestSettings>>().GetSettings(); var settings = Settings.GetSettings();
var baseUrl = settings.BaseUrl; var baseUrl = settings.BaseUrl;
BaseUrl = baseUrl; BaseUrl = baseUrl;

@ -158,9 +158,11 @@
<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="Modules\BaseApiModule.cs" />
<Compile Include="Modules\BaseModule.cs" /> <Compile Include="Modules\BaseModule.cs" />
<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="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" />

@ -61,7 +61,7 @@ namespace PlexRequests.UI
x => x.Port, x => x.Port,
e => -1); e => -1);
var updated = result.MapResult(x => x.Updated, e => false); var updated = result.MapResult(x => x.Updated, e => UpdateValue.None);
//TODO //TODO
PrintToConsole("Starting Up! Please wait, this can usually take a few seconds.", ConsoleColor.Yellow); PrintToConsole("Starting Up! Please wait, this can usually take a few seconds.", ConsoleColor.Yellow);
@ -158,5 +158,17 @@ namespace PlexRequests.UI
Console.WriteLine(message); Console.WriteLine(message);
Console.ForegroundColor = ConsoleColor.Gray; Console.ForegroundColor = ConsoleColor.Gray;
} }
private static void CheckUpdate(UpdateValue val)
{
if (val == UpdateValue.Failed)
{
}
if (val == UpdateValue.Updated)
{
// TODO Change the name of PlexRequests.Updater.exe_Updated and delete the old version
}
}
} }
} }

@ -57,7 +57,7 @@ namespace PlexRequests.UI.Start
/// <c>true</c> if updated; otherwise, <c>false</c>. /// <c>true</c> if updated; otherwise, <c>false</c>.
/// </value> /// </value>
[Option('u', "updated", Required = false, HelpText = "This should only be used by the internal application")] [Option('u', "updated", Required = false, HelpText = "This should only be used by the internal application")]
public bool Updated { get; set; } public UpdateValue Updated { get; set; }
} }
} }

@ -0,0 +1,35 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: UpdateValue.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.Start
{
public enum UpdateValue
{
None,
Updated,
Failed
}
}

@ -56,6 +56,8 @@
<li><a href="@url/admin"><i class="fa fa-cog"></i> Settings</a></li> <li><a href="@url/admin"><i class="fa fa-cog"></i> Settings</a></li>
<li><a href="@url/changepassword"><i class="fa fa-key"></i> Change password</a></li> <li><a href="@url/changepassword"><i class="fa fa-key"></i> Change password</a></li>
<li class="divider"></li> <li class="divider"></li>
<li><a href="@url/logout"><i class="fa fa-heart"></i> Donate!</a></li>
<li class="divider"></li>
<li><a href="@url/logout"><i class="fa fa-sign-out"></i> Logout</a></li> <li><a href="@url/logout"><i class="fa fa-sign-out"></i> Logout</a></li>
</ul> </ul>
</li> </li>
@ -70,14 +72,14 @@
<div id="updateAvailable" hidden="hidden"></div> <div id="updateAvailable" hidden="hidden"></div>
</nav> </nav>
<div class="container"> <div class="container">
@RenderBody() @RenderBody()
</div> </div>
<div class="scroll-top-wrapper "> <div class="scroll-top-wrapper ">
<span class="scroll-top-inner"> <span class="scroll-top-inner">
<i class="fa fa-2x fa-arrow-circle-up"></i> <i class="fa fa-2x fa-arrow-circle-up"></i>
</span> </span>
</div> </div>
</body> </body>
</html> </html>
<script> <script>
@ -93,7 +95,7 @@
success: function (response) { success: function (response) {
if (response.updateAvailable) { if (response.updateAvailable) {
var status = createBaseUrl(urlBase, '/admin/status'); var status = createBaseUrl(urlBase, '/admin/status');
$('#updateAvailable').html("<i class='fa fa-cloud-download' aria-hidden='true'></i> There is a new update available! Click <a style='color: white' href='"+status+"'>Here!</a>"); $('#updateAvailable').html("<i class='fa fa-cloud-download' aria-hidden='true'></i> There is a new update available! Click <a style='color: white' href='" + status + "'>Here!</a>");
$('#updateAvailable').removeAttr("hidden"); $('#updateAvailable').removeAttr("hidden");
} }
}, },

@ -192,7 +192,7 @@ namespace PlexRequests.Updater
private void FinishUpdate() private void FinishUpdate()
{ {
var startInfo = new ProcessStartInfo("PlexRequests.exe") { Arguments = Error ? "-u false" : "-u true" }; var startInfo = new ProcessStartInfo("PlexRequests.exe") { Arguments = Error ? "-u 2" : "-u 1" };
Process.Start(startInfo); Process.Start(startInfo);
Environment.Exit(0); Environment.Exit(0);

Loading…
Cancel
Save