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());
        }
예제 #4
0
        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());
        }
예제 #9
0
        public async Task <TimetableUserEntry> GetByUsernameAsync(string username)
        {
            string normalizedEmail = TimetableUserEntry.NormalizeUsernameToId(username) + "@example.com";

            return(await db.Users.FirstOrDefaultAsync(e => e.InternalEmail == normalizedEmail));
        }
예제 #10
0
        public TimetableUserEntry GetByUsername(string username)
        {
            string normalizedEmail = TimetableUserEntry.NormalizeUsernameToId(username) + "@example.com";

            return(db.Users.FirstOrDefault(e => e.InternalEmail == normalizedEmail));
        }
예제 #11
0
        /// <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);
                            }
                        }
                    }
                });
            }
        }