This commit is contained in:
2020-04-21 18:32:31 +00:00
parent fd56b763be
commit 8c19e087e2
3 changed files with 76 additions and 54 deletions

View File

@@ -10,6 +10,10 @@ todo: check attachment NOTES property is actually supported
//todo: search tables in schema, I think there is a missing index here, need to look at the search query section again as it was changed several times from the original schema creation //todo: search tables in schema, I think there is a missing index here, need to look at the search query section again as it was changed several times from the original schema creation
todo: can a user be locked out from the server end even though they posess a valid token?
- and prevent download of images etc?
todo: api / server landing page is shitty on a mobile todo: api / server landing page is shitty on a mobile
todo: add query fail logging to datalist just like done with picklist so in production can catch mysterious problems more easily todo: add query fail logging to datalist just like done with picklist so in production can catch mysterious problems more easily

View File

@@ -60,55 +60,54 @@ namespace AyaNova.Api.Controllers
//Moved this functionality to authentication and expiry follows jwt token expiry
// //LOOKAT: Centralize this code somewhere else, it's going to be needed for backup as well
// //consider the 1 hour thing, is this legit depending on client?
// /// <summary>
// /// Get download token
// /// A download token is good for 1 hour from issue
// /// </summary>
// /// <returns>Current download token for user</returns>
// [HttpGet("DownloadToken")]
// public async Task<IActionResult> GetDownloadTokenAsync()
// {
// if (!serverState.IsOpen)
// return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
//LOOKAT: Centralize this code somewhere else, it's going to be needed for backup as well // long lUserId = UserIdFromContext.Id(HttpContext.Items);
//consider the 1 hour thing, is this legit depending on client? // var u = await ct.User.FirstOrDefaultAsync(a => a.Id == lUserId);
// if (u == null)
// return NotFound();
// else
// {
/// <summary> // //Generate a download token and store it with the user account
/// Get download token // //users who are authenticated can get their token via download route
/// A download token is good for 1 hour from issue // Guid g = Guid.NewGuid();
/// </summary> // string dlkey = Convert.ToBase64String(g.ToByteArray());
/// <returns>Current download token for user</returns> // dlkey = dlkey.Replace("=", "");
[HttpGet("DownloadToken")] // dlkey = dlkey.Replace("+", "");
public async Task<IActionResult> GetDownloadTokenAsync()
{
if (!serverState.IsOpen)
return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
long lUserId = UserIdFromContext.Id(HttpContext.Items); // //get expiry date for download token
var u = await ct.User.FirstOrDefaultAsync(a => a.Id == lUserId); // var exp = new DateTimeOffset(DateTime.Now.AddHours(1).ToUniversalTime(), TimeSpan.Zero);
if (u == null)
return NotFound();
else
{
//Generate a download token and store it with the user account // u.DlKey = dlkey;
//users who are authenticated can get their token via download route // u.DlKeyExpire = exp.DateTime;
Guid g = Guid.NewGuid(); // ct.User.Update(u);
string dlkey = Convert.ToBase64String(g.ToByteArray()); // try
dlkey = dlkey.Replace("=", ""); // {
dlkey = dlkey.Replace("+", ""); // await ct.SaveChangesAsync();//triggering concurrency exception here
// }
// catch (Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException)
// {
// log.LogInformation("Auth retry dlkey");
// };
//get expiry date for download token // return Ok(ApiOkResponse.Response(new { dlkey = u.DlKey, expires = u.DlKeyExpire }, true));
var exp = new DateTimeOffset(DateTime.Now.AddHours(1).ToUniversalTime(), TimeSpan.Zero); // }
u.DlKey = dlkey; // }
u.DlKeyExpire = exp.DateTime;
ct.User.Update(u);
try
{
await ct.SaveChangesAsync();//triggering concurrency exception here
}
catch (Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException)
{
log.LogInformation("Auth retry dlkey");
};
return Ok(ApiOkResponse.Response(new { dlkey = u.DlKey, expires = u.DlKeyExpire }, true));
}
}
@@ -336,21 +335,25 @@ namespace AyaNova.Api.Controllers
var dlkeyUser = await ct.User.SingleOrDefaultAsync(m => m.DlKey == dlkey); var dlkeyUser = await ct.User.SingleOrDefaultAsync(m => m.DlKey == dlkey);
if (dlkeyUser == null) if (dlkeyUser == null)
{ {
return BadRequest(new ApiErrorResponse(ApiErrorCode.NOT_AUTHORIZED, "dlkey", "Download token not valid")); //don't want to leak information so just say not found
//return BadRequest(new ApiErrorResponse(ApiErrorCode.NOT_AUTHORIZED, "dlkey", "Download token not valid"));
return StatusCode(401, new ApiErrorResponse(ApiErrorCode.AUTHENTICATION_FAILED));
} }
//Make sure the token provided is for the current user //Make sure the token provided is for the current user
long UserId = UserIdFromContext.Id(HttpContext.Items); long UserId = UserIdFromContext.Id(HttpContext.Items);
if (UserId != dlkeyUser.Id) if (UserId != dlkeyUser.Id)
{ {
return BadRequest(new ApiErrorResponse(ApiErrorCode.NOT_AUTHORIZED, "dlkey", "Download token not valid")); // return BadRequest(new ApiErrorResponse(ApiErrorCode.NOT_AUTHORIZED, "dlkey", "Download token not valid"));
return StatusCode(401, new ApiErrorResponse(ApiErrorCode.AUTHENTICATION_FAILED));
} }
var utcNow = new DateTimeOffset(DateTime.Now.ToUniversalTime(), TimeSpan.Zero); var utcNow = new DateTimeOffset(DateTime.Now.ToUniversalTime(), TimeSpan.Zero);
if (dlkeyUser.DlKeyExpire < utcNow.DateTime) if (dlkeyUser.DlKeyExpire < utcNow.DateTime)
{ {
return BadRequest(new ApiErrorResponse(ApiErrorCode.NOT_AUTHORIZED, "dlkey", "Download token has expired")); // return BadRequest(new ApiErrorResponse(ApiErrorCode.NOT_AUTHORIZED, "dlkey", "Download token has expired"));
return StatusCode(401, new ApiErrorResponse(ApiErrorCode.AUTHENTICATION_FAILED));
} }
//Ok, user has a valid download key and it's not expired yet so get the attachment record //Ok, user has a valid download key and it's not expired yet so get the attachment record

View File

@@ -158,7 +158,7 @@ namespace AyaNova.Api.Controllers
//Multiple users are allowed the same password and login //Multiple users are allowed the same password and login
//Salt will differentiate them so get all users that match login, then try to match pw //Salt will differentiate them so get all users that match login, then try to match pw
var users = await ct.User.AsNoTracking().Where(m => m.Login == creds.Login).ToListAsync(); var users = await ct.User.Where(m => m.Login == creds.Login && m.Active == true).ToListAsync();
foreach (User u in users) foreach (User u in users)
{ {
@@ -177,13 +177,15 @@ namespace AyaNova.Api.Controllers
} }
//If the user is inactive they may not login // //If the user is inactive they may not login
if (!u.Active) // if (!u.Active)
{ // {
//This is leaking information, instead just act like bad creds // //This is leaking information, instead just act like bad creds
//return StatusCode(401, new ApiErrorResponse(ApiErrorCode.NOT_AUTHORIZED, null, "User deactivated")); // //return StatusCode(401, new ApiErrorResponse(ApiErrorCode.NOT_AUTHORIZED, null, "User deactivated"));
return StatusCode(401, new ApiErrorResponse(ApiErrorCode.AUTHENTICATION_FAILED)); // return StatusCode(401, new ApiErrorResponse(ApiErrorCode.AUTHENTICATION_FAILED));
} // }
//build the key (JWT set in startup.cs) //build the key (JWT set in startup.cs)
byte[] secretKey = System.Text.Encoding.ASCII.GetBytes(ServerBootConfig.AYANOVA_JWT_SECRET); byte[] secretKey = System.Text.Encoding.ASCII.GetBytes(ServerBootConfig.AYANOVA_JWT_SECRET);
@@ -192,6 +194,18 @@ namespace AyaNova.Api.Controllers
var iat = new DateTimeOffset(DateTime.Now.ToUniversalTime(), TimeSpan.Zero);//timespan zero means zero time off utc / specifying this is a UTC datetime var iat = new DateTimeOffset(DateTime.Now.ToUniversalTime(), TimeSpan.Zero);//timespan zero means zero time off utc / specifying this is a UTC datetime
var exp = new DateTimeOffset(DateTime.Now.AddDays(JWT_LIFETIME_DAYS).ToUniversalTime(), TimeSpan.Zero); var exp = new DateTimeOffset(DateTime.Now.AddDays(JWT_LIFETIME_DAYS).ToUniversalTime(), TimeSpan.Zero);
//=============== download token ===================
//Generate a download token and store it with the user account
//string DownloadToken = Convert.ToBase64String(Guid.NewGuid().ToByteArray());
string DownloadToken = Hasher.GenerateSalt();
DownloadToken = DownloadToken.Replace("=", "");
DownloadToken = DownloadToken.Replace("+", "");
u.DlKey = DownloadToken;
u.DlKeyExpire = exp.DateTime;
await ct.SaveChangesAsync();
//=======================================================
var payload = new Dictionary<string, object>() var payload = new Dictionary<string, object>()
{ {
{ "iat", iat.ToUnixTimeSeconds().ToString() }, { "iat", iat.ToUnixTimeSeconds().ToString() },
@@ -200,7 +214,8 @@ namespace AyaNova.Api.Controllers
{ "id", u.Id.ToString() }, { "id", u.Id.ToString() },
{ "name", u.Name}, { "name", u.Name},
{ "usertype", u.UserType}, { "usertype", u.UserType},
{ "ayanova/roles", ((int)u.Roles).ToString() } { "ayanova/roles", ((int)u.Roles).ToString()},
{ "dlt", DownloadToken }
}; };