/// <summary> /// Request an autologin using the values from the HttpRequest if /// is need. /// </summary> public static void RequestAutologin(HttpRequest Request) { // First, check standard 'Authorization' header var auth = Request.Headers["Authorization"]; if (!String.IsNullOrEmpty(auth)) { var m = System.Text.RegularExpressions.Regex.Match(auth, "^LC alu=([^,]+),alk=(.+)$"); if (m.Success) { var alu = m.Groups[1].Value; var alk = m.Groups[2].Value; LcAuth.Autologin(alu, alk); } } else { // Using custom headers first, best for security using the REST API. var Q = Request.QueryString; var alk = N.DW(Request.Headers["alk"]) ?? Q["alk"]; var alu = N.DW(Request.Headers["alu"]) ?? Q["alu"]; // Autologin feature for anonymous sessions with autologin parameters on request if (!Request.IsAuthenticated && alk != null && alu != null) { // 'alk' url parameter stands for 'Auto Login Key' // 'alu' url parameter stands for 'Auto Login UserID' LcAuth.Autologin(alu, alk); } } }
public static void Logout() { LcAuth.RemovesCurrentUserAuthorization(); // Log out of the current user context WebSecurity.Logout(); Session.Clear(); }
public static void checkAccountIsConfirmed(string username) { // #454: User is auto-logged on registering, allowing it to do the Onboarding, // But next times, it is required to confirm email before logged. // Since we set IsConfirmed as true on database to let 'auto-logging on register', // we must check for the existance of a confirmation token: var userId = WebSecurity.GetUserId(username); using (var db = new LcDatabase()) { string token = LcAuth.GetConfirmationToken(userId); if (userId > -1 && !string.IsNullOrWhiteSpace(token)) { // Resend confirmation mail var confirmationUrl = LcUrl.LangUrl + "Account/Confirm/?confirmationCode=" + Uri.EscapeDataString(token ?? ""); var isProvider = (bool)(db.QueryValue("SELECT IsProvider FROM users WHERE UserID=@0", userId) ?? false); if (isProvider) { LcMessaging.SendWelcomeProvider(userId, username); } else { LcMessaging.SendWelcomeCustomer(userId, username); } /// http 409:Conflict throw new HttpException(409, "Your account has not yet been confirmed. Please check your inbox and spam folders and click on the e-mail sent."); } } }
/// <summary> /// Quick signup, just username/email and a password. /// /// IMPORTANT: It was used in a previous iteration of the project, now the detailed is in use and this lacks some of the latest /// additions, like client-confirmationCode code. /// </summary> /// <param name="page"></param> /// <returns></returns> public static LoginResult QuickSignup(WebPage page) { page.Validation.Add("password", new PasswordValidator()); page.Validation.RequireField("email", "You must specify an email."); // Username is an email currently, so need to be restricted page.Validation.Add("email", Validator.Regex(LcValidators.EmailAddressRegexPattern, "The email is not valid.")); page.Validation.RequireField("password", "You must specify a password."); if (page.Validation.IsValid()) { var username = Request.Form["email"]; var password = Request.Form["password"]; var rememberMe = Request.Form["rememberMe"].AsBool(); var returnProfile = Request.Form["returnProfile"].AsBool(); var profileTypeStr = Request.Form["profileType"] ?? ""; var isProvider = new string[] { "SERVICE-PROFESSIONAL", "FREELANCE", "FREELANCER", "PROVIDER" }.Contains(profileTypeStr.ToUpper()); var utm = Request.Url.Query; // If the user exists, try to log-in with the given password, // becoming a provider if that was the requested profileType and follow as // a normal login. // If the password didn't match, throw a sign-up specific error (email in use) // Otherwise, just register the user. if (LcAuth.ExistsEmail(username)) { // Try Login try { var logged = Login(username, password, rememberMe, returnProfile); // throw exception on error if (isProvider) { LcAuth.BecomeProvider(logged.userID); } return(logged); } catch (HttpException ex) { // Not valid log-in, throw a 'email exists' error with Conflict http code throw new HttpException(409, "Email address is already in use."); } } else { var registered = LcAuth.RegisterUser(username, "", "", password, isProvider, utm); LcAuth.SendRegisterUserEmail(registered); // Auto login: return(Login(username, password, rememberMe, returnProfile, true)); } } else { // Bad request, input data incorrect because of validation rules throw new HttpException(400, LcRessources.ValidationSummaryTitle); } }
public static LoginResult FacebookLogin(WebPage page, bool createAccount = false, bool isProvider = false) { var returnProfile = Request.Form["returnProfile"].AsBool(); // Get Facebook User using the Request["accessToken"], // or signed_request or cookie. var fbuser = LcFacebook.GetUserFromCurrentRequest(); var fuserid = fbuser != null ? ((string)fbuser["id"] ?? "0").AsLong() : 0; if (fuserid > 0) { // It exists? var user = LcAuth.GetFacebookUser(fuserid); if (user != null) { // Become provider if (createAccount && isProvider) { LcAuth.BecomeProvider(user.UserID); user.IsProvider = true; LcAuth.SendRegisterUserEmail(user); } } else { if (createAccount) { user = CreateFacebookAccount(fbuser, isProvider); } else { throw new HttpException(400, "[[[Incorrect user]]]"); } } // Performs system login, using the autologin info since // there is no password here. var ret = GetLoginResultForID(new LcAuth.UserAuthorization { userID = user.UserID, token = LcAuth.RegisterAuthorizationForUser(user.UserID) }, returnProfile); LcAuth.Autologin(ret.userID.ToString(), ret.authKey); return(ret); } else { throw new HttpException(500, "[[[Invalid Facebook credentials]]]"); } }
private static LcAuth.RegisteredUser CreateFacebookAccount(dynamic facebookUser, bool isProvider) { // Create user with Facebook var result = LcAuth.RegisterUser( facebookUser.email, facebookUser.first_name, facebookUser.last_name, LcAuth.GeneratePassword(), isProvider, Request.Url.Query //,facebookUser.gender ); LcAuth.ConnectWithFacebookAccount(result.userID, facebookUser.id); LcAuth.SendRegisterUserEmail(result); return(result); }
public static LoginResult Login(string username, string password, bool rememberMe = false, bool returnProfile = false, bool allowUnconfirmed = false) { // DISABLED CONFIRMATION CHECK FOR BETA PERIOD, BECAUSE WE HAVE NOT THE EMAIL CONFIRMATION READY // AFTER THE WHOLE SITE AND MESSAGING CHANGE. // TODO: RE-ENABLE AFTER BETA //if (!allowUnconfirmed) // checkAccountIsConfirmed(username); if (LcAuth.Login(username, password, rememberMe)) { var userId = WebSecurity.GetUserId(username); return(GetLoginResultForID(userId, returnProfile)); } else { throw new HttpException(400, "Incorrect username or password."); } }
private static LoginResult GetLoginResultForID(int userID, bool returnProfile) { var authKey = LcAuth.GetAutologinKey(userID); LcRest.UserProfile profile = null; if (returnProfile) { profile = LcRest.UserProfile.Get(userID); } return(new LoginResult { redirectUrl = getRedirectUrl(userID), userID = userID, authKey = authKey, profile = profile, onboardingStep = profile == null ? null : profile.onboardingStep }); }
static UserAuthorization GetUserAuthorizationFromQueryString(System.Collections.Specialized.NameValueCollection queryString) { // #827 Simple token Authorization header var token = queryString["access_token"]; if (!String.IsNullOrEmpty(token)) { var userID = LcAuth.GetUserIdByAuthorizationToken(token); if (userID.HasValue) { return(new UserAuthorization { userID = userID.Value, token = token }); } } return(null); }
/// <summary> /// Request an autologin using the values from the HttpRequest if /// is need. /// </summary> public static void RequestAutologin(HttpRequest Request) { // First, check standard 'Authorization' header var auth = Request.Headers["Authorization"]; if (!String.IsNullOrEmpty(auth)) { // First: support standard authorization if (!StartSessionWithAuthorizationHeader(auth)) { // Legacy 'header based on autologin scheme' feature as fallback (to be removed as soon as Apps using it are updated) var m = System.Text.RegularExpressions.Regex.Match(auth, "^LC alu=([^,]+),alk=(.+)$"); if (m.Success) { var alu = m.Groups[1].Value; var alk = m.Groups[2].Value; LcAuth.Autologin(alu, alk); } } } else { var Q = Request.QueryString; // First: support standard authorization if (!StartSessionWithAuthorizationQueryString(Q)) { // Legacy 'autologin' feature as fallback (to be removed as soon as Apps and emails using it are updated) // Using custom headers first, best for security using the REST API. var alk = N.DW(Request.Headers["alk"]) ?? Q["alk"]; var alu = N.DW(Request.Headers["alu"]) ?? Q["alu"]; // Autologin feature for anonymous sessions with autologin parameters on request if (!Request.IsAuthenticated && alk != null && alu != null) { // 'alk' url parameter stands for 'Auto Login Key' // 'alu' url parameter stands for 'Auto Login UserID' LcAuth.Autologin(alu, alk); } } } }
private static LoginResult GetLoginResultForID(LcAuth.UserAuthorization authorization, bool returnProfile) { var authKey = LcAuth.GetAutologinKey(authorization.userID); LcRest.UserProfile profile = null; if (returnProfile) { profile = LcRest.UserProfile.Get(authorization.userID); } return(new LoginResult { redirectUrl = getRedirectUrl(authorization.userID), userID = authorization.userID, authKey = authKey, authToken = authorization.token, //authorization = authorization, profile = profile, onboardingStep = profile == null ? null : profile.onboardingStep }); }
static UserAuthorization GetUserAuthorizationFromHeader(string authorizationHeaderValue) { if (String.IsNullOrWhiteSpace(authorizationHeaderValue)) { return(null); } var tokenMatch = System.Text.RegularExpressions.Regex.Match(authorizationHeaderValue, "^Bearer (.+)$"); if (tokenMatch.Success) { var token = tokenMatch.Groups[1].Value; var userID = LcAuth.GetUserIdByAuthorizationToken(token); if (userID.HasValue) { return(new UserAuthorization { userID = userID.Value, token = token }); } } return(null); }
/// <summary> /// Process a request to create a user job title given a jobTitleID or as /// fallback a validated and sanitized jobTitleName (pass in GetValidatedJobTitleName result) /// </summary> /// <param name="userID"></param> /// <param name="jobTitleID"></param> /// <param name="jobTitleName"></param> /// <returns></returns> public dynamic Create(int userID, int jobTitleID, string jobTitleName) { var jobTitleExists = false; if (jobTitleID == 0) { // Look-up/new-job-title version: it's possible that the user wrotes a // job title name without pick-up one from the list, we look-up for that in database // for a jobTitleID, // and may not exists, so we try to create a new one with a template. // Name for the job title is required if (String.IsNullOrEmpty(jobTitleName)) { throw new HttpException(400, "A Job Title is required"); } // Search var jobTitle = LcRest.PublicJobTitle.AutocompleteSearch(jobTitleName, LcRest.Locale.Current).FirstOrDefault(); if (jobTitle != null) { // Use the first one jobTitleID = jobTitle.value; jobTitleExists = true; } else { // Create a new job-title based on the given name #650 jobTitleID = LcData.JobTitle.CreateJobTitleByName(jobTitleName, LcRest.Locale.Current.languageID, LcRest.Locale.Current.countryID, userID); // Check well know custom error codes if (jobTitleID == -1) { throw new HttpException(400, String.Format("The Job Title '{0}' is not allowed.", jobTitleName)); } LcMessaging.NotifyNewJobTitle(jobTitleName, jobTitleID); jobTitleExists = true; } } else { // Double check that the job title exists jobTitleExists = LcRest.PublicJobTitle.Get(jobTitleID, LcRest.Locale.Current) != null; } if (jobTitleID > 0 && jobTitleExists) { // Read data; It stops on not valid: var data = GetValidatedItemBodyInput(); LcData.JobTitle.InsertUserJobTitle( userID, jobTitleID, data.policyID, data.intro, data.instantBooking, data.collectPaymentAtBookMeButton, LcRest.Locale.Current.languageID, LcRest.Locale.Current.countryID ); // If user is just a client, needs to become a professional var user = LcRest.UserProfile.Get(userID); if (!user.isServiceProfessional) { LcAuth.BecomeProvider(userID); // Set onboarding step as done for 'add job titles' to avoid display that screen again to the user: LcData.UserInfo.SetOnboardingStep(userID, "addJobTitles"); // Send email as provider LcMessaging.SendWelcomeProvider(userID, WebSecurity.CurrentUserName); } } else { throw new HttpException(404, "Job Title not found or disapproved"); } return(GetItem(userID, jobTitleID)); }
/// <summary> /// Convert a user record with 'Not Enabled Account' into a standard enabled account. See IsUserButNotEnabledAccount /// for more info, and check that value before call this to prevent an error when user has an enabled account. /// This supports the cases /// - User with status of 'serviceProfessionalClient': the user has an account created as client by a service professional. /// - User with status of 'subscriber': the user submitted it's email through a Lead Generation API to get in touch with newsletters or /// to reference a service professional. /// /// For the conversion, we need support next actions/requests: /// - A: We need to communicate that specific situation (error message), generate a confirmation code /// for the existent user, send email to let him to confirm that he/she owns the given e-mail. /// - B: On returning here after request/response A, a confirmation code is being provided and we must proceed /// by checking the confirmation code and, on success, enable account (change status), update the membership password and /// continue with a valid set of LoginResult. External code should allow user to update any additional account data. /// </summary> /// <param name="userID"></param> /// <param name="email"></param> /// <param name="password"></param> /// <param name="returnProfile"></param> /// <returns></returns> private static LoginResult SignupANotEnabledAccount(int userID, string email, string password, bool returnProfile, int accountStatusID, bool isOrganization) { // Get confirmation code, if any var confirmationCode = Request["confirmationCode"]; // Prepare error message var errTpl = ""; if (accountStatusID == (int)LcEnum.AccountStatus.serviceProfessionalClient) { errTpl = UserIsServiceProfessionalClientMessage; } else if (accountStatusID == (int)LcEnum.AccountStatus.subscriber) { errTpl = UserIsSubscriberMessage; } else { throw new Exception("[[[Not allowed]]]"); } var errMsg = String.Format(errTpl, email); // Action/Request A: Create confirmation code if (String.IsNullOrEmpty(confirmationCode)) { // To generate a confirmation code (creates the Membership record, that does not exists still) // this needs a random password (we still didn't verified the user, so do NOT trust on the given password). // NOTE: since this can be attempted several time by the user, and next attempts will fail because the Membership // record will exists already, just double check and try creation only if record don't exists: if (!LcAuth.HasMembershipRecord(userID)) { WebSecurity.CreateAccount(email, LcAuth.GeneratePassword(), true); } StartOnboardingForUser(userID); // send email to let him to confirm it owns the given e-mail LcMessaging.SendWelcomeCustomer(userID, email); // Not valid after all, just communicate was was done and needs to do to active its account: throw new HttpException(409, errMsg); } // Action/Request B: confirm confirmation code else { // If confirmation token is valid, enable account and reset password if (LcAuth.GetConfirmationToken(userID) == confirmationCode) { // We know is valid, we can update the accountStatus to be an standard/enabled account // and that will allow to set the account as confirmed using (var db = new LcDatabase()) { db.Execute("UPDATE users SET accountStatusID = @1 WHERE UserID = @0", userID, LcEnum.AccountStatus.active); } // now we can confirm (we already know the code is valid, it will just double check and update database) LcAuth.ConfirmAccount(confirmationCode); // set the password provided by the user. Trick: we need to generate a reset token in order to set the password. var token = WebSecurity.GeneratePasswordResetToken(email); LcAuth.ResetPassword(token, password); // Left continue with profile data update.. } else { // RE-send email to let him to confirm it owns the given e-mail LcMessaging.SendWelcomeCustomer(userID, email); throw new HttpException(409, errMsg); } } // We need a logged object, and additionally a double check is performed (so we ensure setting the password process worked). return(Login(email, password, false, returnProfile, false)); }
/// <summary> /// Signup with fields: /// - email [required] /// - password [required when no facebookUserID is given] /// - facebookUserID [optional] /// - countryID [optional defaults to COUNTRY_CODE_USA] /// - profileType [optional defaults to client] /// - utm [optional, not a named form parameter but the whole query string] /// - firstName [optional except atBooking] /// - lastName [optional except atBooking] /// - phone [optional except atBooking] /// - returnProfile [optional defaults to false] Returns the user profile in a property of the result /// - atBooking [optional] /// - isOrganization [optional] Default false /// </summary> /// <param name="page"></param> /// <returns></returns> public static LoginResult Signup(WebPage page) { page.Validation.RequireField("email", "[[[You must specify an email.]]]"); // Username is an email currently, so need to be restricted page.Validation.Add("email", Validator.Regex(LcValidators.EmailAddressRegexPattern, "[[[The email is not valid.]]]")); // First data var profileTypeStr = Request.Form["profileType"] ?? ""; var isServiceProfessional = SERVICE_PROFESSIONAL_TYPE == profileTypeStr.ToUpper(); var isClient = !isServiceProfessional; var facebookUserID = Request.Form["facebookUserID"].AsLong(0); var facebookAccessToken = Request.Form["facebookAccessToken"]; var email = Request.Form["email"]; var atBooking = Request.Form["atBooking"].AsBool(); var isOrganization = Request.Form["isOrganization"].AsBool(); // // Conditional validations // Facebook var useFacebookConnect = facebookUserID > 0 && !String.IsNullOrEmpty(facebookAccessToken); if (!useFacebookConnect) { page.Validation.RequireField("password", "[[[You must specify a password.]]]"); // We manually validate if a password was given, in order to prevent // showing up the validation format message additionally to the 'required password' message if (!String.IsNullOrWhiteSpace(Request.Form["password"])) { page.Validation.Add("password", new PasswordValidator()); } } else { var prevFbUser = LcAuth.GetFacebookUser(facebookUserID); if (prevFbUser != null) { throw new HttpException(409, "[[[Facebook account already connected. Sign in.]]]"); } } // For a signup at a client booking, we require more fields if (atBooking) { page.Validation.RequireField("phone", "[[[You must specify your mobile phone number.]]]"); page.Validation.RequireField("firstName", "[[[You must specify your first name.]]]"); page.Validation.RequireField("lastName", "[[[You must specify your last name.]]]"); } if (page.Validation.IsValid()) { // TODO To use countryCode for a more 'open' public REST API, where 'code' is a well know ISO 2-letters CODE //var countryCode = Request.Form["countryCode"] ?? "US"; var countryID = Request.Form["countryID"].AsInt(COUNTRY_CODE_AU); // Autogenerated password (we need to save one) on facebook connect: var password = useFacebookConnect ? LcAuth.GeneratePassword() : Request.Form["password"]; var firstName = Request.Form["firstName"]; var lastName = Request.Form["lastName"]; var phone = Request.Form["phone"]; var returnProfile = Request.Form["returnProfile"].AsBool(); var utm = Request.Url.Query; LoginResult logged = null; // If the user exists, try to log-in with the given password, // becoming a provider if that was the requested profileType and follow as // a normal login. // If the password didn't match, throw a sign-up specific error (email in use) // Otherwise, just register the user. if (LcAuth.ExistsEmail(email)) { // We query the user with that email var userID = WebSecurity.GetUserId(email); var user = LcRest.UserProfile.Get(userID); // There are special cases when a user is registered, but never has accepted TOU or created a password (Not Enabled Account), // and is possible for that user to become an regular/enabled account. if (IsUserButNotEnabledAccount(user)) { logged = SignupANotEnabledAccount(userID, email, password, returnProfile, user.accountStatusID, isOrganization); } else { // If the email exists, we try to log-in using the provided password, to don't bother with "e-mail in use" error // if the user provides the correct credentials (maybe just don't remember he/she has already an account; make it easy for them // to return). // Try Login try { logged = Login(email, password, false, returnProfile, true); userID = logged.userID; // Ensure we set-up // as a professional if requested // Next code will throw exception on error if (isServiceProfessional) { LcAuth.BecomeProvider(userID); } } catch (HttpException) { // Not valid log-in, throw a 'email exists' error with Conflict http code throw new HttpException(409, "[[[E-mail address is already in use.]]]"); } } // Update account data with the extra information. using (var db = new LcDatabase()) { db.Execute(@" UPDATE users SET firstName = coalesce(@1, firstName), lastName = coalesce(@2, lastName), mobilePhone = coalesce(@3, mobilePhone), isOrganization = @4 WHERE userID = @0 ", userID, firstName, lastName, phone, isOrganization); // Create a home address record almost with the country var home = LcRest.Address.GetHomeAddress(userID); home.countryCode = LcRest.Locale.GetCountryCodeByID(countryID); home.countryID = countryID; LcRest.Address.SetAddress(home); StartOnboardingForUser(userID); } // SIGNUP LcMessaging.SendMail("*****@*****.**", "Sign-up", String.Format(@" <html><body><h3>Sign-up.</h3> <strong>This user was already in the database, is re-registering itself again!</strong><br/> <dl> <dt>Profile:</dt><dd>{0}</dd> <dt>First Name:</dt><dd>{1}</dd> <dt>Last Name:</dt><dd>{2}</dd> <dt>Country:</dt><dd>{5}</dd> <dt>Email:</dt><dd>{3}</dd> <dt>UserID:</dt><dd>{4}</dd> <dt>Phone:</dt><dd>{6}</dd> </dl> </body></html> ", profileTypeStr, firstName, lastName, email, logged.userID, countryID, phone)); return(logged); } else { if (useFacebookConnect) { // Verify Facebook ID and accessToken contacting to Facebook Servers if (LcFacebook.GetUserFromAccessToken(facebookUserID.ToString(), facebookAccessToken) == null) { throw new HttpException(400, "[[[Facebook account does not exists.]]]"); } } var registered = LcAuth.RegisterUser(email, firstName, lastName, password, isServiceProfessional, utm, -1, null, phone, null, countryID, isOrganization); // Create a home address record almost with the country var home = LcRest.Address.GetHomeAddress(registered.UserID); home.countryCode = LcRest.Locale.GetCountryCodeByID(countryID); home.countryID = countryID; LcRest.Address.SetAddress(home); if (useFacebookConnect) { // Register connection between the new account and the Facebook account LcAuth.ConnectWithFacebookAccount(registered.UserID, facebookUserID); } // Welcome and confirmation e-mail LcAuth.SendRegisterUserEmail(registered); // SIGNUP LcMessaging.SendMail("*****@*****.**", "Sign-up", String.Format(@" <html><body><h3>Sign-up.</h3> <dl> <dt>Profile:</dt><dd>{0}</dd> <dt>First Name:</dt><dd>{1}</dd> <dt>Last Name:</dt><dd>{2}</dd> <dt>Country:</dt><dd>{5}</dd> <dt>Email:</dt><dd>{3}</dd> <dt>UserID:</dt><dd>{4}</dd> <dt>Phone:</dt><dd>{6}</dd> </dl> </body></html> ", profileTypeStr, firstName, lastName, email, registered.UserID, countryID, phone)); // Auto login: return(Login(email, password, false, returnProfile, true)); } } else { // Bad request, input data incorrect because of validation rules throw new HttpException(400, LcRessources.ValidationSummaryTitle); } }
/// <summary> /// Signup with fields: /// - email [required] /// - password [required when no facebookUserID is given] /// - facebookUserID [optional] /// - countryID [optional defaults to COUNTRY_CODE_USA] /// - profileType [optional defaults to client] /// - utm [optional, not a named form parameter but the whole query string] /// - firstName [optional for professionals, required for clients] /// - lastName [optional for professionals, required for clients] /// - postalCode [optional] /// - referralCode [optional] /// - device [optional] /// - phone [optional for professionals, required for clients] /// - returnProfile [optional defaults to false] Returns the user profile in a property of the result /// </summary> /// <param name="page"></param> /// <returns></returns> public static LoginResult Signup(WebPage page) { page.Validation.RequireField("email", "You must specify an email."); // Username is an email currently, so need to be restricted page.Validation.Add("email", Validator.Regex(LcValidators.EmailAddressRegexPattern, "The email is not valid.")); // First data var profileTypeStr = Request.Form["profileType"] ?? ""; var isServiceProfessional = SERVICE_PROFESSIONAL_TYPE == profileTypeStr.ToUpper(); var isClient = !isServiceProfessional; var facebookUserID = Request.Form["facebookUserID"].AsLong(0); var facebookAccessToken = Request.Form["facebookAccessToken"]; var email = Request.Form["email"]; // // Conditional validations // Facebook var useFacebookConnect = facebookUserID > 0 && !String.IsNullOrEmpty(facebookAccessToken); if (!useFacebookConnect) { page.Validation.RequireField("password", "You must specify a password."); // We manually validate if a password was given, in order to prevent // showing up the validation format message additionally to the 'required password' message if (!String.IsNullOrWhiteSpace(Request.Form["password"])) { page.Validation.Add("password", new PasswordValidator()); } } else { var prevFbUser = LcAuth.GetFacebookUser(facebookUserID); if (prevFbUser != null) { throw new HttpException(409, "Facebook account already connected. Sign in."); } } // Profile Type if (isClient) { page.Validation.RequireField("phone", "You must specify your mobile phone number."); page.Validation.RequireField("firstName", "You must specify your first name."); page.Validation.RequireField("lastName", "You must specify your last name."); } if (page.Validation.IsValid()) { var postalCode = Request.Form["postalCode"]; // TODO To use countryCode for a more 'open' public REST API, where 'code' is a well know ISO 2-letters CODE //var countryCode = Request.Form["countryCode"] ?? "US"; var countryID = Request.Form["countryID"].AsInt(COUNTRY_CODE_USA); // Postal code is Optional if (!String.IsNullOrEmpty(postalCode)) { // Validate postal code before continue var add = new LcRest.Address { postalCode = postalCode, //countryCode = countryCode countryID = countryID }; if (!LcRest.Address.AutosetByCountryPostalCode(add)) { // bad postal code page.ModelState.AddError("postalCode", "Invalid postal code"); throw new HttpException(400, LcRessources.ValidationSummaryTitle); } } // Autogenerated password (we need to save one) on facebook connect: var password = useFacebookConnect ? LcAuth.GeneratePassword() : Request.Form["password"]; var firstName = Request.Form["firstName"]; var lastName = Request.Form["lastName"]; var referralCode = Request.Form["referralCode"]; var device = Request.Form["device"]; var phone = Request.Form["phone"]; var returnProfile = Request.Form["returnProfile"].AsBool(); var utm = Request.Url.Query; LoginResult logged = null; // If the user exists, try to log-in with the given password, // becoming a provider if that was the requested profileType and follow as // a normal login. // If the password didn't match, throw a sign-up specific error (email in use) // Otherwise, just register the user. if (LcAuth.ExistsEmail(email)) { // If the email exists, we try to log-in using the provided password, to don't bother with "e-mail in use" error // if the user provides the correct credentials (maybe just don't remember he/she has already an account; make it easy for them // to return). // BUT we have a special situation that needs extra checks: // CLIENT--CONFIRMATION LOGIC // The email can exists because the user has an account created as client by a service professional: // - A: On that cases, we need to communicate that specific situation (error message), generate a confirmation code // for the existent user, send email to let him to confirm it owns the given e-mail. // - B: On returning here after point A, a confirmation code is provided and we must proceed // by checking the confirmation code and, on success, unlock and update the membership password and // continue updating any given data. var userID = WebSecurity.GetUserId(email); var user = LcRest.UserProfile.Get(userID); if (user.accountStatusID != (int)LcEnum.AccountStatus.serviceProfessionalClient) { // NOT a client, just standard sign-up that requires verify the email/password or fail // Try Login try { logged = Login(email, password, false, returnProfile, true); userID = logged.userID; // throw exception on error if (isServiceProfessional) { LcAuth.BecomeProvider(userID); } } catch (HttpException) { // Not valid log-in, throw a 'email exists' error with Conflict http code throw new HttpException(409, "E-mail address is already in use."); } } else { // CLIENT--CONFIRMATION LOGIC // The email can exists because the user has an account created as client by a service professional: // - A: On that cases, we need to communicate that specific situation (error message), generate a confirmation code // for the existent user, send email to let him to confirm it owns the given e-mail. // - B: On returning here after point A, a confirmation code is provided and we must proceed // by checking the confirmation code and, on success, unlock and update the membership password and // continue updating any given data. var confirmationCode = Request["confirmationCode"]; var errMsg = String.Format(@"We see one of our service professionals has already scheduled services for you in the past. We've just sent an invitation to create your account to {0}. Please follow its instructions. We can't wait to get you on board!", email ); if (String.IsNullOrEmpty(confirmationCode)) { // Point A: create confirmation code // generate a confirmation code (creates the Membership record, that does not exists still since is as just a client) // this needs a random password (we still didn't verified the user, so do NOT trust on the given password). // NOTE: since this can be attempted several time by the user, and next attempts will fail because the Membership // record will exists already, just double check and try creation only if record don't exists: if (!LcAuth.HasMembershipRecord(userID)) { WebSecurity.CreateAccount(email, LcAuth.GeneratePassword(), true); } // send email to let him to confirm it owns the given e-mail LcMessaging.SendWelcomeCustomer(userID, email); // Not valid after all, just communicate was was done and needs to do to active its account: throw new HttpException(409, errMsg); } else { // Point B: confirm confirmation code if (LcAuth.GetConfirmationToken(userID) == confirmationCode) { // We know is valid, we can update the accountStatus to not be any more a "service professional's client" // and that will allow to set the account as confirmed using (var db = new LcDatabase()) { db.Execute("UPDATE users SET accountStatusID = @1 WHERE UserID = @0", userID, LcEnum.AccountStatus.active); } // now we can confirm (we already know the code is valid, it will just double check and update database) LcAuth.ConfirmAccount(confirmationCode); // set the password provided by the user. Trick: we need to generate a reset token in order to set the password. var token = WebSecurity.GeneratePasswordResetToken(email); LcAuth.ResetPassword(token, password); // Left continue with profile data update.. } else { // RE-send email to let him to confirm it owns the given e-mail LcMessaging.SendWelcomeCustomer(userID, email); throw new HttpException(409, errMsg); } } // We need a logged object, and additionally a double check is performed (so we ensure setting the password process worked). logged = Login(email, password, false, returnProfile, false); } // Update account data with the extra information. using (var db = new LcDatabase()) { db.Execute(@" UPDATE users SET firstName = coalesce(@1, firstName), lastName = coalesce(@2, lastName), mobilePhone = coalesce(@3, mobilePhone), signupDevice = coalesce(@4, signupDevice) WHERE userID = @0 ", userID, firstName, lastName, phone, device); if (!String.IsNullOrEmpty(postalCode)) { var address = LcRest.Address.GetHomeAddress(userID); if (address.postalCode != postalCode) { address.postalCode = postalCode; //address.countryCode = countryCode; address.countryCode = LcRest.Locale.GetCountryCodeByID(countryID); address.countryID = countryID; LcRest.Address.SetAddress(address); } } } // SIGNUP LcMessaging.SendMail("*****@*****.**", "Sign-up", String.Format(@" <html><body><h3>Sign-up.</h3> <strong>This user was already in the database, is re-registering itself again!</strong><br/> <dl> <dt>Profile:</dt><dd>{0}</dd> <dt>First Name:</dt><dd>{1}</dd> <dt>Last Name:</dt><dd>{2}</dd> <dt>Postal code:</dt><dd>{3}</dd> <dt>Country:</dt><dd>{9}</dd> <dt>Referral code:</dt><dd>{4}</dd> <dt>Device:</dt><dd>{5}</dd> <dt>Phone:</dt><dd>{6}</dd> <dt>Email:</dt><dd>{7}</dd> <dt>UserID:</dt><dd>{8}</dd> </dl> </body></html> ", profileTypeStr, firstName, lastName, postalCode, referralCode, device, phone, email, logged.userID, countryID)); return(logged); } else { if (useFacebookConnect) { // Verify Facebook ID and accessToken contacting to Facebook Servers if (LcFacebook.GetUserFromAccessToken(facebookUserID.ToString(), facebookAccessToken) == null) { throw new HttpException(400, "Facebook account does not exists."); } } var registered = LcAuth.RegisterUser(email, firstName, lastName, password, isServiceProfessional, utm, -1, null, phone, device); if (!String.IsNullOrEmpty(postalCode)) { // Set address var address = LcRest.Address.GetHomeAddress(registered.UserID); address.postalCode = postalCode; //address.countryCode = countryCode; address.countryCode = LcRest.Locale.GetCountryCodeByID(countryID); address.countryID = countryID; LcRest.Address.SetAddress(address); } if (useFacebookConnect) { // Register connection between the new account and the Facebook account LcAuth.ConnectWithFacebookAccount(registered.UserID, facebookUserID); } // Welcome and confirmation e-mail LcAuth.SendRegisterUserEmail(registered); // SIGNUP LcMessaging.SendMail("*****@*****.**", "Sign-up", String.Format(@" <html><body><h3>Sign-up.</h3> <dl> <dt>Profile:</dt><dd>{0}</dd> <dt>First Name:</dt><dd>{1}</dd> <dt>Last Name:</dt><dd>{2}</dd> <dt>Postal code:</dt><dd>{3}</dd> <dt>Country:</dt><dd>{9}</dd> <dt>Referral code:</dt><dd>{4}</dd> <dt>Device:</dt><dd>{5}</dd> <dt>Phone:</dt><dd>{6}</dd> <dt>Email:</dt><dd>{7}</dd> <dt>UserID:</dt><dd>{8}</dd> </dl> </body></html> ", profileTypeStr, firstName, lastName, postalCode, referralCode, device, phone, email, registered.UserID, countryID)); // Auto login: return(Login(email, password, false, returnProfile, true)); } } else { // Bad request, input data incorrect because of validation rules throw new HttpException(400, LcRessources.ValidationSummaryTitle); } }
/// <summary> /// Process a request to create a user job title given a jobTitleID with /// a validated and sanitized jobTitleName (pass in GetValidatedJobTitleName result) /// as a custom listing title. /// </summary> /// <param name="userID"></param> /// <param name="jobTitleID"></param> /// <param name="jobTitleName"></param> /// <returns></returns> public dynamic Create(int userID, int jobTitleID, string jobTitleName) { if (jobTitleID == 0 || jobTitleID == LcRest.UserJobTitle.UserGeneratedJobTitleID) { // new-job-title version: it's possible that the user wrotes a // job title name without pick-up one from the list, on that case // the user generated job title is assigned and the given title name is // used as listing title // Name for the job title is required if (String.IsNullOrWhiteSpace(jobTitleName)) { throw new HttpException(400, "A Job Title is required"); } // Search: we try an exact match, just in case we have already the job title (singular or plural) and // user didn't select it from the list var locale = LcRest.Locale.Current; var jobTitle = LcRest.JobTitle.FindExactName(jobTitleName, locale.languageID, locale.countryID); if (jobTitle.HasValue) { // Use the first one jobTitleID = jobTitle.Value; } else { // Create a new job-title based on the given name #650 jobTitleID = LcRest.UserJobTitle.UserGeneratedJobTitleID; } } // Double check that the job title exists else { var existentTitle = LcRest.PublicJobTitle.Get(jobTitleID, LcRest.Locale.Current); if (existentTitle == null) { throw new HttpException(404, "Job Title not found or disapproved"); } // If exists, we use the user given title, with fallback to the one we have for the given jobTitleID else if (String.IsNullOrWhiteSpace(jobTitleName)) { jobTitleName = existentTitle.singularName; } } // Read data; It stops on not valid: var data = GetValidatedItemBodyInput(); LcRest.UserJobTitle.Create(new LcRest.UserJobTitle { userID = userID, jobTitleID = jobTitleID, title = jobTitleName, intro = data.intro, cancellationPolicyID = data.policyID, collectPaymentAtBookMeButton = data.collectPaymentAtBookMeButton, instantBooking = data.instantBooking }); // If user is just a client, needs to become a professional var user = LcRest.UserProfile.Get(userID); if (!user.isServiceProfessional) { LcAuth.BecomeProvider(userID); // Set onboarding step as done for 'add job title' to avoid display that screen again to the user: LcData.UserInfo.SetOnboardingStep(userID, "addJobTitle"); // Send email as provider LcMessaging.SendWelcomeProvider(userID, WebSecurity.CurrentUserName); } return(LcRest.UserJobTitle.GetItem(userID, jobTitleID)); }