Exemple #1
0
        /// <summary>
        ///     Processes all payroll for all organizations system-wide. Should run on the 1st of every month.
        /// </summary>
        public static void ProcessMonthly()
        {
            DateTime today = DateTime.UtcNow;

            string lastRun = Persistence.Key["LastSalaryRun"];

            string expectedLastRun = today.ToString("yyyyMM", CultureInfo.InvariantCulture);

            if (lastRun != null &&
                String.Compare(lastRun, expectedLastRun, CultureInfo.InvariantCulture, CompareOptions.IgnoreCase) >= 0)
            {
                // nothing to do, return

                return;
            }

            Persistence.Key["LastSalaryRun"] = expectedLastRun;

            // Process the payroll for all organizations. Assume payday is 25th.
            // TODO: Different payday per organization?

            Payroll  payroll = GetAll();
            DateTime payday  = new DateTime(today.Year, today.Month, 25);

            foreach (PayrollItem payrollItem in payroll)
            {
                Salary salary = Salary.Create(payrollItem, payday);
                SwarmopsLog.CreateEntry(payrollItem.Person, new SalaryCreatedLogEntry(salary));

                // TODO: CREATE SALARY SPECIFICATION, SEND TO PERSON
            }
        }
Exemple #2
0
        protected void ButtonSubmit_Click(object sender, EventArgs e)
        {
            DateTime dateOfBirth = new DateTime(1800, 1, 1);  // null equivalent

            if (this.TextDateOfBirth.Text.Length > 0)
            {
                dateOfBirth = DateTime.Parse(this.TextDateOfBirth.Text);
            }

            string street = this.TextStreet1.Text;

            if (!string.IsNullOrEmpty(this.TextStreet2.Text))
            {
                street += "|" + this.TextStreet2.Text;
            }

            Person newPerson = Person.Create(this.TextName.Text, this.TextMail.Text, string.Empty, this.TextPhone.Text,
                                             street, this.TextPostal.Text, this.TextCity.Text, this.DropCountries.SelectedValue, dateOfBirth,
                                             (PersonGender)Enum.Parse(typeof(PersonGender), this.DropGenders.SelectedValue));

            DateTime            participationExpiry = Constants.DateTimeHigh;
            ParticipantMailType welcomeMailType     = ParticipantMailType.ParticipantAddedWelcome_NoExpiry;

            int participationDurationMonths = Int32.Parse(CurrentOrganization.Parameters.ParticipationDuration);

            if (participationDurationMonths < 1000)
            {
                participationExpiry = DateTime.Today.AddMonths(participationDurationMonths);
                welcomeMailType     = ParticipantMailType.ParticipantAddedWelcome;
            }

            Participation newParticipation = Participation.Create(newPerson, CurrentOrganization, participationExpiry);

            OutboundComm.CreateParticipantMail(welcomeMailType, newParticipation, CurrentUser);

            newPerson.LastLogonOrganizationId = CurrentOrganization.Identity;

            SwarmopsLogEntry logEntry = SwarmopsLog.CreateEntry(newPerson,
                                                                new Swarmops.Logic.Support.LogEntries.PersonAddedLogEntry(newParticipation, CurrentUser));

            logEntry.CreateAffectedObject(newParticipation);
            logEntry.CreateAffectedObject(CurrentUser);

            // Clear form and make way for next person

            this.TextName.Text             = string.Empty;
            this.TextStreet1.Text          = string.Empty;
            this.TextStreet2.Text          = string.Empty;
            this.TextMail.Text             = string.Empty;
            this.TextPhone.Text            = string.Empty;
            this.TextPostal.Text           = string.Empty;
            this.TextCity.Text             = string.Empty;
            this.TextDateOfBirth.Text      = string.Empty;
            this.DropGenders.SelectedValue = "Unknown";

            this.TextName.Focus();
            this.LiteralLoadAlert.Text = Resources.Pages.Swarm.AddPerson_PersonSuccessfullyRegistered;
        }
        public static AjaxCallResult Commence(int personId)
        {
            AuthenticationData authData = GetAuthenticationDataAndCulture();

            if (!authData.Authority.HasSystemAccess())
            {
                // Restrict impersonation to system-level access for now: it's a debugging tool

                return(new AjaxCallResult
                {
                    Success = false,
                    DisplayMessage = CommonV5.JavascriptEscape(Resources.Pages.Admin.CommenceImpersonation_Failed)
                });
            }

            // BEGIN IMPERSONATION

            Person impersonatedPerson = Person.FromIdentity(personId);

            SwarmopsLogEntry newEntry = SwarmopsLog.CreateEntry(impersonatedPerson,
                                                                new ImpersonationLogEntry {
                ImpersonatorPersonId = authData.CurrentUser.PersonId, Started = true
            });

            newEntry.CreateAffectedObject(authData.CurrentUser); // link impersonator to log entry for searchability

            // Someone who has system level access can always impersonate => no further access control at this time

            // SECURITY CONSIDERATIONS: If somebody replaces/fires a superior? Trivially undoable at the database level

            DateTime  utcNow = DateTime.UtcNow;
            Authority impersonatingAuthority = Authority.FromLogin(impersonatedPerson, authData.CurrentOrganization);

            impersonatingAuthority.Impersonation = new Impersonation
            {
                ImpersonatedByPersonId = authData.CurrentUser.PersonId,
                ImpersonationStarted   = utcNow
            };

            FormsAuthentication.SetAuthCookie(impersonatingAuthority.ToEncryptedXml(), false);
            HttpContext.Current.Response.AppendCookie(new HttpCookie("DashboardMessage", CommonV5.JavascriptEscape(String.Format(Resources.Pages.Admin.CommenceImpersonation_Success, utcNow))));
            return(new AjaxCallResult {
                Success = true
            });
        }
Exemple #4
0
        protected void ButtonSubmit_Click(object sender, EventArgs e)
        {
            DateTime dateOfBirth = new DateTime(1800, 1, 1);  // null equivalent

            if (this.TextDateOfBirth.Text.Length > 0)
            {
                dateOfBirth = DateTime.Parse(this.TextDateOfBirth.Text);
            }

            string street = this.TextStreet1.Text;

            if (!string.IsNullOrEmpty(this.TextStreet2.Text))
            {
                street += "|" + this.TextStreet2.Text;
            }

            Person newPerson = Person.Create(this.TextName.Text, this.TextMail.Text, string.Empty, this.TextPhone.Text,
                                             street, this.TextPostal.Text, this.TextCity.Text, this.DropCountries.SelectedValue, dateOfBirth,
                                             (PersonGender)Enum.Parse(typeof(PersonGender), this.DropGenders.SelectedValue));

            Membership newMembership = Membership.Create(newPerson, CurrentOrganization, DateTime.Today.AddYears(1));

            OutboundComm.CreateMembershipLetter(ParticipantMailType.MemberAddedWelcome, newMembership, CurrentUser);

            SwarmopsLogEntry logEntry = SwarmopsLog.CreateEntry(newPerson,
                                                                new Swarmops.Logic.Support.LogEntries.PersonAddedLogEntry(newMembership, CurrentUser));

            logEntry.CreateAffectedObject(newMembership);
            logEntry.CreateAffectedObject(CurrentUser);

            // Clear form and make way for next person

            this.TextName.Text             = string.Empty;
            this.TextStreet1.Text          = string.Empty;
            this.TextStreet2.Text          = string.Empty;
            this.TextMail.Text             = string.Empty;
            this.TextPhone.Text            = string.Empty;
            this.TextPostal.Text           = string.Empty;
            this.TextCity.Text             = string.Empty;
            this.TextDateOfBirth.Text      = string.Empty;
            this.DropGenders.SelectedValue = "Unknown";

            this.TextName.Focus();
            this.LiteralLoadAlert.Text = Resources.Pages.Swarm.AddPerson_PersonSuccessfullyRegistered;
        }
        public static bool RequestTicket(string mailAddress)
        {
            mailAddress = mailAddress.Trim();

            if (string.IsNullOrEmpty(mailAddress))
            {
                return(false); // this is the only case when we return false: a _syntactically_invalid_ address
            }

            People concernedPeople = People.FromMail(mailAddress);  // Should result in exactly 1

            if (concernedPeople.Count != 1)
            {
                return(true); // TODO: Prevent registration with duplicate mail addy, or this will cause problems down the road
            }

            Person concernedPerson = concernedPeople[0];

            if (!string.IsNullOrEmpty(concernedPerson.BitIdAddress))
            {
                // Cannot reset password - two factor auth is enabled. Manual intervention required.

                OutboundComm.CreateSecurityNotification(concernedPerson, null, null, string.Empty,
                                                        NotificationResource.Password_CannotReset2FA);
                return(true); // still returning true - the fail info is in mail only
            }


            string resetTicket = SupportFunctions.GenerateSecureRandomKey(16);

            resetTicket = resetTicket.Substring(0, 21);                                                                                   // We're using a 21-character (84-bit) key mostly for UI consistency with the ticket sent in mail, and it's secure enough

            concernedPerson.ResetPasswordTicket = DateTime.UtcNow.AddHours(1).ToString(CultureInfo.InvariantCulture) + "," + resetTicket; // Adds expiry - one hour

            OutboundComm.CreateSecurityNotification(concernedPerson, null, null, resetTicket,
                                                    NotificationResource.Password_ResetOnRequest);

            SwarmopsLog.CreateEntry(null,
                                    new PasswordResetRequestLogEntry(concernedPerson, SupportFunctions.GetRemoteIPAddressChain()));

            return(true);
        }
Exemple #6
0
        public static AjaxCallResult TerminateImpersonation()
        {
            AuthenticationData authData = GetAuthenticationDataAndCulture();

            if (!authData.Authority.ImpersonationActive)
            {
                return(new AjaxCallResult {
                    Success = false
                });                                          // no impersonation active. Race condition?
            }

            int    realUserPersonId = authData.Authority.Impersonation.ImpersonatedByPersonId;
            Person impersonator     = Person.FromIdentity(realUserPersonId);

            // Terminate impersonation and set new authority cookie from the impersonator data.
            // VERY SECURITY SENSITIVE: The identity as impersonator will be the new user.

            // TODO: LOG LOG LOG LOG

            SwarmopsLogEntry logEntry = SwarmopsLog.CreateEntry(authData.CurrentUser,
                                                                new ImpersonationLogEntry
            {
                ImpersonatorPersonId = impersonator.Identity,
                Started = false
            });

            logEntry.CreateAffectedObject(impersonator); // link impersonator to log entry for searchability

            DateTime utcNow = DateTime.UtcNow;

            Authority authority =
                Authority.FromLogin(impersonator, authData.CurrentOrganization);

            FormsAuthentication.SetAuthCookie(authority.ToEncryptedXml(), false);
            HttpContext.Current.Response.AppendCookie(new HttpCookie("DashboardMessage", CommonV5.JavascriptEscape(String.Format(Resources.Pages.Admin.CommenceImpersonation_Ended, utcNow))));

            // returning Success will force a reload, resetting dashboard to original user

            return(new AjaxCallResult {
                Success = true
            });
        }
        public static AjaxCallResult SignupParticipant(string name, int organizationId, string mail, string password, string phone,
                                                       string street1, string street2, string postalCode, string city, string countryCode, string dateOfBirth,
                                                       int geographyId, bool activist, PersonGender gender, int[] positionIdsVolunteer)
        {
            CommonV5.CulturePreInit(HttpContext.Current.Request);  // Set culture, for date parsing

            if (geographyId == 0)
            {
                geographyId = Geography.RootIdentity; // if geo was undetermined, set it to "Global"
            }

            Organization organization      = Organization.FromIdentity(organizationId);
            DateTime     parsedDateOfBirth = new DateTime(1800, 1, 1); // Default if unspecified

            if (dateOfBirth.Length > 0)
            {
                parsedDateOfBirth = DateTime.Parse(dateOfBirth);
            }

            Person newPerson = Person.Create(name, mail, password, phone, street1 + "\n" + street2.Trim(), postalCode,
                                             city, countryCode, parsedDateOfBirth, gender);
            Participation participation = newPerson.AddParticipation(organization, DateTime.UtcNow.AddYears(1));    // TODO: set duration from organization settings of Participantship

            // TODO: SEND NOTIFICATIONS

            // Log the signup

            SwarmopsLog.CreateEntry(newPerson, new PersonAddedLogEntry(participation, newPerson));

            // Create notification

            OutboundComm.CreateParticipantNotification(newPerson, newPerson, organization,
                                                       NotificationResource.Participant_Signup);

            // Add the bells and whistles

            if (activist)
            {
                newPerson.CreateActivist(false, false);
            }

            if (positionIdsVolunteer.Length > 0)
            {
                Volunteer volunteer = newPerson.CreateVolunteer();
                foreach (int positionId in positionIdsVolunteer)
                {
                    Position position = Position.FromIdentity(positionId);
                    volunteer.AddPosition(position);
                    SwarmopsLog.CreateEntry(newPerson, new VolunteerForPositionLogEntry(newPerson, position));
                }
            }

            newPerson.LastLogonOrganizationId = organizationId;

            // Create a welcome message to the Dashboard

            HttpContext.Current.Response.AppendCookie(new HttpCookie("DashboardMessage", CommonV5.JavascriptEscape(String.Format(Resources.Pages.Public.Signup_DashboardMessage, organization.Name))));

            // Set authentication cookie, which will log the new person in using the credentials just given

            FormsAuthentication.SetAuthCookie(Authority.FromLogin(newPerson).ToEncryptedXml(), true);

            AjaxCallResult result = new AjaxCallResult {
                Success = true
            };

            return(result);
        }
        public static bool PerformReset(string mailAddress, string ticket, string newPassword)
        {
            People people = People.FromMail(mailAddress.Trim());

            if (people.Count != 1)
            {
                return(false);
            }

            Person resetPerson = people[0];

            string[] resetData = resetPerson.ResetPasswordTicket.Split(',');

            if (resetData.Length != 2)
            {
                return(false); // invalid data or no ticket
            }

            // This may throw on invalid data, which will give a slightly different error but that's fine too for now.
            DateTime ticketExpiresUtc = DateTime.Parse(resetData[0]);

            if (DateTime.UtcNow > ticketExpiresUtc)
            {
                // Ticket expired.
                return(false);
            }

            if (ticket != resetData[1])
            {
                // Bad ticket.
                return(false);
            }

            // When we get here, all checks to reset the password have completed. Change the password, log the change,
            // notify the user that the password was changed, set a new authentication cookie, and have the web page
            // redirect to Dashboard (by returning true).

            // Clear password reset ticket

            resetPerson.ResetPasswordTicket = string.Empty;

            // Create lockdown code, notify

            string lockdownTicket = SupportFunctions.GenerateSecureRandomKey(16);

            OutboundComm.CreateSecurityNotification(resetPerson, null, null, lockdownTicket,
                                                    NotificationResource.Password_Changed);
            resetPerson.AccountLockdownTicket = DateTime.UtcNow.AddDays(1).ToString(CultureInfo.InvariantCulture) + "," +
                                                lockdownTicket;

            // Set new password

            resetPerson.SetPassword(newPassword);

            // Log the password reset

            SwarmopsLog.CreateEntry(resetPerson,
                                    new PasswordResetLogEntry(resetPerson, SupportFunctions.GetRemoteIPAddressChain()));

            // Set authentication cookies

            int lastOrgId = resetPerson.LastLogonOrganizationId;

            if (lastOrgId == 0)
            {
                lastOrgId = Organization.SandboxIdentity;
            }

            if (!resetPerson.ParticipatesInOrganizationOrParent(lastOrgId))
            {
                // If the person doesn't have access to the last organization (anymore), log on to Sandbox

                lastOrgId = 1;
            }

            // Set cookies

            FormsAuthentication.SetAuthCookie(Authority.FromLogin(resetPerson).ToEncryptedXml(), true);
            DashboardMessage.Set(Resources.Pages.Security.ResetPassword_Success);

            return(true); // temp

            // do NOT NOT NOT trim password - this is deliberate. Passwords starting/ending in whitespace must be possible
        }
Exemple #9
0
        public static AjaxInputCallResult SetPersonEditorData(int personId, string field, string newValue)
        {
            if (newValue == null || field == null)
            {
                throw new ArgumentNullException();
            }

            AuthenticationData authData = GetAuthenticationDataAndCulture();
            bool self = false;

            // Are we modifying ourselves?

            if (personId == 0)   // request self record
            {
                self     = true; // may make use of this later
                personId = authData.CurrentUser.Identity;
            }

            // Preliminary input validation

            if (string.IsNullOrEmpty(newValue))
            {
                if (field != "TwitterId") // These fields may be set to empty; default is disallow
                {
                    return(new AjaxInputCallResult
                    {
                        Success = false,
                        ObjectIdentity = personId,
                        DisplayMessage = Resources.Global.Global_FieldCannotBeEmpty,
                        FailReason = AjaxInputCallResult.ErrorInvalidFormat,
                        NewValue = GetPersonValue(personId, field)
                    });
                }
            }

            // Verify authority to see and change personal data

            Person affectedPerson = Person.FromIdentity(personId);

            if (!self)
            {
                if (!authData.Authority.CanSeePerson(affectedPerson) ||
                    !authData.Authority.HasAccess(new Access(authData.CurrentOrganization, affectedPerson.Geography,
                                                             AccessAspect.PersonalData)))
                {
                    throw new UnauthorizedAccessException();
                }
            }

            string oldValue;
            string displayMessage = string.Empty;

            while (newValue.Contains("  "))
            {
                newValue = newValue.Trim().Replace("  ", " ");  // double, triple, quadruple spaces reduced to one
            }

            switch (field)
            {
            case "Name":
                oldValue            = affectedPerson.Name;
                affectedPerson.Name = newValue;
                break;

            case "Mail":
                oldValue            = affectedPerson.Mail;
                affectedPerson.Mail = newValue;
                break;

            case "Phone":
                oldValue             = affectedPerson.Phone;
                affectedPerson.Phone = newValue;
                if (!Regex.IsMatch(newValue, @"^[0-9 \(\)\-\+]+$"))
                {
                    // using characters not typically seen in a phone number? Warn
                    displayMessage = Resources.Global.Master_EditPersonWarning_Phone;
                }
                break;

            case "TwitterId":
                if (newValue.StartsWith("@"))
                {
                    newValue = newValue.Substring(1);
                }
                oldValue = affectedPerson.TwitterId;
                affectedPerson.TwitterId = newValue;
                break;

            default:
                throw new ArgumentException("Unrecognized field in /Automation/SwarmFunctions.SetPersonEditorData");
            }

            SwarmopsLogEntry logEntry = SwarmopsLog.CreateEntry(affectedPerson, new PersonalDataChangedLogEntry
            {
                ActingPersonId   = authData.CurrentUser.PersonId,
                AffectedPersonId = affectedPerson.PersonId,
                Field            = field,
                IpAddress        = SupportFunctions.GetMostLikelyRemoteIPAddress(),
                OldValue         = oldValue,
                NewValue         = newValue
            });

            if (!self)
            {
                logEntry.CreateAffectedObject(authData.CurrentUser);
            }

            return(new AjaxInputCallResult
            {
                ObjectIdentity = personId,
                Success = true,
                NewValue = newValue,
                DisplayMessage = displayMessage
            });
        }