/// <summary>
        /// Clears the EF connections pool cache
        /// </summary>
        protected void EfConnectionsPoolCacheCleanup()
        {
            ConsoleEx.Write("EF connections pool cache cleanup... ", ConsoleColor.DarkYellow);

            //EF caches connections, so in some scenarios, when there was a conn to a db that is being dropped and recreated EF will fail to
            //reconnect to it.
            //After such fail EF will reset the conns pool cache and open up new connections
            //So basically here just force the EF conn failure so it can drop previously cachec connections that are not valid anymore

            try
            {
                var ctx = new MapHiveDbContext();
                // ReSharper disable once ReturnValueOfPureMethodIsNotUsed
                ctx.Applications.FirstOrDefault();
            }
            catch (Exception)
            {
                //ignore
            }

            try
            {
                var userAccountService = CustomUserAccountService.GetInstance("MapHiveMbr");
                userAccountService.GetByEmail("*****@*****.**");
            }
            catch (Exception)
            {
                //ignore
            }

            ConsoleEx.Write("Done!" + Environment.NewLine, ConsoleColor.DarkGreen);
        }
Example #2
0
        public async Task <IHttpActionResult> ActivateAccount(AccountActivationInput activationInput, string appCtx = null)
        {
            try
            {
                var activationOutput =
                    await
                    Auth.ActivateAccountAsync(CustomUserAccountService.GetInstance("MapHiveMbr"),
                                              activationInput.VerificationKey, activationInput.InitialPassword);


                //need to resend email with new verification key, as the previous one was stale
                if (activationOutput.VerificationKeyStale)
                {
                    var dbCtx      = new MapHiveDbContext("MapHiveMeta");
                    var emailStuff = await GetEmailStuffAsync("activate_account_stale", appCtx, dbCtx);

                    //basically need to send an email the verification key has expired and send a new one
                    var user = await dbCtx.Users.Where(u => u.Email == activationOutput.Email).FirstOrDefaultAsync();

                    //since got an email off mbr, user should not be null, but just in a case...
                    if (user == null)
                    {
                        return(BadRequest());
                    }

                    //prepare the email template tokens
                    var tokens = new Dictionary <string, object>
                    {
                        { "UserName", $"{user.GetFullUserName()} ({user.Email})" },
                        { "Email", user.Email },
                        { "RedirectUrl", this.GetRequestSource().Split('#')[0] },
                        { "VerificationKey", activationOutput.VerificationKey },
                        { "InitialPassword", "" }
                    };

                    //prepare and send the email
                    EmailSender.Send(emailStuff.Item1, emailStuff.Item2.Prepare(tokens), user.Email);
                }

                //mbr has not found a user, so bad, bad, bad request it was
                if (activationOutput.UnknownUser)
                {
                    return(BadRequest());
                }

                //wipe out some potentially sensitive data
                activationOutput.Email           = null;
                activationOutput.VerificationKey = null;

                return(Ok(activationOutput));
            }
            catch (Exception ex)
            {
                return(HandleException(ex));
            }
        }
Example #3
0
        public async Task <IHttpActionResult> Link(Guid organisationId, MapHiveUser user)
        {
            try
            {
                await this.OrganisationContext.AddOrganisationUser(_dbCtx, user, CustomUserAccountService.GetInstance("MapHiveMbr"));

                return(Ok());
            }
            catch (Exception ex)
            {
                return(HandleException(ex));
            }
        }
Example #4
0
        public async Task <IHttpActionResult> ChangePassword(ChangePassInput input)
        {
            //Note: basically this is a pass reset request, so NO need to inform a potential attacker about exceptions - always return ok!

            try
            {
                var resetPassSuccess =
                    await Auth.ChangePasswordAsync(CustomUserAccountService.GetInstance("MapHiveMbr"), input.NewPass, input.OldPass);

                return(Ok(resetPassSuccess));
            }
            catch (Exception ex)
            {
                return(HandleException(ex));
            }
        }
Example #5
0
        public async Task <IHttpActionResult> ChangePasswordFromResetKey(PassResetInput input)
        {
            //Note: basically this is a pass reset request, so NO need to inform a potential attacker about exceptions - always return ok!

            try
            {
                var resetPassSuccess =
                    await Auth.ChangePasswordFromResetKeyAsync(CustomUserAccountService.GetInstance("MapHiveMbr"), input.NewPass, input.VerificationKey);

                //Note: if handle to get here, then pass should be reset

                return(Ok(resetPassSuccess));
            }
            catch (Exception ex)
            {
                return(HandleException(ex));
            }
        }
Example #6
0
        public async Task <IHttpActionResult> PassResetRequest(PassResetRequestInput input, string appCtx = null)
        {
            //Note: basically this is a pass reset request, so NO need to inform a potential attacker about exceptions - always return ok!

            try
            {
                var requestPassResetOutput =
                    await Auth.RequestPassResetAsync(CustomUserAccountService.GetInstance("MapHiveMbr"), input.Email);

                var dbCtx      = new MapHiveDbContext("MapHiveMeta");
                var emailStuff = await GetEmailStuffAsync("pass_reset_request", appCtx, dbCtx);

                //basically need to send an email the verification key has expired and send a new one
                var user = await dbCtx.Users.Where(u => u.Email == input.Email).FirstOrDefaultAsync();

                //since got here and email off mbr, user should not be null, but just in a case...
                if (user == null)
                {
                    //return BadRequest();
                    return(Ok());
                }

                //prepare the email template tokens
                var tokens = new Dictionary <string, object>
                {
                    { "UserName", $"{user.GetFullUserName()} ({user.Email})" },
                    { "Email", user.Email },
                    { "RedirectUrl", this.GetRequestSource().Split('#')[0] },
                    { "VerificationKey", requestPassResetOutput.VerificationKey }
                };

                //prepare and send the email
                EmailSender.Send(emailStuff.Item1, emailStuff.Item2.Prepare(tokens), user.Email);

                return(Ok());
            }
            catch (Exception ex)
            {
                //return HandleException(ex);
                return(Ok());
            }
        }
Example #7
0
        public async Task <IHttpActionResult> Delete(Guid uuid)
        {
            try
            {
                var user = await(new MapHiveUser()).ReadAsync(_dbCtx, uuid);

                if (user != null)
                {
                    await user.DestroyAsync(_dbCtx, CustomUserAccountService.GetInstance("MapHiveMbr"));

                    return(StatusCode(HttpStatusCode.NoContent));
                }
                else
                {
                    return(NotFound());
                }
            }
            catch (Exception ex)
            {
                return(this.HandleException(ex));
            }
        }
Example #8
0
        /// <summary>
        /// Standardises user creation
        /// </summary>
        /// <param name="dbCtx"></param>
        /// <param name="user"></param>
        /// <param name="emailStuff"></param>
        /// <param name="redirectUrl"></param>
        /// <returns></returns>
        public static async Task <MapHiveUser> CreateUser(DbContext dbCtx, MapHiveUser user,
                                                          Tuple <IEmailAccount, IEmailTemplate> emailStuff, string redirectUrl)
        {
            //Note: there are 2 options to send emails when creation a user account:
            //1. listen to UserCreated evt on the User object and then process email manually
            //2. grab the appropriate email template and email account, potentially adjust some email template tokens prior to creating a user and pass both sender account and email template to a user creation procedure

            //In this scenario a second approach is used

            //initial email template customisation:
            //{UserName}
            //{Email}
            //{RedirectUrl}
            var replacementData = new Dictionary <string, object>
            {
                { "UserName", $"{user.GetFullUserName()} ({user.Email})" },
                { "Email", user.Email },
                { "RedirectUrl", redirectUrl }
            };

            emailStuff?.Item2.Prepare(replacementData);

            return(await user.CreateAsync(dbCtx, CustomUserAccountService.GetInstance("MapHiveMbr"), emailStuff?.Item1, emailStuff?.Item2));
        }
Example #9
0
        protected virtual async Task Handle_AddSuper(Dictionary <string, string> args)
        {
            var cmd = GetCallerName();

            args = args ?? new Dictionary <string, string>();

            if (GetHelp(args))
            {
                Console.WriteLine($"'{cmd}' : adds a maphive superuser to the system. expects the master of puppets app to be registered; will fail if it is not.");
                Console.WriteLine($"syntax: {cmd} space separated params: ");
                Console.WriteLine("\t[e:email]");
                Console.WriteLine("\t[p:pass]");
                Console.WriteLine("\t[s:slug] user's slug");
                Console.WriteLine("\t[o:{presence}] whether or not user is an org user");
                Console.WriteLine();
                Console.WriteLine($"example: {cmd} e:[email protected] p:test");
                return;
            }

            var email     = ExtractParam("e", args);
            var pass      = ExtractParam("p", args);
            var slug      = ExtractParam("s", args);
            var isOrgUser = ContainsParam("o", args);

            //use the default account if email and pass not provided
            if (string.IsNullOrEmpty(email) || string.IsNullOrEmpty(pass))
            {
                email = "*****@*****.**";
                pass  = "******";
            }

            if (!isOrgUser && string.IsNullOrEmpty(slug))
            {
                slug = email.Split('@')[0];
            }

            //delegate user creation
            var prms = new Dictionary <string, string>()
            {
                { "e", email },
                { "p", pass },
                { "s", slug }
            };

            if (isOrgUser)
            {
                prms.Add("o", null);
            }
            await Handle_AddUser(prms);

            try
            {
                ConsoleEx.Write("Setting up super user access... ", ConsoleColor.DarkYellow);

                var ctx = new MapHiveDbContext("MapHiveMeta");

                //create the master org if not exists!
                var org = await ctx.Organisations.FirstOrDefaultAsync(o => o.Slug == "thehive");

                if (org == null)
                {
                    org = await new Organisation
                    {
                        Slug        = "thehive",
                        DisplayName = "The Hive"
                    }.CreateAsync(ctx);

                    //get the maphive admin app
                    var masterofpuppets = await ctx.Applications.FirstOrDefaultAsync(a => a.ShortName == "masterofpuppets");

                    //add a link to a non-public app!
                    //users with access to this organisation will be able to access this app too
                    //note: perhaps with some app access privs defined at some point
                    org.AddLink(masterofpuppets);
                    await org.UpdateAsync(ctx);
                }

                //get user by email
                var user = await ctx.Users.FirstOrDefaultAsync(u => u.Email == email);

                //assign user to the org, so user is visible 'within' an org
                org.AddLink(user);
                await org.UpdateAsync(ctx);

                //assing the master org owner role to a user
                var orgOwnerR = await org.GetOrgOwnerRoleAsync(ctx);

                user.AddLink(orgOwnerR);


                //finally save a user
                await user.UpdateAsync(ctx, CustomUserAccountService.GetInstance("MapHiveMbr"));

                ConsoleEx.Write("Done!" + Environment.NewLine, ConsoleColor.DarkGreen);
                Console.WriteLine();
            }
            catch (Exception ex)
            {
                HandleException(ex);
                return;
            }
        }
Example #10
0
        public async Task <IHttpActionResult> Post(Guid organisationId, MapHiveUser user)
        {
            //this is an org user, so needs to be flagged as such!
            user.IsOrgUser            = true;
            user.ParentOrganisationId = organisationId;

            try
            {
                var createdUser = await Utils.User.CreateUser(_dbCtx, user,
                                                              await GetEmailStuffAsync("user_created", _dbCtx as ILocalised), GetRequestSource().Split('#')[0]);

                if (createdUser != null)
                {
                    //user has been created, so now need to add it to the org with an appropriate role
                    await this.OrganisationContext.AddOrganisationUser(_dbCtx, createdUser, CustomUserAccountService.GetInstance("MapHiveMbr"));

                    return(Ok(createdUser));
                }

                return(NotFound());
            }
            catch (Exception ex)
            {
                return(this.HandleException(ex));
            }
        }
Example #11
0
        public async Task <IHttpActionResult> UnLink(Guid organisationId, Guid uuid)
        {
            try
            {
                //get a user an make sure he is not an org user!
                var user = await new MapHiveUser().ReadAsync(_dbCtx, uuid);
                if (
                    user == null || (user.IsOrgUser && user.ParentOrganisationId == organisationId) ||
                    user.UserOrgId == organisationId    //also avoid removing own org of a user!
                    )
                {
                    throw MapHive.Server.Core.DataModel.Validation.Utils.GenerateValidationFailedException(nameof(MapHiveUser), MapHive.Server.Core.DataModel.Validation.ValidationErrors.OrgOwnerDestroyError);
                }



                //not providing a user role will effectively wipe out user assignment
                user.OrganisationRole = null;
                await this.OrganisationContext.ChangeOrganisationUserRole(_dbCtx, user, CustomUserAccountService.GetInstance("MapHiveMbr"));

                OrganisationContext.RemoveLink(user);
                await OrganisationContext.UpdateAsync(_dbCtx);

                return(Ok());
            }
            catch (Exception ex)
            {
                return(HandleException(ex));
            }
        }
Example #12
0
        public async Task <IHttpActionResult> Put(Guid organisationId, MapHiveUser user, Guid uuid)
        {
            try
            {
                var entity = await user.UpdateAsync <MapHiveUser, CustomUserAccount>(_dbCtx, CustomUserAccountService.GetInstance("MapHiveMbr"), uuid);

                if (entity != null)
                {
                    //once the user has been updated, need to update its role within an org too
                    await this.OrganisationContext.ChangeOrganisationUserRole(_dbCtx, user, CustomUserAccountService.GetInstance("MapHiveMbr"));

                    return(Ok(entity));
                }

                return(NotFound());
            }
            catch (Exception ex)
            {
                return(this.HandleException(ex));
            }
        }
Example #13
0
        protected virtual async Task Handle_AddUser(Dictionary <string, string> args)
        {
            var cmd = GetCallerName();

            if (GetHelp(args))
            {
                Console.WriteLine($"'{cmd}' : adds a user to the system");
                Console.WriteLine($"syntax: {cmd} space separated params: ");
                Console.WriteLine("\t[e:email]");
                Console.WriteLine("\t[p:pass]");
                Console.WriteLine("\t[s:slug] user's slug");
                Console.WriteLine("\t[o:{presence}] whether or not user is an org user");
                Console.WriteLine();
                Console.WriteLine($"example: {cmd} e:[email protected] p:test");
                return;
            }

            var email     = ExtractParam("e", args);
            var pass      = ExtractParam("p", args);
            var slug      = ExtractParam("s", args);
            var isOrgUser = ContainsParam("o", args);

            if (string.IsNullOrEmpty(email) || string.IsNullOrEmpty(pass))
            {
                ConsoleEx.WriteErr("User name and pass cannot be empty!");
            }

            ConsoleEx.WriteLine($"Creating user: '******' with the following pass: '******'; user is org user: {isOrgUser}; slug: {slug}", ConsoleColor.DarkYellow);

            //need a valid user to create a Core.Base object
            Server.Core.Utils.Identity.ImpersonateGhostUser();

            var user = new MapHiveUser
            {
                Email     = email,
                Slug      = slug,
                IsOrgUser = isOrgUser
            };


            //Note: db context uses a connection defined in app cfg.
            //TODO - make it somewhat dynamic!
            try
            {
                //destroy a previous account if any
                await DestroyUser <MapHiveUser>(email, new MapHiveDbContext("MapHiveMeta"), CustomUserAccountService.GetInstance("MapHiveMbr"));

                IDictionary <string, object> op = null;
                user.UserCreated += (sender, eventArgs) =>
                {
                    op = eventArgs.OperationFeedback;
                };

                await user.CreateAsync(new MapHiveDbContext("MapHiveMeta"), CustomUserAccountService.GetInstance("MapHiveMbr"));

                //once user is created, need to perform an update in order to set it as valid
                user.IsAccountVerified = true;
                await user.UpdateAsync(new MapHiveDbContext("MapHiveMeta"), CustomUserAccountService.GetInstance("MapHiveMbr"), user.Uuid);

                //and also need to change the pass as the default procedure autogenerates a pass
                CustomUserAccountService.GetInstance("MapHiveMbr")
                .ChangePassword(user.Uuid, (string)op["InitialPassword"], pass);

                ConsoleEx.WriteOk($"User '{email}' with the following pass: '******' has been created.");
                Console.WriteLine();
            }
            catch (Exception ex)
            {
                HandleException(ex);
                return;
            }
        }
Example #14
0
        public async Task <IHttpActionResult> Put(MapHiveUser obj, Guid uuid)
        {
            try
            {
                var entity = await obj.UpdateAsync <MapHiveUser, CustomUserAccount>(_dbCtx, CustomUserAccountService.GetInstance("MapHiveMbr"), uuid);

                if (entity != null)
                {
                    return(Ok(entity));
                }

                return(NotFound());
            }
            catch (Exception ex)
            {
                return(this.HandleException(ex));
            }
        }
        protected virtual async Task Handle_DestroyUser(Dictionary <string, string> args)
        {
            var cmd = GetCallerName();

            if (GetHelp(args))
            {
                Console.WriteLine($"'{cmd}' : destroys a user account");
                Console.WriteLine($"syntax: {cmd} space separated params: ");
                Console.WriteLine("\t[e:email]");
                Console.WriteLine("\t[d:destroy the default user account ([email protected])]");
                Console.WriteLine();
                Console.WriteLine($"example: {cmd} e:[email protected]");
                Console.WriteLine($"example: {cmd} d:true");
                return;
            }

            var email = ExtractParam("e", args);
            var dflt  = ExtractParam <bool>("d", args);

            //use the default account if required
            if (dflt)
            {
                email = "*****@*****.**";
            }

            if (string.IsNullOrEmpty(email))
            {
                ConsoleEx.WriteErr("You must either provide email of a user you're trying to wipe out or pass default:true; for more info type destroyuser help");
                Console.WriteLine();
                return;
            }

            //need a valid user to create a Core.Base object
            Server.Core.Utils.Identity.ImpersonateGhostUser();


            //Note: db context uses a connection defined in app cfg.
            await DestroyUser <MapHiveUser>(email, new MapHiveDbContext("MapHiveMeta"), CustomUserAccountService.GetInstance("MapHiveMbr"));

            Console.WriteLine();
        }
Example #16
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
                    );
            }
        }