/// <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); }
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)); } }
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)); } }
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)); } }
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)); } }
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()); } }
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)); } }
/// <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)); }
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; } }
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)); } }
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)); } }
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)); } }
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; } }
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(); }
/// <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 ); } }