Exemplo n.º 1
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
                    );
            }
        }
Exemplo n.º 2
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);
        }
Exemplo n.º 3
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);
        }
        /// <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);
        }
Exemplo n.º 5
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
                    );
            }
        }