def test_password_reset_no_new_password(test_project, waf, create_users): """Test changing password via post.""" change_form_data = { 'username': '******', } selector, verifier = users._generate_split_token() token = '{0}{1}'.format(selector.decode('utf-8'), verifier.decode('utf-8')) with get_engine().connect() as con: query = sa.select('*').select_from(user) row = con.execute(query).fetchone() change_form_data['username'] = row.username stmt = user_password_reset.insert().values( user_id=row.id, selector=str(selector), verifier=hashlib.sha256(verifier).hexdigest(), expires=get_utc(datetime.datetime.now() + datetime.timedelta(hours=3)), ) con.execute(stmt) encoded_user_id = users.encode_user_id(row.id) request, response = testing.simulate_request(waf) middleware = testing.injected_session_start(waf, request) request, response = waf.server.test_client.post( f'/auth/password_reset/{encoded_user_id}/{token}/', json=change_form_data, headers=testing.csrf_headers()) testing.injected_session_end(waf, middleware) assert response.status == 401
async def log_in(request, user_row): """Add the user to the session, update last_login. :param request: Sanic request. :param user_row: User SQLAlchmey result. """ request['session']['user'] = user_row await update_user(database=None, target_username=user_row.username, last_login=get_utc(datetime.datetime.now()))
async def log_in(request, user_row): """Add the user to the session, update last_login. :param request: Sanic request. :param user_row: User SQLAlchmey result. """ last_login = get_utc(datetime.datetime.now()) request['session']['user'] = user_row request['session']['csrf_token'] = generate_csrf_token( user_row.get('id'), user_row.get('last_login')) await update_user(database=None, target_username=user_row.get('username'), last_login=last_login)
async def create_user(username, password, first_name=None, last_name=None, email=None, user_type=None, is_staff=False, is_superuser=False, is_active=True, date_joined=None, database=None): """Create a user. :param username: String. Username. :param password: String. Password. Will be stored encoded. :param first_name: String. User's first name. (Default None) :param last_name: String. User's last name. (Default None) :param user_type: String. Optionally segment users with string categories. (Default None) :param is_active: Boolean. If user account is active. (Default True) :param is_staff: Boolean. If user is "staff" and has "admin" level access. (Default False) :param is_superuser: Boolean. If user is a "superuser" and has all permissions without being explicitly assigned. (Default False) :param date_joined: Datetime (with timezone). Date user account was created. :param database: String. Database name to connect to. (Default: None - use jawaf.auth default) """ database = database_key(database) if date_joined is None: date_joined = get_utc(datetime.datetime.now()) async with Connection(database) as con: stmt = user.insert().values(username=username, password=make_password(password), first_name=first_name, last_name=last_name, email=email, user_type=user_type, is_staff=is_staff, is_superuser=is_superuser, is_active=is_active, date_joined=date_joined, last_login=None) await con.execute(stmt)
def create_user_sync(engine, username='', password='', first_name=None, last_name=None, email=None, user_type=None, is_staff=False, is_superuser=False, is_active=True, date_joined=None): """Create a user synchronously. :param engine: SQLAlchemy Engine. Engine to connect to. :param username: String. Username. :param password: String. Password. Will be stored encoded. :param first_name: String. User's first name. (Default None) :param last_name: String. User's last name. (Default None) :param user_type: String. Optionally segment users with string categories. (Default None) :param is_active: Boolean. If user account is active. (Default True) :param is_staff: Boolean. If user is "staff" and has "admin" level access. (Default False) :param is_superuser: Boolean. If user is a "superuser" and has all permissions without being explicitly assigned. (Default False) :param date_joined: Datetime (with timezone). Date user account was created. """ if not engine: raise ServerError('Must specify an SQLAlchemy Engine') if date_joined is None: date_joined = get_utc(datetime.datetime.now()) with engine.connect() as con: stmt = user.insert().values(username=username, password=make_password(password), first_name=first_name, last_name=last_name, email=email, user_type=user_type, is_staff=is_staff, is_superuser=is_superuser, is_active=is_active, date_joined=date_joined, last_login=None) con.execute(stmt)
async def generate_reset_split_token(user_id, database=None): """Generate a password reset token for the current user. :param user_id: Int. User id to generate split token for. :param database: String. Database name to connect to. (Default: None - use jawaf.auth default) :return: String. Joined token. """ database = _database_key(database) selector, verifier = _generate_split_token() async with Connection(database) as con: stmt = user_password_reset.insert().values( user_id=user_id, selector=selector, verifier=hashlib.sha256(verifier).hexdigest(), expires=get_utc(datetime.datetime.now() + datetime.timedelta( hours=settings.AUTH_CONFIG['password_reset_expiration'])), ) await con.execute(stmt) return '%s%s' % (selector.decode('utf-8'), verifier.decode('utf-8'))
async def check_user_reset_access(username, user_id, split_token, database=None): """Check password reset token for the current user. :param username: String. Username to check. :param user_id: Int. User_id.. :param split_token: String. Split token to search for and validate against. :param database: String. Database name to connect to. (Default: None - use jawaf.auth default) """ if username is None or user_id is None: return False database = database_key(database) selector = split_token[:SELECTOR_ENCODED_LENGTH].encode('utf-8') verifier = split_token[SELECTOR_ENCODED_LENGTH:].encode('utf-8') async with Connection(database) as con: query = sa.select('*').select_from(user) \ .where(user.c.username == username) \ .where(user.c.id == user_id) row = await con.fetchrow(query) if not row: # username and id don't match! return False query = sa.select('*').select_from(user_password_reset) \ .where(user_password_reset.c.selector == str(selector)) \ .where(user_password_reset.c.user_id == user_id) row = await con.fetchrow(query) if not row: # Selector not found - invalid link. return False if get_utc(datetime.datetime.now()) > row.get('expires'): # First remove the expired record. stmt = user_password_reset.delete().where( user_password_reset.c.id == row.get('id')) await con.execute(stmt) return False if hashlib.sha256(verifier).hexdigest() == row.get('verifier'): # Only allow this reset token to be used once. stmt = user_password_reset.delete().where( user_password_reset.c.id == row.get('id')) await con.execute(stmt) return True return False
def test_password_reset(test_project, waf, create_users): """Test changing password via post.""" change_form_data = { 'username': '******', 'new_password': '******', } selector, verifier = users._generate_split_token() token = '%s%s' % (selector.decode('utf-8'), verifier.decode('utf-8')) with get_engine().connect() as con: stmt = user_password_reset.insert().values( user_id=1, selector=str(selector), verifier=hashlib.sha256(verifier).hexdigest(), expires=get_utc(datetime.datetime.now() + datetime.timedelta(hours=3)), ) con.execute(stmt) encoded_user_id = users.encode_user_id(1) request, response = waf.server.test_client.post( '/auth/password_reset/%s/%s/' % (encoded_user_id, token), data=change_form_data) assert response.status == 200
def test_get_utc(): """Test converting a naive datetime to a utc datetime with timezone info.""" target = datetime.datetime(2017, 4, 11, 0, 0) utc_target = timezone.get_utc(target) assert utc_target.tzinfo == pytz.utc assert utc_target.hour == int(0 - UTC_OFFSET)