This commit is contained in:
2020-06-12 23:41:42 +00:00
parent 8be276b5b6
commit 7b452670f0
7 changed files with 184 additions and 74 deletions

View File

@@ -38,10 +38,10 @@ namespace rockfishCore.Controllers
#if (DEBUG) #if (DEBUG)
private const string LICENSE_SERVER_URL = "http://localhost:3001/"; private const string LICENSE_SERVER_URL = "http://localhost:3001/";
private const string SUPPORT_EMAIL = "cardjohn@ayanova.com"; public const string SUPPORT_EMAIL = "cardjohn@ayanova.com";
#else #else
private const string LICENSE_SERVER_URL = "https://rockfish.ayanova.com/"; private const string LICENSE_SERVER_URL = "https://rockfish.ayanova.com/";
private const string SUPPORT_EMAIL="support@ayanova.com"; public const string SUPPORT_EMAIL="support@ayanova.com";
#endif #endif
[HttpPost] [HttpPost]
@@ -66,7 +66,7 @@ namespace rockfishCore.Controllers
System.Diagnostics.Debug.WriteLine("RvRController:Post - TODO: Test MustBeOlderThan date code"); System.Diagnostics.Debug.WriteLine("RvRController:Post - TODO: Test MustBeOlderThan date code");
//if there is an active trial for this db then can't do this they must request we re-release it or completely zap the database instead //if there is an active trial for this db then can't do this they must request we re-release it or completely zap the database instead
var MustBeOlderThan = DateUtil.DateToEpoch(DateTime.Now.AddDays(-45)); var MustBeOlderThan = DateUtil.DateToEpoch(DateTime.Now.AddDays((RavenKeyFactory.TRIAL_PERIOD_DAYS * -1)));
if (await ct.TrialRequest.Where(z => z.DbId == r.DbId && z.DtProcessed != null && z.DtProcessed > MustBeOlderThan).AnyAsync()) if (await ct.TrialRequest.Where(z => z.DbId == r.DbId && z.DtProcessed != null && z.DtProcessed > MustBeOlderThan).AnyAsync())
{ {
return BadRequest("E1000 - Can't trial; there is already an active trial license issued for this database Id"); return BadRequest("E1000 - Can't trial; there is already an active trial license issued for this database Id");
@@ -114,7 +114,7 @@ namespace rockfishCore.Controllers
var rfUrl = LICENSE_SERVER_URL + $"default.htm#!/trialEdit/{req.Id}"; var rfUrl = LICENSE_SERVER_URL + $"default.htm#!/trialEdit/{req.Id}";
var body = $"Email address {req.Email} was just verified for {req.ContactName} at {req.CompanyName}.\r\nTrial key is ready to be processed now:\r\n{rfUrl}"; var body = $"Email address {req.Email} was just verified for {req.ContactName} at {req.CompanyName}.\r\nTrial key is ready to be processed now:\r\n{rfUrl}";
//send confirmation email //send confirmation email
RfMail.SendMessage("support@ayanova.com", "support@ayanova.com", "AyaNova trial request requiring action", body, false); RfMail.SendMessage("support@ayanova.com", SUPPORT_EMAIL, "AyaNova trial request requiring action", body, false);
return new ContentResult return new ContentResult
{ {

View File

@@ -5,7 +5,7 @@ using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using rockfishCore.Util;
namespace rockfishCore.Controllers namespace rockfishCore.Controllers
{ {
@@ -65,44 +65,74 @@ namespace rockfishCore.Controllers
return Ok(rec); return Ok(rec);
} }
//Approve
// PUT: api/TrialRequest/5 [HttpPost("approve/{id}")]
[HttpPut("{id}")] public async Task<IActionResult> ApproveTrial([FromRoute] long id)
public async Task<IActionResult> PutTrialRequest([FromRoute] long id, [FromBody] TrialRequest TrialRequest)
{ {
if (!ModelState.IsValid) if (!ModelState.IsValid)
{
return BadRequest(ModelState); return BadRequest(ModelState);
var trial = await ct.TrialRequest.SingleOrDefaultAsync(m => m.Id == id);
if (trial == null)
return NotFound();
//DO APPROVE
//check not already processed and ignore if so
if(trial.DtProcessed!=null){
//already processed, nothing to do here
return BadRequest("Already processed");
} }
if (id != TrialRequest.Id) //generate license key and insert in record
{ trial.Key = RavenKeyFactory.GetRavenTrialKey(trial.DbId, trial.CompanyName);
return BadRequest(); trial.Status = TrialRequest.TrialRequestStatus.Approved;
} trial.DtProcessed = DateUtil.NowAsEpoch();
await ct.SaveChangesAsync();
ct.Entry(TrialRequest).State = EntityState.Modified; //send approved email to user
var body = $"Your trial license request has been approved.\r\nThe license will fetch and install automatically shortly or you can fetch it now in the License form menu.";
//send confirmation email
RfMail.SendMessage("support@ayanova.com", trial.Email, "AyaNova trial request approved", body, false);
try return Ok(trial);
{
await ct.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!await TrialRequestExistsAsync(id))
{
return NotFound();
}
else
{
throw;
}
}
return NoContent();
} }
//Reject
[HttpPost("reject/{id}")]
public async Task<IActionResult> RejectTrial([FromRoute] long id, [FromQuery] string rejectReason)
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
var trial = await ct.TrialRequest.SingleOrDefaultAsync(m => m.Id == id);
if (trial == null)
return NotFound();
//DO REJECT
//check not already processed and ignore if so
if(trial.DtProcessed!=null){
//already processed, nothing to do here
return BadRequest("Already processed");
}
//generate license key and insert in record
trial.RejectReason=rejectReason;
trial.Status = TrialRequest.TrialRequestStatus.Rejected;
trial.DtProcessed = DateUtil.NowAsEpoch();
await ct.SaveChangesAsync();
//send approved email to user
string reason=string.Empty;
if(!string.IsNullOrWhiteSpace(rejectReason)){
reason=$"The request was rejected due to:\r\n{rejectReason}";
}
var body = $"Your trial license request was not approved.\r\n{reason}";
//send confirmation email
RfMail.SendMessage("support@ayanova.com", trial.Email, "AyaNova trial request approved", body, false);
return Ok(trial);
}
private async Task<bool> TrialRequestExistsAsync(long id) private async Task<bool> TrialRequestExistsAsync(long id)

View File

@@ -5,8 +5,6 @@ using System.IO;
using Org.BouncyCastle.Security; using Org.BouncyCastle.Security;
using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.OpenSsl; using Org.BouncyCastle.OpenSsl;
using rockfishCore.Models;
using rockfishCore.Util;
@@ -30,6 +28,8 @@ namespace rockfishCore.Util
//This feature name means it's a trial key //This feature name means it's a trial key
private const string TRIAL_FEATURE_NAME = "TrialMode"; private const string TRIAL_FEATURE_NAME = "TrialMode";
public const int TRIAL_PERIOD_DAYS = 45;
//This feature name means it's a SAAS or rental mode key for month to month hosted service //This feature name means it's a SAAS or rental mode key for month to month hosted service
private const string RENTAL_FEATURE_NAME = "ServiceMode"; private const string RENTAL_FEATURE_NAME = "ServiceMode";
@@ -202,6 +202,38 @@ namespace rockfishCore.Util
#endregion #endregion
public static string GetRavenTrialKey(Guid dbid, string CompanyName)
{
//Build a sample test key, sign it and return it
AyaNovaLicenseKey k = new AyaNovaLicenseKey();
k.LicenseFormat = "2018";
var vv = Math.Truncate((DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1))).TotalSeconds);
string sId = vv.ToString();
if (sId.Contains(","))
sId = sId.Split('.')[0];
k.Id = $"00-{sId}";
k.RegisteredTo = CompanyName;
k.DbId = dbid;
//trial period time limit
k.MaintenanceExpiration = k.LicenseExpiration = DateTime.UtcNow.AddDays(TRIAL_PERIOD_DAYS);
//flag as trial key not regular key
k.Features.Add(new LicenseFeature() { Feature = TRIAL_FEATURE_NAME, Count = 0 });
//add every possible feature
k.Features.Add(new LicenseFeature() { Feature = ACCOUNTING_FEATURE_NAME, Count = 0 });
k.Features.Add(new LicenseFeature() { Feature = SERVICE_TECHS_FEATURE_NAME, Count = 1000 });
return genKey(k);
}
#region RAVEN test code for development #region RAVEN test code for development
//Trial key magic number for development and testing, all other guids will be fully licensed //Trial key magic number for development and testing, all other guids will be fully licensed

View File

@@ -11,7 +11,7 @@
*/ */
/*global $, io, app */ /*global $, io, app */
app.api = (function() { app.api = (function () {
"use strict"; "use strict";
var initModule, var initModule,
getAuthHeaderObject, getAuthHeaderObject,
@@ -32,7 +32,7 @@ app.api = (function() {
////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////
// NOT AUTHORIZED ERROR HANDLER // NOT AUTHORIZED ERROR HANDLER
$(document).ajaxError(function(event, jqxhr, settings, thrownError) { $(document).ajaxError(function (event, jqxhr, settings, thrownError) {
//unauthorized? Trigger logout which will trigger login after clearing creds //unauthorized? Trigger logout which will trigger login after clearing creds
if (jqxhr.status == 401) { if (jqxhr.status == 401) {
window.location.replace("#!/logout"); window.location.replace("#!/logout");
@@ -46,7 +46,7 @@ app.api = (function() {
// Return the auth token header // Return the auth token header
// //
// //
getAuthHeaderObject = function() { getAuthHeaderObject = function () {
return { return {
Authorization: "Bearer " + app.shell.stateMap.user.token Authorization: "Bearer " + app.shell.stateMap.user.token
}; };
@@ -59,7 +59,7 @@ app.api = (function() {
//Create //Create
//Route app.post('/api/:obj_type/create', function (req, res) { //Route app.post('/api/:obj_type/create', function (req, res) {
// //
create = function(apiRoute, objData, callback) { create = function (apiRoute, objData, callback) {
$.ajax({ $.ajax({
method: "post", method: "post",
dataType: "json", dataType: "json",
@@ -67,10 +67,10 @@ app.api = (function() {
headers: getAuthHeaderObject(), headers: getAuthHeaderObject(),
contentType: "application/json; charset=utf-8", contentType: "application/json; charset=utf-8",
data: JSON.stringify(objData), data: JSON.stringify(objData),
success: function(data, textStatus) { success: function (data, textStatus) {
callback(data); callback(data);
}, },
error: function(jqXHR, textStatus, errorThrown) { error: function (jqXHR, textStatus, errorThrown) {
callback({ callback({
error: 1, error: 1,
msg: textStatus + "\n" + errorThrown, msg: textStatus + "\n" + errorThrown,
@@ -83,17 +83,17 @@ app.api = (function() {
///////////////// /////////////////
//Get - get anything, the caller provides the route, this should replace most legacy get //Get - get anything, the caller provides the route, this should replace most legacy get
// //
get = function(apiRoute, callback) { get = function (apiRoute, callback) {
$.ajax({ $.ajax({
method: "GET", method: "GET",
dataType: "json", dataType: "json",
url: app.shell.stateMap.apiUrl + apiRoute, url: app.shell.stateMap.apiUrl + apiRoute,
headers: getAuthHeaderObject(), headers: getAuthHeaderObject(),
success: function(data, textStatus) { success: function (data, textStatus) {
callback(data); callback(data);
}, },
error: function(jqXHR, textStatus, errorThrown) { error: function (jqXHR, textStatus, errorThrown) {
callback({ callback({
error: 1, error: 1,
msg: textStatus + "\n" + errorThrown, msg: textStatus + "\n" + errorThrown,
@@ -108,7 +108,7 @@ app.api = (function() {
//Update //Update
//route: app.post('/api/:obj_type/update/:id', function (req, res) { //route: app.post('/api/:obj_type/update/:id', function (req, res) {
// //
update = function(objType, objData, callback) { update = function (objType, objData, callback) {
var theId; var theId;
if (!objData.id) { if (!objData.id) {
return callback({ return callback({
@@ -125,14 +125,14 @@ app.api = (function() {
headers: getAuthHeaderObject(), headers: getAuthHeaderObject(),
contentType: "application/json; charset=utf-8", contentType: "application/json; charset=utf-8",
data: JSON.stringify(objData), data: JSON.stringify(objData),
success: function(data, textStatus) { success: function (data, textStatus) {
if (data == null) { if (data == null) {
data = { ok: 1 }; data = { ok: 1 };
} }
callback(data); callback(data);
}, },
error: function(jqXHR, textStatus, errorThrown) { error: function (jqXHR, textStatus, errorThrown) {
callback({ callback({
error: 1, error: 1,
msg: textStatus + "\n" + errorThrown, msg: textStatus + "\n" + errorThrown,
@@ -144,16 +144,16 @@ app.api = (function() {
/////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////
//remove Item //remove Item
remove = function(apiRoute, callback) { remove = function (apiRoute, callback) {
$.ajax({ $.ajax({
method: "DELETE", method: "DELETE",
dataType: "json", dataType: "json",
url: app.shell.stateMap.apiUrl + apiRoute, url: app.shell.stateMap.apiUrl + apiRoute,
headers: getAuthHeaderObject(), headers: getAuthHeaderObject(),
success: function(data, textStatus) { success: function (data, textStatus) {
callback(data); callback(data);
}, },
error: function(jqXHR, textStatus, errorThrown) { error: function (jqXHR, textStatus, errorThrown) {
callback({ callback({
error: 1, error: 1,
msg: textStatus + "\n" + errorThrown, msg: textStatus + "\n" + errorThrown,
@@ -167,7 +167,7 @@ app.api = (function() {
// uploadFile // uploadFile
// (ajax route to upload a file) // (ajax route to upload a file)
// //
uploadFile = function(apiRoute, objData, callback) { uploadFile = function (apiRoute, objData, callback) {
$.ajax({ $.ajax({
method: "post", method: "post",
dataType: "json", dataType: "json",
@@ -176,10 +176,10 @@ app.api = (function() {
contentType: false, contentType: false,
processData: false, processData: false,
data: objData, data: objData,
success: function(data, textStatus) { success: function (data, textStatus) {
callback(data); callback(data);
}, },
error: function(jqXHR, textStatus, errorThrown) { error: function (jqXHR, textStatus, errorThrown) {
callback({ callback({
error: 1, error: 1,
msg: textStatus + "\n" + errorThrown, msg: textStatus + "\n" + errorThrown,
@@ -192,7 +192,7 @@ app.api = (function() {
////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////
//putAction - ad-hoc put method used to trigger actions etc //putAction - ad-hoc put method used to trigger actions etc
// //
putAction = function(apiRoute, callback) { putAction = function (apiRoute, callback) {
$.ajax({ $.ajax({
method: "put", method: "put",
dataType: "json", dataType: "json",
@@ -200,14 +200,14 @@ app.api = (function() {
headers: getAuthHeaderObject(), headers: getAuthHeaderObject(),
contentType: "application/json; charset=utf-8", contentType: "application/json; charset=utf-8",
//data: JSON.stringify(objData), //data: JSON.stringify(objData),
success: function(data, textStatus) { success: function (data, textStatus) {
if (data == null) { if (data == null) {
data = { ok: 1 }; data = { ok: 1 };
} }
callback(data); callback(data);
}, },
error: function(jqXHR, textStatus, errorThrown) { error: function (jqXHR, textStatus, errorThrown) {
callback({ callback({
error: 1, error: 1,
msg: textStatus + "\n" + errorThrown, msg: textStatus + "\n" + errorThrown,
@@ -216,6 +216,34 @@ app.api = (function() {
} }
}); });
}; };
//////////////////////////////////////////////////////////////
//postAction - ad-hoc post method used to trigger actions etc
// (becuase it shouldn't have been put in the first place above)
postAction = function (apiRoute, callback) {
$.ajax({
method: "post",
dataType: "json",
url: app.shell.stateMap.apiUrl + apiRoute,
headers: getAuthHeaderObject(),
contentType: "application/json; charset=utf-8",
success: function (data, textStatus) {
if (data == null) {
data = { ok: 1 };
}
callback(data);
},
error: function (jqXHR, textStatus, errorThrown) {
callback({
error: 1,
msg: textStatus + "\n" + errorThrown,
error_detail: {}
});
}
});
};
////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////
// LICENSE KEY RELATED API METHODS // LICENSE KEY RELATED API METHODS
@@ -223,7 +251,7 @@ app.api = (function() {
//CreateLicense //CreateLicense
//Route app.post('/api/license/create', function (req, res) { //Route app.post('/api/license/create', function (req, res) {
// //
createLicense = function(objData, callback) { createLicense = function (objData, callback) {
$.ajax({ $.ajax({
method: "post", method: "post",
dataType: "text", dataType: "text",
@@ -231,10 +259,10 @@ app.api = (function() {
headers: getAuthHeaderObject(), headers: getAuthHeaderObject(),
contentType: "application/json; charset=utf-8", contentType: "application/json; charset=utf-8",
data: JSON.stringify(objData), data: JSON.stringify(objData),
success: function(data, textStatus) { success: function (data, textStatus) {
callback(data); callback(data);
}, },
error: function(jqXHR, textStatus, errorThrown) { error: function (jqXHR, textStatus, errorThrown) {
callback({ callback({
error: 1, error: 1,
msg: textStatus + "\n" + errorThrown, msg: textStatus + "\n" + errorThrown,
@@ -249,16 +277,16 @@ app.api = (function() {
//Fetch license requests //Fetch license requests
//route: app.get('/api/license/requests', function (req, res) { //route: app.get('/api/license/requests', function (req, res) {
// //
getLicenseRequests = function(callback) { getLicenseRequests = function (callback) {
$.ajax({ $.ajax({
method: "GET", method: "GET",
dataType: "json", dataType: "json",
url: app.shell.stateMap.apiUrl + "license/requests", url: app.shell.stateMap.apiUrl + "license/requests",
headers: getAuthHeaderObject(), headers: getAuthHeaderObject(),
success: function(data, textStatus) { success: function (data, textStatus) {
callback(data); callback(data);
}, },
error: function(jqXHR, textStatus, errorThrown) { error: function (jqXHR, textStatus, errorThrown) {
callback({ callback({
error: 1, error: 1,
msg: textStatus + "\n" + errorThrown, msg: textStatus + "\n" + errorThrown,
@@ -273,16 +301,16 @@ app.api = (function() {
//Fetch generated response to license request //Fetch generated response to license request
//route: app.get('/api/license/generateFromRequest/:uid', function (req, res) { //route: app.get('/api/license/generateFromRequest/:uid', function (req, res) {
// //
generateFromRequest = function(uid, callback) { generateFromRequest = function (uid, callback) {
$.ajax({ $.ajax({
method: "GET", method: "GET",
dataType: "json", dataType: "json",
url: app.shell.stateMap.apiUrl + "license/generateFromRequest/" + uid, url: app.shell.stateMap.apiUrl + "license/generateFromRequest/" + uid,
headers: getAuthHeaderObject(), headers: getAuthHeaderObject(),
success: function(data, textStatus) { success: function (data, textStatus) {
callback(data); callback(data);
}, },
error: function(jqXHR, textStatus, errorThrown) { error: function (jqXHR, textStatus, errorThrown) {
callback({ callback({
error: 1, error: 1,
msg: textStatus + "\n" + errorThrown, msg: textStatus + "\n" + errorThrown,
@@ -296,7 +324,7 @@ app.api = (function() {
//Email license request response //Email license request response
//app.post('/api/license/email_response', function (req, res) { //app.post('/api/license/email_response', function (req, res) {
// //
licenseEmailResponse = function(objData, callback) { licenseEmailResponse = function (objData, callback) {
$.ajax({ $.ajax({
method: "post", method: "post",
dataType: "text", dataType: "text",
@@ -304,10 +332,10 @@ app.api = (function() {
headers: getAuthHeaderObject(), headers: getAuthHeaderObject(),
contentType: "application/json; charset=utf-8", contentType: "application/json; charset=utf-8",
data: JSON.stringify(objData), data: JSON.stringify(objData),
success: function(data, textStatus) { success: function (data, textStatus) {
callback(data); callback(data);
}, },
error: function(jqXHR, textStatus, errorThrown) { error: function (jqXHR, textStatus, errorThrown) {
callback({ callback({
error: 1, error: 1,
msg: textStatus + "\n" + errorThrown, msg: textStatus + "\n" + errorThrown,
@@ -317,7 +345,7 @@ app.api = (function() {
}); });
}; };
initModule = function() {}; initModule = function () {};
return { return {
initModule: initModule, initModule: initModule,
@@ -329,6 +357,7 @@ app.api = (function() {
update: update, update: update,
uploadFile: uploadFile, uploadFile: uploadFile,
putAction: putAction, putAction: putAction,
postAction: postAction,
createLicense: createLicense, createLicense: createLicense,
getLicenseRequests: getLicenseRequests, getLicenseRequests: getLicenseRequests,
generateFromRequest: generateFromRequest, generateFromRequest: generateFromRequest,

View File

@@ -12,16 +12,34 @@ app.trialEdit = (function () {
//---------------- BEGIN MODULE SCOPE VARIABLES -------------- //---------------- BEGIN MODULE SCOPE VARIABLES --------------
var var
stateMap = {}, stateMap = {},
onSave, onDelete, onApprove, onReject, onDelete,
configModule, initModule; configModule, initModule;
//----------------- END MODULE SCOPE VARIABLES --------------- //----------------- END MODULE SCOPE VARIABLES ---------------
//------------------- BEGIN EVENT HANDLERS ------------------- //------------------- BEGIN EVENT HANDLERS -------------------
//ONSAVE //ONAPPROVE
// //
onSave = function (event) { onApprove = function (event) {
event.preventDefault();
$.gevent.publish('app-clear-error');
var isFetched=$('#fetched').prop('checked')
app.api.postAction('trial/fetched/' + stateMap.id + "/" + isFetched, function (res) {
if (res.error) {
$.gevent.publish('app-show-error', res.msg);
}
});
return false; //prevent default?
};
//ONREJECT
//
onReject = function (event) {
event.preventDefault(); event.preventDefault();
$.gevent.publish('app-clear-error'); $.gevent.publish('app-clear-error');
@@ -37,6 +55,7 @@ app.trialEdit = (function () {
return false; //prevent default? return false; //prevent default?
}; };
//ONDELETE //ONDELETE
// //
onDelete = function (event) { onDelete = function (event) {

View File

@@ -48,7 +48,7 @@
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="rejectReason">Reject reason</label> <label for="rejectReason">Reject reason *emailed to user</label>
<input class="form-control" type="text" id="rejectReason" name="rejectReason" value="" > <input class="form-control" type="text" id="rejectReason" name="rejectReason" value="" >
</div> </div>

File diff suppressed because one or more lines are too long