def login(request, default_next='/', staff_protocol='https'): def is_secure_password(pw): has = lambda cs: any(char in cs for char in pw) return len(pw) >= 8 and has(string.lowercase) and has(string.uppercase) and has(string.digits) def valid_slug(raw): raw = raw.lstrip('#').strip() allowed = string.letters + string.digits + '_' def process(c): if c in allowed: return c elif c in string.whitespace: return '_' else: return '' return (''.join(map(process, raw)))[:20] cookies_to_delete = [] next_ = get_next(request) if request.method == 'GET': return r2r_jinja('user/login.html', locals(), request) signed_request = request.POST.get(u'signed_request', None) facebook_id = request.POST.get(u'facebook_id', None) if signed_request and facebook_id: user = authenticate(request, facebook_id, signed_request) if user is None: return r2r_jinja('user/login.html', locals(), request) # this is a total hack because we don't care to write a backend for the above authenticate method user.backend = settings.AUTHENTICATION_BACKENDS[0] else: username = valid_slug(request.POST.get('username', '')) password = request.POST.get('password') if check_rate_limit(request, username): message = "Too many retries. Wait a minute and try again." return r2r_jinja('user/login.html', locals(), request) user = auth.authenticate(username=username, password=password) if user is None: if User.objects.filter(username=username).exists(): message = "Incorrect username or password." else: message = "Incorrect username or password." return r2r_jinja('user/login.html', locals(), request) if user.is_staff: if is_secure_password(password): next_ = make_absolute_url(next_ or default_next, protocol=staff_protocol) else: message = ("User is staff and has an insecure password. Please create a more secure one (8 or more " "characters, mixed case and has numbers). Use password reset to fix this.") return r2r_jinja('user/login.html', locals(), request) auth.login(request, user) try: (key, post_data) = after_signup.get_posted_comment(request) if post_data: next_ = post_comment(request, user, post_data, persist_url=False).details().url cookies_to_delete.append(after_signup.make_cookie_key('post_comment')) except KeyError: pass def cleanup(response): for k in cookies_to_delete: response.delete_cookie(k) return response if next_: next_params = request.GET.copy() if 'next' in next_params: del next_params['next'] next_params = '?' + urllib.urlencode(next_params) if next_params else '' return cleanup(HttpResponseRedirect(next_ + next_params)) else: return cleanup(HttpResponseRedirect('/'))
def get_signup_context(request, skip_invite_code=None, template="user/signup.django.html", cookies_to_set={}, cookies_to_delete=[]): """ Returns an error context (or dict) if the signup is not successful. Returns `None` for successful signups. `cookies_to_set` and `cookies_to_delete` should be passed empty so that this functin may append to them. `cookies_to_set` is for session cookies, to tie into after_signup.py / after_signup.js. """ skip_invite_code = skip_invite_code or request.GET.get('skip_invite_code', '').lower() bypass_copy = settings.SHORT_CODE_COPY.get(skip_invite_code) skippable_codes = (['dicksoup', 'angelgate', 'friends_and_family', 'herpderp', 'fffffat', 'buzzfeedbrews'] + settings.SHORT_CODES) login_url = '/login' if request.REQUEST.get('next'): next = request.REQUEST['next'] params = [urllib.urlencode({'next': next})] if request.method == 'POST': next_params = request.POST.get('next_params', '') else: next_params = request.GET.copy() del next_params['next'] next_params = urllib.urlencode(next_params) if next_params: params.append(next_params) login_url = login_url + '?' + u'&'.join(params) try: fb_user, fb_api = util.get_fb_api(request) except NotLoggedIntoFacebookError: fb_user, fb_api = None, None fb_uid = fb_user.get('uid') if fb_user else None fb_invite = None if request.COOKIES.get('fb_message_id'): fb_invite = FacebookInvite.objects.get_or_none(fb_message_id=request.COOKIES.get('fb_message_id')) cookies_to_delete.append('fb_message_id') if not fb_invite and fb_uid: fb_invite = FacebookInvite.get_invite(fb_user.get('uid')) if request.method == 'GET': return locals() username = request.POST.get('username', '') password = request.POST.get('password', '') email = request.POST.get('email', '') if not fb_uid: fb_uid = request.POST.get('facebook_id', None) def error(message, context=locals()): context['message'] = message Metrics.signup_form_invalid.record(request) return context if check_rate_limit(request, username): return error("Too many failed signup attempts. Wait a minute and try again.") if not password: return error("Password required.") if not User.validate_password(password): return error("Sorry, your password is too short. Please use 5 or more characters.") error_msg = User.validate_username(username) if error_msg: return error(error_msg) if not User.validate_email(email): return error("Please enter a valid email address.") if not User.email_is_unused(email): return error("This email address is already in use. Try <a href='/login'>signing in</a> " "or <a href='/password_reset'>resetting</a> your password if you've forgotten it.") if fb_uid and not UserInfo.facebook_is_unused(fb_uid): return error("This Facebook account is already in use. Try <a href='/login'>signing in</a> " "or <a href='/password_reset'>resetting</a> your password if you've forgotten it.") try: user = User.objects.create_user(username, email, password) except IntegrityError: return error("Username taken.") if not fb_uid: fb_uid = None UserInfo(user=user, invite_bypass=skip_invite_code, facebook_id=fb_uid, enable_timeline=True).save() if fb_invite: fb_invite.invitee = user fb_invite.save() user = auth.authenticate(username=username, password=password) # Handle following featured groups and optionally one defined by their short code. if skip_invite_code: autofollow = settings.SHORT_CODE_AUTOFOLLOW.get(skip_invite_code) if autofollow: to_follow.append(autofollow) economy.grant_daily_free_stickers(request.user, force=True, count=knobs.SIGNUP_FREE_STICKERS) # Follow the Canvas account. try: user.redis.following.sadd(User.objects.get(username=settings.CANVAS_ACCOUNT_USERNAME).id) except User.DoesNotExist: pass # Logged-out remix? cookie_key, post_data = after_signup.get_posted_comment(request) if post_data: post_comment(request, user, post_data) cookies_to_delete.append(cookie_key) inviter_id = request.session.get('inviter') if inviter_id: user.kv.inviter = inviter_id del request.session['inviter'] inviter = User.objects.get(pk=inviter_id) user.follow(inviter) inviter.follow(user) # DEPRECATED. Use after_signup.py / after_signup.js now instead. extra_info = request.POST.get("info") if extra_info: extra_info = util.loads(extra_info) if extra_info.get('in_flow') == 'yes': fact.record('flow_signup', request, {}) # A user may have come to signup by remixing/replying, and we've got their post data to submit and send them # to. if not post_data: post_data = extra_info.get('post') if post_data: post_comment(request, user, post_data) old_session_key = request.session.session_key def _after_signup(): if fb_api: app_requests = fb_api.get_object('/me/apprequests/').get('data', []) for app_request in app_requests: if id in app_request: fb.delete_object(app_request['id']) Metrics.signup.record(request, old_session_key=old_session_key, username=username, email=email) if 'failed_signup' in request.session: del request.session['failed_signup'] Metrics.signup_second_try.record(request) if template == 'signup/_signup_prompt.html': Metrics.signup_prompt.record(request) else: Metrics.signup_main.record(request) bgwork.defer(_after_signup) # auth.login starts a new session and copies the session data from the old one to the new one auth.login(request, user) experiments.migrate_from_request_to_user(request, user)
def signup(request, username, password, email, facebook_access_token=None): if check_rate_limit(request, username): raise ServiceError("Too many signup attempts. Wait a minute and try again.") try: return _login(request, password, username=username, email=email) except ValidationError: pass migrated_from_canvas_account = False errors = defaultdict(list) fb_user = None if facebook_access_token: fb_user = FacebookUser.create_from_access_token(facebook_access_token) def username_taken(): # Ugly hack. for error in errors['username']: if 'taken' in error: return errors['username'].append("Sorry! That username is already taken.") if not password: errors['password'].append("Please enter a password.") if not User.validate_password(password): errors['password'].append("Sorry, your password is too short. " "Please use {} or more characters.".format(User.MINIMUM_PASSWORD_LENGTH)) if not email: errors['email'].append("Please enter your email address.") elif not User.validate_email(email): errors['email'].append("Please enter a valid email address.") username_error = User.validate_username(username) if username_error: errors['username'].append(username_error) if not User.email_is_unused(email): errors['email'].append("Sorry! That email address is already being used for an account.") elif User.is_username_reserved(username): try: user = User.migrate_canvas_user(request, username, password, email=email) except IntegrityError: username_taken() except ValidationError: errors['username'] = ["""Sorry! This username is taken. Please pick a different username, """ + """or if you are "{}," enter your password to sign in.""".format(username)] else: migrated_from_canvas_account = True if errors: if fb_user: fb_user.delete() raise ValidationError(errors) if not migrated_from_canvas_account: try: user = User.objects.create_user(username, email, password) except IntegrityError: username_taken() raise ValidationError(errors) UserInfo.objects.create(user=user) if fb_user: fb_user.user = user fb_user.save() fb_user.notify_friends_of_signup(facebook_access_token) user.migrate_facebook_avatar(request, facebook_access_token) user = auth.authenticate(username=username, password=password) # auth.login starts a new session and copies the session data from the old one to the new one auth.login(request, user) return { 'user': PrivateUserDetails.from_id(user.id).to_client(), 'user_bio': user.userinfo.bio_text, 'user_subscribed_to_starred': is_subscribed(user, 'starred'), 'sessionid': request.session.session_key, 'migrated_from_canvas_account': migrated_from_canvas_account, }
def signup(request, username, password, email, facebook_access_token=None): if check_rate_limit(request, username): raise ServiceError( "Too many signup attempts. Wait a minute and try again.") try: return _login(request, password, username=username, email=email) except ValidationError: pass migrated_from_canvas_account = False errors = defaultdict(list) fb_user = None if facebook_access_token: fb_user = FacebookUser.create_from_access_token(facebook_access_token) def username_taken(): # Ugly hack. for error in errors['username']: if 'taken' in error: return errors['username'].append("Sorry! That username is already taken.") if not password: errors['password'].append("Please enter a password.") if not User.validate_password(password): errors['password'].append("Sorry, your password is too short. " "Please use {} or more characters.".format( User.MINIMUM_PASSWORD_LENGTH)) if not email: errors['email'].append("Please enter your email address.") elif not User.validate_email(email): errors['email'].append("Please enter a valid email address.") username_error = User.validate_username(username) if username_error: errors['username'].append(username_error) if not User.email_is_unused(email): errors['email'].append( "Sorry! That email address is already being used for an account.") elif User.is_username_reserved(username): try: user = User.migrate_canvas_user(request, username, password, email=email) except IntegrityError: username_taken() except ValidationError: errors['username'] = [ """Sorry! This username is taken. Please pick a different username, """ + """or if you are "{}," enter your password to sign in.""". format(username) ] else: migrated_from_canvas_account = True if errors: if fb_user: fb_user.delete() raise ValidationError(errors) if not migrated_from_canvas_account: try: user = User.objects.create_user(username, email, password) except IntegrityError: username_taken() raise ValidationError(errors) UserInfo.objects.create(user=user) if fb_user: fb_user.user = user fb_user.save() fb_user.notify_friends_of_signup(facebook_access_token) user.migrate_facebook_avatar(request, facebook_access_token) user = auth.authenticate(username=username, password=password) # auth.login starts a new session and copies the session data from the old one to the new one auth.login(request, user) return { 'user': PrivateUserDetails.from_id(user.id).to_client(), 'user_bio': user.userinfo.bio_text, 'user_subscribed_to_starred': is_subscribed(user, 'starred'), 'sessionid': request.session.session_key, 'migrated_from_canvas_account': migrated_from_canvas_account, }
def signup(request, username, password, email, facebook_access_token=None, twitter_access_token=None, twitter_access_token_secret=None): if check_rate_limit(request, username): raise ServiceError( _("Too many signup attempts. Wait a minute and try again.")) if not _validate_charset(username): raise ValidationError({ 'username': [ _("Usernames can only contain the letters a-z or A-Z, digits, and underscores." ) ], }) if not _validate_charset(email): raise ValidationError({ 'email': [ _("Sorry, your email address contains invalid characters. Please remove them and try again." ) ], }) try: return _login(request, password, username=username, email=email) except ValidationError: pass errors = defaultdict(list) fb_user = None twitter_user = None if facebook_access_token: fb_user = FacebookUser.get_or_create_from_access_token( facebook_access_token) elif twitter_access_token and twitter_access_token_secret: twitter_user = TwitterUser.get_or_create_from_access_token( twitter_access_token, twitter_access_token_secret) def username_taken(): # Ugly hack. for error in errors['username']: if 'taken' in error: return errors['username'].append(_("Sorry! That username is already taken.")) if not password: errors['password'].append(_("Please enter a password.")) if not User.validate_password(password): errors['password'].append( _(u"Sorry, your password is too short. Please use %(count)d or more characters." % {'count': User.MINIMUM_PASSWORD_LENGTH})) if not email: errors['email'].append(_("Please enter your email address.")) elif not User.validate_email(email): errors['email'].append(_("Please enter a valid email address.")) username_error = User.validate_username(username) if username_error: errors['username'].append(username_error) if not User.email_is_unused(email): errors['email'].append( _("Sorry! That email address is already being used for an account. Try signing in instead, or use another email address." )) if errors: if fb_user: fb_user.delete() raise ValidationError(errors) try: user = User.objects.create_user(username, email, password) except IntegrityError: username_taken() raise ValidationError(errors) ui = UserInfo.objects.create(user=user) ui.update_hashes() if request.app_version is not None: user.kv.signup_app_version.set(request.app_version) if fb_user: fb_user.user = user fb_user.save() fb_user.notify_friends_of_signup(facebook_access_token) fb_user.respond_to_apprequest_invites(facebook_access_token) user.migrate_facebook_avatar(request, facebook_access_token) elif twitter_user: twitter_user.user = user twitter_user.save() twitter_user.notify_followers_of_signup(twitter_access_token, twitter_access_token_secret) twitter_user.auto_follow_from_invite(twitter_access_token, twitter_access_token_secret) @bgwork.defer def migrate_twitter_avatar(): user.migrate_twitter_avatar(request, twitter_access_token, twitter_access_token_secret) user = auth.authenticate(username=username, password=password) # auth.login starts a new session and copies the session data from the old one to the new one auth.login(request, user) return { 'user': PrivateUserDetails.from_id(user.id).to_client(), 'user_bio': user.userinfo.bio_text, 'user_subscribed_to_starred': is_subscribed(user, 'starred'), 'comment_count': 0, 'quest_count': Quest.all_objects.filter(author=user).count(), 'sessionid': request.session.session_key, 'migrated_from_canvas_account': False, 'login': False, 'heavy_state_sync': heavy_state_sync(request.user, app_version=request.app_version, app_version_tuple=request.app_version_tuple), }
def get_signup_context(request, skip_invite_code=None, template="user/signup.django.html", cookies_to_set={}, cookies_to_delete=[]): """ Returns an error context (or dict) if the signup is not successful. Returns `None` for successful signups. `cookies_to_set` and `cookies_to_delete` should be passed empty so that this functin may append to them. `cookies_to_set` is for session cookies, to tie into after_signup.py / after_signup.js. """ skip_invite_code = skip_invite_code or request.GET.get( 'skip_invite_code', '').lower() bypass_copy = settings.SHORT_CODE_COPY.get(skip_invite_code) skippable_codes = ([ 'dicksoup', 'angelgate', 'friends_and_family', 'herpderp', 'fffffat', 'buzzfeedbrews' ] + settings.SHORT_CODES) login_url = '/login' if request.REQUEST.get('next'): next = request.REQUEST['next'] params = [urllib.urlencode({'next': next})] if request.method == 'POST': next_params = request.POST.get('next_params', '') else: next_params = request.GET.copy() del next_params['next'] next_params = urllib.urlencode(next_params) if next_params: params.append(next_params) login_url = login_url + '?' + u'&'.join(params) try: fb_user, fb_api = util.get_fb_api(request) except NotLoggedIntoFacebookError: fb_user, fb_api = None, None fb_uid = fb_user.get('uid') if fb_user else None fb_invite = None if request.COOKIES.get('fb_message_id'): fb_invite = FacebookInvite.objects.get_or_none( fb_message_id=request.COOKIES.get('fb_message_id')) cookies_to_delete.append('fb_message_id') if not fb_invite and fb_uid: fb_invite = FacebookInvite.get_invite(fb_user.get('uid')) if request.method == 'GET': return locals() username = request.POST.get('username', '') password = request.POST.get('password', '') email = request.POST.get('email', '') if not fb_uid: fb_uid = request.POST.get('facebook_id', None) code = InviteCode.objects.get_or_none(code=request.POST.get('code')) def error(message, context=locals()): context['message'] = message Metrics.signup_form_invalid.record(request) return context if check_rate_limit(request, username): return error( "Too many failed signup attempts. Wait a minute and try again.") if not password: return error("Password required.") if not User.validate_password(password): return error( "Sorry, your password is too short. Please use 5 or more characters." ) error_msg = User.validate_username(username) if error_msg: return error(error_msg) if not User.validate_email(email): return error("Please enter a valid email address.") if not User.email_is_unused(email): return error( "This email address is already in use. Try <a href='/login'>signing in</a> " "or <a href='/password_reset'>resetting</a> your password if you've forgotten it." ) if fb_uid and not UserInfo.facebook_is_unused(fb_uid): return error( "This Facebook account is already in use. Try <a href='/login'>signing in</a> " "or <a href='/password_reset'>resetting</a> your password if you've forgotten it." ) try: user = User.objects.create_user(username, email, password) except IntegrityError: return error("Username taken.") if not fb_uid: fb_uid = None UserInfo(user=user, invite_bypass=skip_invite_code, facebook_id=fb_uid, enable_timeline=True).save() if code: code.invitee = user code.save() if fb_invite: fb_invite.invitee = user fb_invite.save() user = auth.authenticate(username=username, password=password) # Handle following featured groups and optionally one defined by their short code. if skip_invite_code: autofollow = settings.SHORT_CODE_AUTOFOLLOW.get(skip_invite_code) if autofollow: to_follow.append(autofollow) economy.grant_daily_free_stickers(request.user, force=True, count=knobs.SIGNUP_FREE_STICKERS) # Follow the Canvas account. try: user.redis.following.sadd( User.objects.get(username=settings.CANVAS_ACCOUNT_USERNAME).id) except User.DoesNotExist: pass # Logged-out remix? cookie_key, post_data = after_signup.get_posted_comment(request) if post_data: post_comment(request, user, post_data) cookies_to_delete.append(cookie_key) inviter_id = request.session.get('inviter') if inviter_id: user.kv.inviter = inviter_id del request.session['inviter'] inviter = User.objects.get(pk=inviter_id) user.follow(inviter) inviter.follow(user) # DEPRECATED. Use after_signup.py / after_signup.js now instead. extra_info = request.POST.get("info") if extra_info: extra_info = util.loads(extra_info) if extra_info.get('in_flow') == 'yes': fact.record('flow_signup', request, {}) # A user may have come to signup by remixing/replying, and we've got their post data to submit and send them # to. if not post_data: post_data = extra_info.get('post') if post_data: post_comment(request, user, post_data) old_session_key = request.session.session_key def _after_signup(): if fb_api: app_requests = fb_api.get_object('/me/apprequests/').get( 'data', []) for app_request in app_requests: if id in app_request: fb.delete_object(app_request['id']) Metrics.signup.record(request, old_session_key=old_session_key, username=username, email=email) if 'failed_signup' in request.session: del request.session['failed_signup'] Metrics.signup_second_try.record(request) if template == 'signup/_signup_prompt.html': Metrics.signup_prompt.record(request) else: Metrics.signup_main.record(request) bgwork.defer(_after_signup) # auth.login starts a new session and copies the session data from the old one to the new one auth.login(request, user) experiments.migrate_from_request_to_user(request, user)
def signup(request, username, password, email, facebook_access_token=None, twitter_access_token=None, twitter_access_token_secret=None): if check_rate_limit(request, username): raise ServiceError(_("Too many signup attempts. Wait a minute and try again.")) if not _validate_charset(username): raise ValidationError({ 'username': [_("Usernames can only contain the letters a-z or A-Z, digits, and underscores.")], }) if not _validate_charset(email): raise ValidationError({ 'email': [_("Sorry, your email address contains invalid characters. Please remove them and try again.")], }) try: return _login(request, password, username=username, email=email) except ValidationError: pass errors = defaultdict(list) fb_user = None twitter_user = None if facebook_access_token: fb_user = FacebookUser.get_or_create_from_access_token(facebook_access_token) elif twitter_access_token and twitter_access_token_secret: twitter_user = TwitterUser.get_or_create_from_access_token(twitter_access_token, twitter_access_token_secret) def username_taken(): # Ugly hack. for error in errors['username']: if 'taken' in error: return errors['username'].append(_("Sorry! That username is already taken.")) if not password: errors['password'].append(_("Please enter a password.")) if not User.validate_password(password): errors['password'].append(_(u"Sorry, your password is too short. Please use %(count)d or more characters." % {'count': User.MINIMUM_PASSWORD_LENGTH})) if not email: errors['email'].append(_("Please enter your email address.")) elif not User.validate_email(email): errors['email'].append(_("Please enter a valid email address.")) username_error = User.validate_username(username) if username_error: errors['username'].append(username_error) if not User.email_is_unused(email): errors['email'].append(_("Sorry! That email address is already being used for an account. Try signing in instead, or use another email address.")) if errors: if fb_user: fb_user.delete() raise ValidationError(errors) try: user = User.objects.create_user(username, email, password) except IntegrityError: username_taken() raise ValidationError(errors) ui = UserInfo.objects.create(user=user) ui.update_hashes() if request.app_version is not None: user.kv.signup_app_version.set(request.app_version) if fb_user: fb_user.user = user fb_user.save() fb_user.notify_friends_of_signup(facebook_access_token) fb_user.respond_to_apprequest_invites(facebook_access_token) user.migrate_facebook_avatar(request, facebook_access_token) elif twitter_user: twitter_user.user = user twitter_user.save() twitter_user.notify_followers_of_signup(twitter_access_token, twitter_access_token_secret) twitter_user.auto_follow_from_invite(twitter_access_token, twitter_access_token_secret) @bgwork.defer def migrate_twitter_avatar(): user.migrate_twitter_avatar(request, twitter_access_token, twitter_access_token_secret) user = auth.authenticate(username=username, password=password) # auth.login starts a new session and copies the session data from the old one to the new one auth.login(request, user) return { 'user': PrivateUserDetails.from_id(user.id).to_client(), 'user_bio': user.userinfo.bio_text, 'user_subscribed_to_starred': is_subscribed(user, 'starred'), 'comment_count': 0, 'quest_count': Quest.all_objects.filter(author=user).count(), 'sessionid': request.session.session_key, 'migrated_from_canvas_account': False, 'login': False, 'heavy_state_sync': heavy_state_sync(request.user, app_version=request.app_version, app_version_tuple=request.app_version_tuple), }