private void SendMessageBySmtp(IEmailMessage message, IEmailAccount sender, params IEmailAccount[] to) { try { var msg = new MailMessage { BodyEncoding = Encoding.UTF8, Subject = message.Subject, Body = message.Body, From = new MailAddress(sender.ToAdressString()), IsBodyHtml = message.IsBodyHtml, }; foreach (var emailRecipient in to) { msg.To.Add(new MailAddress(emailRecipient.ToAdressString())); } _smtpClient.Send(msg); } catch (Exception exception) { throw new EmailException(ErrorMessage, exception); } }
/// <summary> /// Resends activation link for a user account /// </summary> /// <param name="dbCtx"></param> /// <param name="emailSender"></param> /// <param name="emailAccount"></param> /// <param name="emailTemplate"></param> /// <param name="password"></param> /// <returns></returns> public async Task ResendActivationLinkAsync(DbContext dbCtx, IEmailSender emailSender, IEmailAccount emailAccount = null, IEmailTemplate emailTemplate = null, string password = null) { if (Uuid == default(Guid)) { throw new InvalidOperationException("You cannot resend an activation link - this user has not yet been created..."); } var newActivationDetails = await Auth.GetNewAccountActivationDetailsAsync(Uuid); //generate new email confirmation token var emailConfirmationToken = Auth.MergeIdWithToken( Uuid, newActivationDetails.newAccountActivationToken ); //send out the email if (emailTemplate != null && emailAccount != null) { emailSender.Send( emailAccount, emailTemplate.Prepare(new Dictionary <string, object> { { "VerificationKey", emailConfirmationToken }, { "InitialPassword", newActivationDetails.newPass } }), Email ); } }
/// <summary> /// Sends an email /// </summary> /// <param name="emailAccount"></param> /// <param name="emailTemplate"></param> /// <param name="recipient"></param> /// <returns></returns> public async Task SendAsync(IEmailAccount emailAccount, IEmailTemplate emailTemplate, string recipient) { await Task.Run(() => { var mail = new MailMessage(); mail.To.Add(recipient); mail.From = new MailAddress(emailAccount.Sender); mail.Subject = emailTemplate.Title; mail.SubjectEncoding = Encoding.UTF8; mail.Body = emailTemplate.Body; mail.IsBodyHtml = emailTemplate.IsBodyHtml; mail.BodyEncoding = Encoding.UTF8; var smtp = new SmtpClient { Host = emailAccount.SmtpHost, Port = emailAccount.SmtpPort ?? 587, Credentials = new System.Net.NetworkCredential(emailAccount.User, emailAccount.Pass), EnableSsl = emailAccount.Ssl ?? false }; var actionId = DateTime.Now.Ticks; try { //Log($"{actionId} :: Attempting to send email to {recipient}; time start: {DateTime.Now.ToShortDateString()}-{DateTime.Now.ToShortTimeString()}"); smtp.Send(mail); //Log($"{actionId} :: Email sent to {recipient}", $"Time taken in seconds: {new TimeSpan(DateTime.Now.Ticks - actionId).TotalSeconds}"); } catch (Exception ex) { var msgs = new List <string> { $"{actionId} :: Failed to send emails to {recipient}", $"Time taken in seconds: {new TimeSpan(DateTime.Now.Ticks - actionId).TotalSeconds}", $"Sender details - host: {emailAccount.SmtpHost}, port: {emailAccount.SmtpPort}, sender: {emailAccount.Sender}, user: {emailAccount.User}, pass: {emailAccount.Pass}, ssl: {emailAccount.Ssl}", }; var e = ex; var tab = string.Empty; while (e != null) { msgs.Add($"{tab}{e.Message}"); e = ex.InnerException; tab += '\t'; } //debug //TODO - use proper logging utils! //Log( // msgs.ToArray() //); } }); }
/// <summary> /// Sends an email to the recipient. Email is sent in a fire'n'forget manner. /// Note: in some scenarios fire'n'forget means the email may not eventually be sent at all. /// Uses Mailkit as an smtp client /// </summary> /// <param name="emailAccount">EmailAccount deails</param> /// <param name="emailTemplate">Email data to be sent out</param> /// <param name="recipient">Email of a recipient</param> public void Send(IEmailAccount emailAccount, IEmailTemplate emailTemplate, string recipient) { Task.Run(() => { var msg = new MimeMessage(); msg.To.Add(new MailboxAddress(recipient)); msg.From.Add(new MailboxAddress(emailAccount.Sender)); msg.Subject = emailTemplate.Title; msg.Body = new TextPart(emailTemplate.IsBodyHtml ? TextFormat.Html : TextFormat.Plain) { Text = emailTemplate.Body }; using (var emailClient = new SmtpClient()) { try { //emailClient.Connect(emailAccount.SmtpHost, emailAccount.SmtpPort ?? 587, true); //looks like outlook mailkit does not like the enforce secure connection when talking to outlook online //i guess outlook will enforce the tsl anyway emailClient.Connect(emailAccount.SmtpHost, emailAccount.SmtpPort ?? 587); //not using oauth, so remove it! emailClient.AuthenticationMechanisms.Remove("XOAUTH2"); emailClient.Authenticate(emailAccount.User, emailAccount.Pass); emailClient.Send(msg); emailClient.Disconnect(true); } catch { //ignore } } }); }
/// <summary> /// Creates a new user account in both MembershipReboot database and in the MapHive meta database; /// sends out a confirmation email if email account and template are provided /// </summary> /// <typeparam name="T"></typeparam> /// <typeparam name="TAccount"></typeparam> /// <param name="userAccountService"></param> /// <param name="dbCtx"></param> /// <param name="emailAccount"></param> /// <param name="emailTemplate"></param> /// <returns></returns> protected internal virtual async Task <T> CreateAsync <T, TAccount>(DbContext dbCtx, UserAccountService <TAccount> userAccountService, IEmailAccount emailAccount = null, IEmailTemplate emailTemplate = null) where T : MapHiveUserBase where TAccount : RelationalUserAccount { T output; //need to validate the model first await ValidateAsync(dbCtx); //make sure the email is ALWAYS lower case Email = Email.ToLower(); //check if the email is already used or not; throw validation feedback exception if so //Note - could do it in the mh meta, but both dbs must be in sync anyway var emailInUse = userAccountService.GetByEmail(Email) != null; if (emailInUse) { throw Validation.Utils.GenerateValidationFailedException(nameof(Email), ValidationErrors.EmailInUse); } //user account exists in two places - mbr and mh databases. Therefore need to handle them both properly wrapped into transactions DbContext mbrDbCtx = GetMembershipRebootDbCtx(userAccountService); System.Data.Common.DbTransaction mbrTrans = null; System.Data.Common.DbTransaction mhTrans = null; //since this method wraps the op on 2 dbs into transactions, it must handle connections manually and take care of closing it aftwerwards //it is therefore required to clone contexts with independent conns so the base contexts can be reused var clonedMhDbCtx = dbCtx.Clone(contextOwnsConnection: false); var clonedMbrDbCtx = mbrDbCtx.Clone(false); try { //open the connections as otherwise will not be able to begin transaction await clonedMbrDbCtx.Database.Connection.OpenAsync(); await clonedMhDbCtx.Database.Connection.OpenAsync(); //begin the transaction and set the transaction object back on the db context so it uses it mbrTrans = clonedMbrDbCtx.Database.Connection.BeginTransaction(); clonedMbrDbCtx.Database.UseTransaction(mbrTrans); mhTrans = clonedMhDbCtx.Database.Connection.BeginTransaction(); clonedMhDbCtx.Database.UseTransaction(mhTrans); //first create a membership reboot account //wire up evt too, to intercept what mbr is trying to say... AccountCreatedEvent <TAccount> e = null; userAccountService.Configuration.AddEventHandler(new MembershipRebootEventHandlers.AccountCreatedEventHandler <TAccount>( (evt) => { e = evt; })); var newMbrAccount = userAccountService.CreateAccount(this.Email, Cartomatic.Utils.Crypto.Generator.GenerateRandomString(10), this.Email); //so can next pass some data to the mh meta user object this.Uuid = newMbrAccount.ID; //mbr work done, so can create the user within the mh metadata db output = await base.CreateAsync <T>(clonedMhDbCtx); //looks like we're good to go, so can commit mbrTrans.Commit(); mhTrans.Commit(); var opFeedback = new Dictionary <string, object> { { nameof(e.InitialPassword), e.InitialPassword }, { nameof(e.VerificationKey), e.VerificationKey } }; //if email related objects have been provided, send the account created email if (emailAccount != null && emailTemplate != null) { EmailSender.Send( emailAccount, emailTemplate.Prepare(opFeedback), Email ); } //finally the user created event UserCreated?.Invoke( this, new Events.OpFeedbackEventArgs { OperationFeedback = opFeedback } ); } catch (Exception ex) { mbrTrans?.Rollback(); mhTrans?.Rollback(); throw Validation.Utils.GenerateValidationFailedException(ex); } finally { //try to close the connections as they were opened manually and therefore may not have been closed! clonedMhDbCtx.Database.Connection.CloseConnection(dispose: true); clonedMbrDbCtx.Database.Connection.CloseConnection(dispose: true); mbrTrans?.Dispose(); mhTrans?.Dispose(); } return(output); }
public static async Task <T> CreateAsync <T, TAccount>(this T obj, DbContext dbCtx, UserAccountService <TAccount> userAccountService, IEmailAccount emailAccount, IEmailTemplate emailTemplate) where T : MapHiveUserBase where TAccount : RelationalUserAccount { return(await obj.CreateAsync <T, TAccount>(dbCtx, userAccountService, emailAccount, emailTemplate)); }
public EmailSender(IEmailAccount emailAcct, HttpContextBase context, IDateTimeHelper dateTime, IUtilityService utilityService) { this._emailAccount = emailAcct; this._httpContext = context; this._iDateTimeHelper = dateTime; }
/// <summary> /// Creates a user acount, sends out email, modifies pass if a custom pass is provided; /// this is a simple wrapper over the standard user.CreateAsync that adds an option to provide a specific password /// </summary> /// <typeparam name="T"></typeparam> /// <param name="dbCtx"></param> /// <param name="user"></param> /// <param name="emailSender"></param> /// <param name="emailAccount"></param> /// <param name="emailTemplate"></param> /// <param name="password"></param> /// <returns></returns> public static async Task <CreateUserAccountOutput> CreateUserAccountAsync(DbContext dbCtx, MapHiveUser user, IEmailSender emailSender, IEmailAccount emailAccount = null, IEmailTemplate emailTemplate = null, string password = null) { var output = new CreateUserAccountOutput { User = user }; //see if a user has already been created. if so do not attempt to create it; if (user.Uuid != default(Guid)) { return(output); } //need to grab an initial pass to change it if a pass has been provided var initialPass = string.Empty; //wire up an evt listener, so can react to user created evt and send a confirmation email user.UserCreated += (sender, args) => { initialPass = (string)args.OperationFeedback["InitialPassword"]; //output, so can use it in the m2m tests output.InitialPassword = initialPass; output.VerificationKey = Auth.MergeIdWithToken( user.Uuid, (string)args.OperationFeedback["VerificationKey"] ); //prepare email if present emailTemplate?.Prepare(args.OperationFeedback); if (emailTemplate != null && emailAccount != null) { emailSender.Send( emailAccount, emailTemplate, user.Email ); } }; //create user without auto email send here - it's customised and sent via evt handler above var createdUser = await user.CreateAsync(dbCtx); //once user has been created adjust his pass if provided if (!string.IsNullOrEmpty(password)) { //grab user manager var userManager = MapHive.Core.Identity.UserManagerUtils.GetUserManager(); var idUser = await userManager.FindByIdAsync(user.Uuid.ToString()); await userManager.ChangePasswordAsync(idUser, initialPass, password); } return(output); }
/// <summary> /// Resends an activation link for a user; expects a user_created email template and a valid user identifier /// </summary> /// <param name="context"></param> /// <param name="userId"></param> /// <param name="ea"></param> /// <param name="emailTpl"></param> /// <returns></returns> public static async Task ResendActivationLink(DbContext context, Guid userId, IEmailAccount ea = null, IEmailTemplate emailTpl = null) { //Note: MBR seems to not like resending an activation link with a new password... It simply does not generate a new one but rather regenerates the verification key //because of that need to fake an account creation with some random email and then grab some auto generated data out of it // //basically a new account is created in order to get a new token and a new pass and verification key with its creation date //such user account is then destroyed but the necessary details are set on the account that we try to resend a link for. if (userId == default(Guid)) { throw new InvalidOperationException("You cannot resend an activation link - this user has not yet been created..."); } var mbrCtx = new CustomDbContext("MapHiveMbr"); //get the mbr user object var mbrUser = await mbrCtx.Users.FirstOrDefaultAsync(u => u.ID == userId); if (mbrUser == null) { throw new InvalidOperationException("User does not exist in MBR."); } //need user account service to properly create a MBR user var userAccountService = CustomUserAccountService.GetInstance("MapHiveMbr"); //wire up an evt listener - this is the way mbr talks AccountCreatedEvent <CustomUserAccount> e = null; userAccountService.Configuration.AddEventHandler( new MembershipRebootEventHandlers.AccountCreatedEventHandler <CustomUserAccount>(evt => e = evt)); //rrnd email - after all need to avoid scenarios when two folks try the same resend activation procedure at once var rndEmail = $"{DateTime.Now.Ticks}@somedomain.com"; //finally a new rnd user, so we can get a properly recreated verification key and a new pass... var newMbrAccount = userAccountService.CreateAccount(rndEmail, Cartomatic.Utils.Crypto.Generator.GenerateRandomString(10), rndEmail); //update the account in question with //mbrUser.VerificationKey = newMbrAccount.VerificationKey; //mbrUser.VerificationPurpose = newMbrAccount.VerificationPurpose; //mbrUser.HashedPassword = newMbrAccount.HashedPassword; //mbrUser.VerificationKeySent = newMbrAccount.VerificationKeySent //because the properties are read only, we need to do some crazy hocus-pocus again //note: looks like the type returned via mbrCtx.Users.FirstOrDefaultAsync is somewhat more dynamic and does not //map properly. therefore need to use a 'barebone' object instance var obj = new CustomUserAccount(); //Warning - this sql is postgresql specific! var updateSql = $@"UPDATE {mbrCtx.GetTableSchema(obj)}.""{mbrCtx.GetTableName(obj)}"" SET ""{mbrCtx.GetTableColumnName(obj, nameof(mbrUser.VerificationKey))}"" = '{newMbrAccount.VerificationKey}', ""{mbrCtx.GetTableColumnName(obj, nameof(mbrUser.VerificationPurpose))}"" = {(int)newMbrAccount.VerificationPurpose}, ""{mbrCtx.GetTableColumnName(obj, nameof(mbrUser.HashedPassword))}"" = '{newMbrAccount.HashedPassword}', ""{mbrCtx.GetTableColumnName(obj, nameof(mbrUser.VerificationKeySent))}"" = '{newMbrAccount.VerificationKeySent}' WHERE ""{mbrCtx.GetTableColumnName(obj, nameof(mbrUser.ID))}"" = '{mbrUser.ID}';"; //get rid of the new account mbrCtx.Users.Remove(await mbrCtx.Users.FirstAsync(u => u.ID == newMbrAccount.ID)); //and save da mess await mbrCtx.SaveChangesAsync(); await mbrCtx.Database.ExecuteSqlCommandAsync(updateSql); //send out the email if (emailTpl != null && ea != null) { MapHive.Server.Core.Email.EmailSender.Send( ea, emailTpl.Prepare(new Dictionary <string, object> { { nameof(e.VerificationKey), e.VerificationKey }, { nameof(e.InitialPassword), e.InitialPassword } }), mbrUser.Email ); } }
private static void ReceiveAndWriteEmails(IEmailAccount emailAccount) { var receivedEmails = emailAccount.ReceiveEmails(); WriteEmails(receivedEmails, emailAccount.EmailAddress); }
public void SendMessage(IEmailMessage message, IEmailAccount sender, IEnumerable <IEmailAccount> to) { SendMessageBySmtp(message, sender, to.ToArray()); }
public void SendMessage(IEmailMessage message, IEmailAccount sender, IEmailAccount to) { SendMessageBySmtp(message, sender, to); }
/// <summary> /// Creates a new user account in both Identity database and in the MapHive meta database; /// sends out a confirmation email if email account and template are provided /// </summary> /// <typeparam name="T"></typeparam> /// <param name="dbCtx"></param> /// <param name="emailSender"></param> /// <param name="emailAccount"></param> /// <param name="emailTemplate"></param> /// <returns></returns> protected internal virtual async Task <T> CreateAsync <T>(DbContext dbCtx, IEmailSender emailSender, IEmailAccount emailAccount, IEmailTemplate emailTemplate) where T : Base { T output; //need to validate the model first await ValidateAsync(dbCtx); //make sure the email is ALWAYS lower case Email = Email.ToLower(); //grab user manager var userManager = MapHive.Core.Identity.UserManagerUtils.GetUserManager(); //check if the email is already used or not; throw validation feedback exception if so //Note - could do it in the mh meta, but both dbs must be in sync anyway var emailInUse = await userManager.FindByEmailAsync(Email) != null; if (emailInUse) { throw Validation.Utils.GenerateValidationFailedException(nameof(Email), ValidationErrors.EmailInUse); } try { var rndPass = Cartomatic.Utils.Crypto.Generator.GenerateRandomString(10); var idUser = new MapHiveIdentityUser { Id = Guid.NewGuid(), UserName = Email.ToLower(), Email = Email.ToLower() }; var result = await userManager.CreateAsync(idUser, rndPass); //so can next pass some data to the mh meta user object this.Uuid = idUser.Id; //identity work done, so can create the user within the mh metadata db output = await base.CreateAsync <T>(dbCtx); var opFeedback = new Dictionary <string, object> { { "InitialPassword", rndPass }, { "VerificationKey", Auth.MergeIdWithToken( idUser.Id, await userManager.GenerateEmailConfirmationTokenAsync(idUser) ) } }; //if email related objects have been provided, send the account created email if (emailAccount != null && emailTemplate != null) { emailSender.Send( emailAccount, emailTemplate.Prepare(opFeedback), Email ); } //finally the user created event UserCreated?.Invoke( this, new Events.OpFeedbackEventArgs { OperationFeedback = opFeedback } ); } catch (Exception ex) { throw Validation.Utils.GenerateValidationFailedException(ex); } return(output); }
protected internal override async Task <T> CreateAsync <T, TAccount>(DbContext dbCtx, UserAccountService <TAccount> userAccountService, IEmailAccount emailAccount = null, IEmailTemplate emailTemplate = null) { EnsureSlug(); var user = await base.CreateAsync <T, TAccount>(dbCtx, userAccountService, emailAccount, emailTemplate) as MapHiveUser; //unless user is marked as an org user, create an org for him if (!user.IsOrgUser) { await CreateUserOrganisationAsync(dbCtx, userAccountService); } return((T)(Base)user); }
protected internal override async Task <T> CreateAsync <T>(DbContext dbCtx, IEmailSender emailSender, IEmailAccount emailAccount = null, IEmailTemplate emailTemplate = null) { EnsureSlug(); //need guid to properly tie up resources with the parent object //so generating it here Uuid = Guid.NewGuid(); await HandleResources(dbCtx, Uuid); Fullname = this.GetFullUserName(); var user = await base.CreateAsync <T>(dbCtx, emailSender, emailAccount, emailTemplate) as MapHiveUser; return((T)(Base)user); }
public EmailSender(IEmailAccount emailAcct, HttpContextBase context) { this._EmailAccount = emailAcct; this._httpContext = context; }
/// <summary> /// Resends an activation link for a user; expects a user_created email template and a valid user identifier /// </summary> /// <param name="context"></param> /// <param name="userId"></param> /// <param name="emailSender"></param> /// <param name="emailAccount"></param> /// <param name="emailTemplate"></param> /// <returns></returns> public static async Task ResendActivationLink(DbContext context, Guid userId, IEmailSender emailSender, IEmailAccount emailAccount = null, IEmailTemplate emailTemplate = null) { //grab a user first var user = await Base.ReadObjAsync <MapHiveUser>(context, userId, true); //and use the user method - implemented at user lvl even though applies to auth - need to send email await user.ResendActivationLinkAsync(context, emailSender, emailAccount, emailTemplate); }