def users_put_view(request): """ Used for forgotten password requests """ # TODO Make tests for this # TODO Make this a temporary reset link thing instead of actually resetting passwords to random if request.user is not None: request.response.status = 400 return { 'd': error_dict('api_errors', 'you are already logged in, ignoring') } username = request.json_body.get('username') email = request.json_body.get('email') if not isinstance(username, basestring) or not isinstance( email, basestring): request.response.status = 400 return { 'd': error_dict('api_errors', 'username and email are required string fields') } user = request.dbsession.query(User).filter( User.username == username.lower()).one_or_none() if user is None or user.email != email.lower(): request.response.status = 400 return {'d': error_dict('api_errors', 'invalid state found')} uid = uuid4() newpass = uid.hex user.salt = os.urandom(256) user.password = hash_password(newpass, user.salt) # This needs to be written to whatever queues/sends an email out. # send_email.apply_async((user.email, "pyrapostbin password recovery", recovery_template %(username, newpass))) return {'d': 'recovery email sent'}
def test_missing_parameters(self): """ If we don't pass in all the required parameters, get an error """ self.assertEqual(0, self.session.query(User).count()) for i in ['username', 'email', 'password']: self.request.json_body = deepcopy(self.new_account) del self.request.json_body[i] result = users_post_view(self.request)['d'] self.assertEqual( result, error_dict( 'api_errors', 'username, email, and password are all required string fields' )) self.request.json_body = {} result = users_post_view(self.request)['d'] self.assertEqual( result, error_dict( 'api_errors', 'username, email, and password are all required string fields') ) self.request.json_body = {'username': '******'} result = users_post_view(self.request)['d'] self.assertEqual( result, error_dict( 'api_errors', 'username, email, and password are all required string fields') )
def test_bad_emails(self): """ If we pass a bad email, get an error about it """ bad_email = 'invalid@spaced domain.com' email_result = {} try: validate_email(bad_email) # validate and get info self.fail('Validate email did not raise exception, fix the test') except EmailNotValidError as e: # email is not valid, exception message is human-readable email_result = error_dict('api_errors', 'email invalid: %s' % e) self.request.json_body = deepcopy(self.good_dict) self.request.json_body['email'] = bad_email result = user_id_put_view(self.request)['d'] self.assertEqual(result, email_result) self.assertNotEqual(result, {}) self.request.json_body = deepcopy(self.good_dict) self.request.json_body['email'] = 234234 # Not a string result = user_id_put_view(self.request)['d'] self.assertEqual( result, error_dict('api_errors', 'email invalid: must be a string'))
def users_post_view(request): username = request.json_body.get('username') if not isinstance(username, basestring): request.response.status = 400 return { 'd': error_dict( 'api_errors', 'username, email, and password are all required string fields') } if username_in_use(request.json_body['username'], request.dbsession): request.response.status = 400 return { 'd': error_dict( 'verification_error', 'username already in use: %s' % request.json_body['username']) } requires = ['email', 'password'] if not all(field in request.json_body for field in requires) \ or not all(isinstance(request.json_body.get(field), basestring) for field in request.json_body): request.response.status = 400 return { 'd': error_dict( 'api_errors', 'username, email, and password are all required string fields') } user = User() user.salt = os.urandom(256) user.password = hash_password(request.json_body['password'], user.salt) user.username = request.json_body['username'].lower() user.email = request.json_body['email'].lower() user.origin = request.json_body.get('origin', None) user.authpin = '123456' request.dbsession.add(user) request.dbsession.flush() request.dbsession.refresh(user) s = Session() s.owner = user.id s.token = str(uuid4()) request.dbsession.add(s) request.dbsession.flush() request.dbsession.refresh(s) result = dict_from_row(user, remove_fields=removals) result['session'] = dict_from_row(s, remove_fields=removals) return {'d': result}
def user_id_get_view(request): if request.user is None: request.response.status = 400 return { 'd': error_dict('api_errors', 'not authenticated for this request') } if not request.matchdict.get('user_id') or int( request.matchdict.get('user_id')) != request.user.id: request.response.status = 400 return { 'd': error_dict('api_errors', 'not authenticated for this request') } user = request.user result = dict_from_row(user, remove_fields=removals) return {'d': result}
def test_not_logged_in(self): """ If we aren't logged in, get an api error """ self.request.user = None result = user_id_put_view(self.request)['d'] self.assertEqual(result, error_dict('api_errors', 'not authenticated for this request'))
def test_no_user(self): """ If we try to get info without a login, get an error back """ self.request.user = None result = user_id_get_view(self.request)['d'] self.assertEqual(result, error_dict('api_errors', 'not authenticated for this request'))
def test_wrong_id(self): """ If we don't use the right id in the url, get an api error """ self.request.matchdict = {'user_id': int(self.request.user.id)+4} self.request.json_body = {} result = user_id_put_view(self.request)['d'] self.assertEqual(result, error_dict('api_errors', 'not authenticated for this request'))
def test_bad_password_type(self): """ If we pass in anything but a string, get an error indicating so """ for val in [x for x in bad_data_typevals_list if not isinstance(x, basestring) and x is not None]: self.request.json_body = deepcopy(self.good_dict) self.request.json_body['password'] = val result = user_id_put_view(self.request)['d'] self.assertEqual(result, error_dict('api_errors', 'password must be a string'))
def test_no_user_matchdict(self): """ If we try to get info a login but not the same ID in the url, get an error back """ user = self.datautils.create_user() self.request.matchdict = {'user_id': user.id + 4} self.request.user = user result = user_id_get_view(self.request)['d'] self.assertEqual(result, error_dict('api_errors', 'not authenticated for this request'))
def test_username_not_in_use(self): """ If we provide a username that is not in use, get False """ self.request.json_body = {'username': '******'} self.datautils.create_user({'username': '******', 'password': '******'}) result = users_post_view(self.request)['d'] self.assertIsInstance(result, dict) self.assertEqual(result, error_dict('api_errors', 'username, email, and password are all required string fields'))
def test_username_in_use(self): """ If we provide a username that is in use, get True """ self.request.json_body = {'username': '******'} self.datautils.create_user({'username': '******', 'password': '******'}) result = users_post_view(self.request)['d'] self.assertIsInstance(result, dict) self.assertEqual(result, error_dict('verification_error', 'username already in use: testuser'))
def test_username_taken(self): """ If we try to create an account that is in use, get an api error """ self.datautils.create_user({'username': '******'}) self.assertEqual(1, self.session.query(User).count()) self.request.json_body = deepcopy(self.new_account) result = users_post_view(self.request)['d'] self.assertEqual(result, error_dict('verification_error', 'username already in use: %s' % self.new_account['username']))
def test_invalid_password(self): """ If we give a string of insufficient complexity, error """ self.request.json_body = deepcopy(self.good_dict) invalids = ['5horT'] for val in invalids: self.request.json_body['password'] = val result = user_id_put_view(self.request)['d'] self.assertEqual(result, error_dict('api_errors', 'password must be at least 8 characters'))
def test_bad_parameters(self): """ If we don't pass in all the right types, get an error """ self.assertEqual(0, self.session.query(User).count()) for i in ['username', 'email', 'password']: self.request.json_body = deepcopy(self.new_account) for val in [x for x in bad_data_typevals_list if not isinstance(x, basestring)]: self.request.json_body[i] = val result = users_post_view(self.request)['d'] self.assertEqual(result, error_dict('api_errors', 'username, email, and password are all required string fields'))
def user_id_put_view(request): if request.user is None: request.response.status = 400 return { 'd': error_dict('api_errors', 'not authenticated for this request') } if not request.matchdict.get('user_id') or int( request.matchdict.get('user_id')) != request.user.id: request.response.status = 400 return { 'd': error_dict('api_errors', 'not authenticated for this request') } valid_types = { 'email': basestring, 'pin': basestring, 'timezone': datetime, 'infoemails': bool, } email = request.json_body.get('email') authpin = request.json_body.get('authpin') timezone = request.json_body.get('timezone') infoemails = request.json_body.get('infoemails') if not isinstance(email, basestring): request.response.status = 400 return { 'd': error_dict('api_errors', 'email invalid: must be a string') } try: v = validate_email(email) # validate and get info email = v["email"] # replace with normalized form except EmailNotValidError as e: # email is not valid, exception message is human-readable request.response.status = 400 return {'d': error_dict('api_errors', 'email invalid: %s' % e)} password = request.json_body.get('password') # Password must be optional, since they don't know the old value if password is not None: if not isinstance(password, basestring): request.response.status = 400 return {'d': error_dict('api_errors', 'password must be a string')} if len(password) < 8: request.response.status = 400 return { 'd': error_dict('api_errors', 'password must be at least 8 characters') } request.user.password = hash_password(password, request.user.salt) request.user.email = email request.dbsession.flush() request.dbsession.refresh(request.user) return {'d': dict_from_row(request.user, remove_fields=removals)}