using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Logging;
using AyaNova.Models;
using AyaNova.Api.ControllerHelpers;
using AyaNova.Biz;
using System.ComponentModel.DataAnnotations;
namespace AyaNova.Api.Controllers
{
///
/// License route
///
[ApiController]
[Asp.Versioning.ApiVersion("8.0")]
[Route("api/v{version:apiVersion}/license")]
[Produces("application/json")]
[Authorize]
public class LicenseController : ControllerBase
{
private readonly AyContext ct;
private readonly ILogger log;
private readonly ApiServerState serverState;
///
/// ctor
///
///
///
///
public LicenseController(AyContext dbcontext, ILogger logger, ApiServerState apiServerState)
{
ct = dbcontext;
log = logger;
serverState = apiServerState;
}
///
/// Get License info
///
/// Information about the currently installed license in AyaNova
[HttpGet()]
public ActionResult GetLicenseInfo()
{
if (serverState.IsClosed)
{
//Exception for SuperUser account to handle licensing issues
if (UserIdFromContext.Id(HttpContext.Items) != 1)
return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
}
if (!Authorized.HasReadFullRole(HttpContext.Items, AyaType.License))
{
return StatusCode(403, new ApiNotAuthorizedResponse());
}
var ret = AyaNova.Core.License.LicenseInfoAsJson;
return Ok(ApiOkResponse.Response(ret));
}
///
/// Get DB Emptiness
///
/// Checks if there is any data in critical tables only
[HttpGet("database-empty")]
public async Task GetDbEmpty()
{
if (serverState.IsClosed)
{
//Exception for SuperUser account to handle licensing issues
if (UserIdFromContext.Id(HttpContext.Items) != 1)
return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
}
if (!Authorized.HasReadFullRole(HttpContext.Items, AyaType.License))
{
return StatusCode(403, new ApiNotAuthorizedResponse());
}
return Ok(ApiOkResponse.Response(await AyaNova.Util.DbUtil.DBIsEmptyAsync(ct, log)));
}
///
/// Fetch license
///
/// Posting to this route causes AyaNova to attempt to refresh it's license
/// from the AyaNova license server
///
/// On success returns information about the currently installed license in AyaNova
[HttpPost]
public async Task FetchLicense()
{
if (serverState.IsClosed)
{
//Exception for SuperUser account to handle licensing issues
if (UserIdFromContext.Id(HttpContext.Items) != 1)
return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
}
if (!Authorized.HasCreateRole(HttpContext.Items, AyaType.License))
{
return StatusCode(403, new ApiNotAuthorizedResponse());
}
if (!ModelState.IsValid)
{
return BadRequest(new ApiErrorResponse(ModelState));
}
try
{
var ret = await AyaNova.Core.License.FetchKeyAsync(serverState, ct, log, false);
//most often the result will be not found but in future might be other results
//main thing is to not log anything but OK response
if (ret == "ok")
{
//Eventlog
await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserIdFromContext.Id(HttpContext.Items), 0, AyaType.License, AyaEvent.LicenseFetch), ct);
}
else
{
if (ret != "notfound")
log.LogError($"LicenseController::FetchLicense - failed: {ret}");
}
//any response here is OK, there's not necessarily an error, just no key or some mundane problem not technical but biz so let
//ret string instruct client on what to do
return Ok(ret);
}
catch (Exception ex)
{
Exception rootex = ex;
while (rootex.InnerException != null)
{
rootex = rootex.InnerException;
}
if (rootex.Message.Contains("E1020"))
{
return BadRequest(new ApiErrorResponse(ApiErrorCode.INVALID_OPERATION, "LICENSE_KEY", rootex.Message));
}
else
{
throw;
}
}
}
///
/// Request trial license
///
/// Posting to this route causes AyaNova to request a trial license key from the AyaNova license server
/// Database must be empty and unlicensed or trial license
///
///
/// HTTP 204 No Content result code on success or fail code with explanation
[HttpPost("trialRequest")]
public async Task RequestTrial([FromBody] RequestTrial trialRequest)
{
if (serverState.IsClosed)
{
//Exception for SuperUser account to handle licensing issues
if (UserIdFromContext.Id(HttpContext.Items) != 1)
return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
}
if (!Authorized.HasCreateRole(HttpContext.Items, AyaType.License))
{
return StatusCode(403, new ApiNotAuthorizedResponse());
}
if (!ModelState.IsValid)
{
return BadRequest(new ApiErrorResponse(ModelState));
}
if (!await AyaNova.Util.DbUtil.DBIsEmptyAsync(ct, log))
{
return BadRequest(new ApiErrorResponse(ApiErrorCode.INVALID_OPERATION, null, "Only an empty AyaNova database can request a trial key. Erase the database to proceed with a new trial."));
}
if (!AyaNova.Core.License.ActiveKey.IsEmpty && !AyaNova.Core.License.ActiveKey.TrialLicense)
{
return BadRequest(new ApiErrorResponse(ApiErrorCode.INVALID_OPERATION, null, "There is an active registered license. Only an unlicensed or trial license database can request a trial key."));
}
try
{
//Send the request to RockFish here (or at least start the job to do it in which case return Accepted instead of no content and update comment above)
var ret = await Core.License.RequestTrialAsync(trialRequest, log);
if (ret == "ok")
{
//Log
await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserIdFromContext.Id(HttpContext.Items), 0, AyaType.License, AyaEvent.LicenseTrialRequest), ct);
}
return Ok(ret);
}
catch (Exception ex)
{
Exception rootex = ex;
while (rootex.InnerException != null)
{
rootex = rootex.InnerException;
}
if (rootex.Message.Contains("E1020"))
{
return BadRequest(new ApiErrorResponse(ApiErrorCode.INVALID_OPERATION, "LICENSE_KEY", rootex.Message));
}
else
{
throw;
}
}
}
///
/// Permanently erase most data and all attachments for seeding or migration purposes
///
/// This route is used by the v8-migration utility and by the AyaNova UI when seeding a trial database
///
/// Items retained are documented here with the exception of Tax codes:
/// https://ayanova.com/docs/adm-license/#erase-database
/// However not Tax Codes as documented because seeding or migration will create tax codes
///
/// (Only *the* SuperUser account can use this route)
///
/// Must be "I understand"
/// HTTP 204 No Content result code on success or fail code with explanation
[HttpPost("permanently-erase-all-data")]
public async Task RemoveAllData([FromBody] string acceptCode)
{
if (serverState.IsClosed)
{
//Exception for SuperUser account to handle licensing issues
if (UserIdFromContext.Id(HttpContext.Items) != 1)
return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
}
if (!ModelState.IsValid)
return BadRequest(new ApiErrorResponse(ModelState));
long UserId = UserIdFromContext.Id(HttpContext.Items);
//SuperUser only and must have accept code
if (UserId != 1 || string.IsNullOrWhiteSpace(acceptCode) || acceptCode.ToLowerInvariant() != "i understand")
return StatusCode(403, new ApiNotAuthorizedResponse());
//empty the db
await AyaNova.Util.DbUtil.EmptyBizDataFromDatabaseForSeedingOrImportingAsync(log);
//Log
await EventLogProcessor.LogEventToDatabaseAsync(new Event(1, 0, AyaType.Global, AyaEvent.EraseAllData), ct);
return NoContent();
}
///
/// Permanently erase most data and all attachments
///
/// This route is used by the by the AyaNova UI when a user selects to manually erase the database
/// or when they are evaluating and request another trial period with a database that already has a trial license in it
///
/// Items retained are documented here:
/// https://ayanova.com/docs/adm-license/#erase-database
///
///
/// (Only *the* SuperUser account can use this route)
///
/// Must be "I understand"
/// HTTP 204 No Content result code on success or fail code with explanation
[HttpPost("permanently-erase-all-data-keep-tax-codes")]
public async Task RemoveAllDataKeepTaxCodes([FromBody] string acceptCode)
{
if (serverState.IsClosed)
{
//Exception for SuperUser account to handle licensing issues
if (UserIdFromContext.Id(HttpContext.Items) != 1)
return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
}
if (!ModelState.IsValid)
return BadRequest(new ApiErrorResponse(ModelState));
long UserId = UserIdFromContext.Id(HttpContext.Items);
//SuperUser only and must have accept code
if (UserId != 1 || string.IsNullOrWhiteSpace(acceptCode) || acceptCode.ToLowerInvariant() != "i understand")
return StatusCode(403, new ApiNotAuthorizedResponse());
//empty the db
await AyaNova.Util.DbUtil.EmptyBizDataFromDatabaseForSeedingOrImportingAsync(log, true);
//Log
await EventLogProcessor.LogEventToDatabaseAsync(new Event(1, 0, AyaType.Global, AyaEvent.EraseAllData), ct);
return NoContent();
}
///
///
///
///
/// HTTP 204 No Content result code on success or fail code with explanation
[AllowAnonymous]
[HttpPost("lc")]
[ApiExplorerSettings(IgnoreApi = true)]
public async Task lc([FromBody] string acceptCode)
{
//END USER LICENSE AGREEMENT ROUTE ONLY CALLED FROM WEBAPP AND HIDDEN FROM VIEW AS A ROUTE
//SuperUser only and must have accept code
if (string.IsNullOrWhiteSpace(acceptCode) || acceptCode.ToLowerInvariant() != "iaccepttheagreement")
return StatusCode(403, new ApiNotAuthorizedResponse());
await Core.License.FlagEULA(ct, log);
//Log
await EventLogProcessor.LogEventToDatabaseAsync(new Event(1, 0, AyaType.Global, AyaEvent.Modified, "End user license agreement consent obtained"), ct);
return NoContent();
}
//------------------------------------------------------
}//eoc
}//eons