public void SendSingleEmail(int emailDefinitionId, string recipientUserId) { EmailDefinition definition = _db.EmailDefinitions.Find(emailDefinitionId); TimetableUserEntry user = _timetableUserRepository.GetByUsername(recipientUserId); if (definition == null) { throw new ArgumentOutOfRangeException(nameof(emailDefinitionId), "No email definition with such id"); } if (user == null) { throw new ArgumentOutOfRangeException(nameof(user), "No timetable user entry with such id"); } MailMessage message = new MailMessage { Subject = definition.Subject, Body = definition.Body, IsBodyHtml = true, From = EmailHelpers.DefaultSender, To = { new MailAddress(user.InternalEmail, user.Fullname) }, }; _smtpClient.Send(message); }
private void PopulateUserInfo(ITableRow row) { TimetableUserEntry userEntry = _userRepository.GetByUsername(row.UserId); if (userEntry != null) { PopulateUserInfo(row, userEntry); } }
public ActionResult Login(LoginForm form) { if (AuthHelpers.IsAuthenticated(User)) { return(RedirectAfterLogin(true)); } if (!ModelState.IsValid) { // Something went wrong during binding probably return(View()); } string username = TimetableUserEntry.NormalizeUsernameToId(form.Username); TimetableUserEntry user = new TimetableUserRepository().GetByUsername(username); if (user == null) { ModelState.AddModelError("Username", "This username doesn't exist"); return(View()); } // https://stackoverflow.com/a/31585768/2588539 ClaimsIdentity identity = new ClaimsIdentity( new[] { // These 2 are required for default antiforgery provider new Claim(ClaimTypes.NameIdentifier, username), new Claim( "http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider", "ASP.NET Identity", "http://www.w3.org/2001/XMLSchema#string" ), // Additional stuff new Claim(ClaimTypes.Role, user.IsStudentSupport ? Roles.StudentSupport : Roles.Student), new Claim(ClaimTypes.Name, user.Fullname), new Claim(AuthHelpers.DebugModeClaim, "1") }, DefaultAuthenticationTypes.ApplicationCookie ); HttpContext.GetOwinContext().Authentication.SignIn( new AuthenticationProperties { IsPersistent = false }, identity ); return(RedirectAfterLogin()); }
public IDictionary <VotablePosition, ISet <DisplayNomineeEntry> > Fetch(IEnumerable <VotablePosition> positions) { var result = new Dictionary <VotablePosition, ISet <DisplayNomineeEntry> >(); foreach (VotablePosition position in positions) { HashSet <DisplayNomineeEntry> set = new HashSet <DisplayNomineeEntry>(); result[position] = set; foreach (NominationEntry entry in position.NominationEntries) { TimetableUserEntry user = _timetableUserRepository.GetByUsername(entry.Username); set.Add(new DisplayNomineeEntry(entry, user != null, user?.Fullname)); } } return(result); }
public ActionResult NewElectionEntry(NewElectionEntry model) { if (!ModelState.IsValid) { return(new HttpStatusCodeResult(HttpStatusCode.BadRequest)); } // STEP 1: Fetch everything we got as IDs Election election = _db.Elections.Find(model.ElectionId); TimetableUserEntry user = _userRepository.GetByUsername(model.UserId); // STEP 2: Check that everything is correct List <string> errors = new List <string>(); bool suggestReload = false; if (election == null) { errors.Add("Failed to find election"); suggestReload = true; } if (user == null) { errors.Add("Invalid student ID"); } if (user != null && !user.IsStudentActive) { errors.Add("Only active students can participate in elections"); } if (errors.Count > 0) { ViewBag.SuggestReload = suggestReload; string errorHtml = PartialView("_ErrorsDisplay", errors).RenderToString(); return(Json(new { Success = false, HumanErrorHtml = errorHtml })); } // STEP 3: Check that there isn't an entry already for this user // ReSharper disable PossibleNullReferenceException if (_db.ElectionEligibilityEntries.Find(user.UserId, election.Id) != null) { return(Json(new { Success = false, HumanError = "Error: there is already an entry for this user" })); } // STEP 4: Create a new entity and save it ElectionEligibilityEntry newEntry = new ElectionEligibilityEntry() { Username = model.UserId, Election = election }; _db.ElectionEligibilityEntries.Add(newEntry); _db.SaveChanges(); // STEP 5: Prepare the displayed table row ElectionTableRow tableRow = new ElectionTableRow { UserId = user.UserId }; PopulateUserInfo(tableRow, user); // STEP 6: Reply that we are done return(Json(new { Success = true, Entry = tableRow })); }
private static void PopulateUserInfo(ITableRow row, TimetableUserEntry userEntry) { row.UserFullName = userEntry.Fullname; }
public ActionResult NewPositionEntry(NewPositionEntry model) { if (!ModelState.IsValid) { return(new HttpStatusCodeResult(HttpStatusCode.BadRequest)); } // STEP 1: Fetch everything we got as IDs Election election = _db.Elections.Find(model.ElectionId); VotablePosition position = _db.VotablePositions.Find(model.PositionId); TimetableUserEntry user = _userRepository.GetByUsername(model.UserId); // STEP 2: Check that everything is correct List <string> errors = new List <string>(); bool suggestReload = false; if (election == null) { errors.Add("Failed to find election"); suggestReload = true; } if (position == null) { errors.Add("Failed to find the selected position"); suggestReload = true; } if (election != null && position != null && position.ElectionId != election.Id) { errors.Add("The selected position doesn't belong to current election"); suggestReload = true; } if (user == null) { errors.Add("Invalid student ID"); } if (user != null && !user.IsStudentActive) { errors.Add("Only active students can participate in elections"); } if (errors.Count > 0) { ViewBag.SuggestReload = suggestReload; string errorHtml = PartialView("_ErrorsDisplay", errors).RenderToString(); return(Json(new { Success = false, HumanErrorHtml = errorHtml })); } // STEP 3: Check that there isn't an entry already for this user-position tuple bool isDuplicateEntry = _db.PositionEligibilityEntries .Any(entry => entry.PositionId == position.Id && entry.Username == user.UserId); if (isDuplicateEntry) { return(Json(new { Success = false, HumanError = "Error: there is already an entry for this user-position tuple. Please edit that instead" })); } // STEP 4: Create a new entity and save it PositionEligibilityEntry newEntry = new PositionEligibilityEntry() { Username = model.UserId, Position = position, CanNominate = model.CanNominate, CanVote = model.CanVote }; _db.PositionEligibilityEntries.Add(newEntry); _db.SaveChanges(); // STEP 5: Prepare the displayed table row PositionsTableRow tableRow = new PositionsTableRow(newEntry); PopulateUserInfo(tableRow, user); // STEP 6: Reply that we are done return(Json(new { Success = true, Entry = tableRow })); }
public ActionResult LoginSso(string timetableToken, string returnUrl) { if (AuthHelpers.IsAuthenticated(User)) { return(RedirectAfterLogin(true)); } if (timetableToken == null) { // Store the return URL in session to use it when the user comes back if (!string.IsNullOrWhiteSpace(returnUrl)) { Session[ReturnUrlKey] = returnUrl; } return(RedirectToSso()); } if (!Guid.TryParse(timetableToken, out Guid tokenGuid)) { AuthHelpers.Logger.Information( "SSO Fail: failed to parse token GUID '{token}' from {UserHostAddress} ", timetableToken, Request.UserHostAddress ); return(FailCallback()); } TimetableDbContext timetableDb = new TimetableDbContext(); AuthToken token = timetableDb.AuthTokens.Find(tokenGuid); if (token == null || token.UserHostAddress != Request.UserHostAddress) { AuthHelpers.Logger.Information( "SSO Fail: Token {Guid} not found or UserHostAddress ({UserHostAddress}) doesn't match", tokenGuid, Request.UserHostAddress ); return(FailCallback()); } AuthSession session = timetableDb.AuthSessions.Find(token.SessionGuid); if (session == null || session.ExpiresAt < DateTime.Now) { AuthHelpers.Logger.Information( "SSO Fail: Session for token {Guid} not found or it has expired. UserHostAddress: {UserHostAddress}", tokenGuid, Request.UserHostAddress ); return(FailCallback()); } TimetableUserEntry user = new TimetableUserRepository(timetableDb) .GetByUsername(session.UserEmail); if (user == null || user.UserId != TimetableUserEntry.NormalizeUsernameToId(session.UserEmail)) { AuthHelpers.Logger.Information( "SSO Fail: Session for token {Guid} failed to match a timetable user. UserHostAddress: {UserHostAddress}", tokenGuid, Request.UserHostAddress ); return(FailCallback()); } // All good, sign in timetableDb.AuthTokens.Remove(token); timetableDb.SaveChanges(); ClaimsIdentity identity = new ClaimsIdentity( new[] { // These 2 are required for default antiforgery provider new Claim(ClaimTypes.NameIdentifier, user.UserId), new Claim( "http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider", "ASP.NET Identity", "http://www.w3.org/2001/XMLSchema#string" ), // Additional stuff new Claim(ClaimTypes.Role, user.IsStudentSupport ? Roles.StudentSupport : Roles.Student), new Claim(ClaimTypes.Name, user.Fullname), new Claim(AuthHelpers.TimetableSessionClaim, session.Guid.ToString()) }, DefaultAuthenticationTypes.ApplicationCookie ); Session[FailedSsoAttemptsKey] = 0; HttpContext.GetOwinContext().Authentication.SignIn( new AuthenticationProperties { IsPersistent = true // We validate on every request anyway, so prevent needless redirects }, identity ); AuthHelpers.Logger.Information( "SSO Success: token {Guid} was used for successful sign in by {UserId}. UserHostAddress: {UserHostAddress}", tokenGuid, user.UserId, Request.UserHostAddress ); return(RedirectAfterLogin()); }
public async Task <TimetableUserEntry> GetByUsernameAsync(string username) { string normalizedEmail = TimetableUserEntry.NormalizeUsernameToId(username) + "@example.com"; return(await db.Users.FirstOrDefaultAsync(e => e.InternalEmail == normalizedEmail)); }
public TimetableUserEntry GetByUsername(string username) { string normalizedEmail = TimetableUserEntry.NormalizeUsernameToId(username) + "@example.com"; return(db.Users.FirstOrDefault(e => e.InternalEmail == normalizedEmail)); }
/// <summary> /// Configures the authentication system /// </summary> private static void ConfigureAuth(IAppBuilder app) { if (AppAuthConfiguration.Get().DebugMode) { // Fake login page with username only app.UseCookieAuthentication(new CookieAuthenticationOptions { AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie, LoginPath = new PathString($"/{AuthHelpers.LoginPath}"), Provider = new CookieAuthenticationProvider { OnValidateIdentity = context => { if (context.Identity.Claims.All(claim => claim.Type != AuthHelpers.DebugModeClaim)) { context.RejectIdentity(); context.OwinContext.Authentication.SignOut(context.Options.AuthenticationType); } return(Task.CompletedTask); } } }); } else { // Real SSO mode app.UseCookieAuthentication(new CookieAuthenticationOptions { AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie, LoginPath = new PathString($"/{AuthHelpers.LoginPath}"), ExpireTimeSpan = TimeSpan.FromDays(30), SlidingExpiration = true, Provider = new CookieAuthenticationProvider { OnValidateIdentity = async context => { if (context.Identity.Claims.Any(claim => claim.Type == AuthHelpers.DebugModeClaim)) { context.RejectIdentity(); context.OwinContext.Authentication.SignOut(context.Options.AuthenticationType); return; } Claim sessionGuidClaim = context.Identity.Claims .FirstOrDefault(claim => claim.Type == AuthHelpers.TimetableSessionClaim); if (sessionGuidClaim == null) { context.RejectIdentity(); context.OwinContext.Authentication.SignOut(context.Options.AuthenticationType); return; } TimetableDbContext timetableDb = new TimetableDbContext(); AuthSession session = await timetableDb.AuthSessions.FindAsync(new Guid(sessionGuidClaim.Value)); if (session == null || session.ExpiresAt < DateTime.Now) { context.RejectIdentity(); context.OwinContext.Authentication.SignOut(context.Options.AuthenticationType); return; } TimetableUserEntry user = await new TimetableUserRepository(timetableDb) .GetByUsernameAsync(session.UserEmail); if (user == null || user.UserId != context.Identity.GetUserId()) { context.RejectIdentity(); context.OwinContext.Authentication.SignOut(context.Options.AuthenticationType); } } } }); } }