This commit is contained in:
2020-11-18 23:19:13 +00:00
parent 6bcc0ad8a1
commit f21e66de08
5 changed files with 123 additions and 2 deletions

View File

@@ -376,6 +376,31 @@ namespace AyaNova.Api.Controllers
return Ok(ApiOkResponse.Response(u.UserType != UserType.Customer && u.UserType != UserType.HeadOffice));
}
/// <summary>
/// Generate new random credentials for User
/// and email them to the user
///
/// </summary>
/// <param name="id">User id</param>
/// <param name="apiVersion">From route path</param>
/// <returns>NoContent</returns>
[HttpPost("generate-creds-email/{id}")]
public async Task<IActionResult> GenerateCredsAndEmailUser([FromRoute] long id, ApiVersion apiVersion)
{
if (!serverState.IsOpen)
return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
UserBiz biz = UserBiz.GetBiz(ct, HttpContext);
if (!Authorized.HasModifyRole(HttpContext.Items, biz.BizType))
return StatusCode(403, new ApiNotAuthorizedResponse());
if (!ModelState.IsValid)
return BadRequest(new ApiErrorResponse(ModelState));
bool successfulOperation=await biz.GenerateCredsAndEmailUser(id);
if (successfulOperation == false)
return BadRequest(new ApiErrorResponse(biz.Errors));
else
return NoContent();
}
//------------
}//eoc

View File

@@ -125,6 +125,8 @@ namespace AyaNova.Biz
}//eom
//This is told about an event and then determines if there are any subscriptions related to that event and proceses them accordingly
//todo: this should take some kind of general event type like the AyaEvent types (i.e. which CRUD operation is in effect if relevant)
//and also a biz object before and after or just before if not a change and also a AyaType

View File

@@ -41,7 +41,8 @@ namespace AyaNova.Biz
QuoteStatusAge = 29,//* Quote object Created / Updated, conditional on exact status selected IdValue, Tags conditional, advance notice can be set
WorkorderFinished = 30, //*Service work order is set to any status that is flagged as a "Finished" type of status. Customer & User
WorkorderCreatedForCustomer = 31, //*Service work order is created for Customer, only applies to that customer user notify sub for that customer, customer id is in conditional ID value for subscription
WorkorderFinishedFollowUp = 32 //* Service workorder closed status follow up again after this many TIMESPAN
WorkorderFinishedFollowUp = 32, //* Service workorder closed status follow up again after this many TIMESPAN
SendUserCredentials = 33 // Internal System use only: When user generates new credentials and sends them this is the notification type for that see UserBiz GenerateCredsAndEmailUser
//NEW ITEMS REQUIRE translation KEYS

View File

@@ -310,6 +310,64 @@ namespace AyaNova.Biz
}
/////////////////////////////////////////////
// GENERATE AND EMAIL CREDS
//
internal async Task<bool> GenerateCredsAndEmailUser(long userId)
{
User dbObject = await ct.User.Include(o => o.UserOptions).FirstOrDefaultAsync(z => z.Id == userId);
if (dbObject == null)
{
AddError(ApiErrorCode.NOT_FOUND);
return false;
}
if (string.IsNullOrWhiteSpace(dbObject.UserOptions.EmailAddress))
{
AddError(ApiErrorCode.VALIDATION_REQUIRED, "EmailAddress");
return false;
}
var ServerUrl = ServerGlobalOpsSettingsCache.Notify.AyaNovaServerURL;
if (string.IsNullOrWhiteSpace(ServerUrl))
{
await NotifyEventProcessor.AddOpsProblemEvent("User::GenerateCredsAndEmailUser - The OPS Notification setting is empty for AyaNova Server URL. This prevents Notification system from linking events to openable objects.");
AddError(ApiErrorCode.VALIDATION_REQUIRED, "ServerUrl", "Error: no server url configured in notification settings. Can't direct user to server for login. Set server URL and try again.");
return false;
}
var newPassword = Hasher.GetRandomAlphanumericString(32);
var newLogin = Hasher.GetRandomAlphanumericString(32);
dbObject.Password = Hasher.hash(dbObject.Salt, newPassword);
dbObject.Login = newLogin;
await ct.SaveChangesAsync();
//send message
ServerUrl = ServerUrl.Trim().TrimEnd('/');
//Translations
List<string> TransKeysRequired = new List<string>();
TransKeysRequired.Add("UserLogin");
TransKeysRequired.Add("UserPassword");
TransKeysRequired.Add("NewCredsMessageBody");
TransKeysRequired.Add("NewCredsMessageTitle");
long EffectiveTranslationId = dbObject.UserOptions.TranslationId;
if (EffectiveTranslationId == 0) EffectiveTranslationId = ServerBootConfig.AYANOVA_DEFAULT_TRANSLATION_ID;
var TransDict = await TranslationBiz.GetSubsetStaticAsync(TransKeysRequired, EffectiveTranslationId);
var Title = TransDict["NewCredsMessageTitle"];
var NewCredsMessage = TransDict["NewCredsMessageBody"];
var Creds = $"{TransDict["UserLogin"]}:\n{newLogin}\n{TransDict["UserPassword"]}:\n{newPassword}\n";
IMailer m = AyaNova.Util.ServiceProviderProvider.Mailer;
await m.SendEmailAsync(dbObject.UserOptions.EmailAddress, Title, $"{NewCredsMessage}{Creds}{ServerUrl}/home-user-settings", ServerGlobalOpsSettingsCache.Notify);
//Log modification and save context
await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, dbObject.Id, BizType, AyaEvent.Modified, "GeneratedNewCredentialsAndEmailedToUser"), ct);
return true;
}
private async Task SearchIndexAsync(User obj, bool isNew)
{
//SEARCH INDEXING
@@ -592,7 +650,7 @@ namespace AyaNova.Biz
return DownloadUser;
}
////////////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -1,4 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using Microsoft.AspNetCore.Cryptography.KeyDerivation;
namespace AyaNova.Util
@@ -32,6 +34,39 @@ namespace AyaNova.Util
return Convert.ToBase64String(salt);
}
public static string GetRandomAlphanumericString(int length)
{
const string alphanumericCharacters = "0123456789abcdefghijkmnopqrstuvwxyz";
return GetRandomString(length, alphanumericCharacters);
}
public static string GetRandomString(int length, IEnumerable<char> characterSet)
{
if (length < 0)
throw new ArgumentException("length must not be negative", "length");
if (length > int.MaxValue / 8) // 250 million chars ought to be enough for anybody
throw new ArgumentException("length is too big", "length");
if (characterSet == null)
throw new ArgumentNullException("characterSet");
var characterArray = characterSet.Distinct().ToArray();
if (characterArray.Length == 0)
throw new ArgumentException("characterSet must not be empty", "characterSet");
var bytes = new byte[length * 8];
new RNGCryptoServiceProvider().GetBytes(bytes);
var result = new char[length];
for (int i = 0; i < length; i++)
{
ulong value = BitConverter.ToUInt64(bytes, i * 8);
result[i] = characterArray[value % (uint)characterArray.Length];
}
return new string(result);
}
}//eoc
}//eons