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);
            }
        }
Example #2
0
        /// <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
                    );
            }
        }
Example #3
0
        /// <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()
                    //);
                }
            });
        }
Example #4
0
        /// <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
                    }
                }
            });
        }
Example #5
0
        /// <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);
        }
Example #6
0
 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));
 }
Example #7
0
 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);
        }
Example #9
0
        /// <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
                    );
            }
        }
Example #10
0
        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);
 }
Example #13
0
        /// <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);
        }
Example #14
0
        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);
        }
Example #15
0
        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);
        }
Example #16
0
 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);
        }