This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user