206 lines
7.1 KiB
Python
206 lines
7.1 KiB
Python
|
from datetime import datetime
|
||
|
|
||
|
import pytest
|
||
|
|
||
|
from mock import patch
|
||
|
|
||
|
from data.database import EmailConfirmation, User, DeletedNamespace
|
||
|
from data.model.organization import get_organization
|
||
|
from data.model.notification import create_notification
|
||
|
from data.model.team import create_team, add_user_to_team
|
||
|
from data.model.user import create_user_noverify, validate_reset_code, get_active_users
|
||
|
from data.model.user import mark_namespace_for_deletion, delete_namespace_via_marker
|
||
|
from data.model.user import create_robot, lookup_robot, list_namespace_robots
|
||
|
from data.model.user import get_pull_credentials, retrieve_robot_token, verify_robot
|
||
|
from data.model.user import InvalidRobotException, delete_robot, get_matching_users
|
||
|
from data.model.repository import create_repository
|
||
|
from data.fields import Credential
|
||
|
from data.queue import WorkQueue
|
||
|
from util.timedeltastring import convert_to_timedelta
|
||
|
from util.timedeltastring import convert_to_timedelta
|
||
|
from util.security.token import encode_public_private_token
|
||
|
from test.fixtures import *
|
||
|
|
||
|
def test_create_user_with_expiration(initialized_db):
|
||
|
with patch('data.model.config.app_config', {'DEFAULT_TAG_EXPIRATION': '1h'}):
|
||
|
user = create_user_noverify('foobar', 'foo@example.com', email_required=False)
|
||
|
assert user.removed_tag_expiration_s == 60 * 60
|
||
|
|
||
|
@pytest.mark.parametrize('token_lifetime, time_since', [
|
||
|
('1m', '2m'),
|
||
|
('2m', '1m'),
|
||
|
('1h', '1m'),
|
||
|
])
|
||
|
def test_validation_code(token_lifetime, time_since, initialized_db):
|
||
|
user = create_user_noverify('foobar', 'foo@example.com', email_required=False)
|
||
|
created = datetime.now() - convert_to_timedelta(time_since)
|
||
|
verification_code, unhashed = Credential.generate()
|
||
|
confirmation = EmailConfirmation.create(user=user, pw_reset=True,
|
||
|
created=created, verification_code=verification_code)
|
||
|
encoded = encode_public_private_token(confirmation.code, unhashed)
|
||
|
|
||
|
with patch('data.model.config.app_config', {'USER_RECOVERY_TOKEN_LIFETIME': token_lifetime}):
|
||
|
result = validate_reset_code(encoded)
|
||
|
expect_success = convert_to_timedelta(token_lifetime) >= convert_to_timedelta(time_since)
|
||
|
assert expect_success == (result is not None)
|
||
|
|
||
|
|
||
|
@pytest.mark.parametrize('disabled', [
|
||
|
(True),
|
||
|
(False),
|
||
|
])
|
||
|
@pytest.mark.parametrize('deleted', [
|
||
|
(True),
|
||
|
(False),
|
||
|
])
|
||
|
def test_get_active_users(disabled, deleted, initialized_db):
|
||
|
# Delete a user.
|
||
|
deleted_user = model.user.get_user('public')
|
||
|
queue = WorkQueue('testgcnamespace', lambda db: db.transaction())
|
||
|
mark_namespace_for_deletion(deleted_user, [], queue)
|
||
|
|
||
|
users = get_active_users(disabled=disabled, deleted=deleted)
|
||
|
deleted_found = [user for user in users if user.id == deleted_user.id]
|
||
|
assert bool(deleted_found) == (deleted and disabled)
|
||
|
|
||
|
for user in users:
|
||
|
if not disabled:
|
||
|
assert user.enabled
|
||
|
|
||
|
|
||
|
def test_mark_namespace_for_deletion(initialized_db):
|
||
|
def create_transaction(db):
|
||
|
return db.transaction()
|
||
|
|
||
|
# Create a user and then mark it for deletion.
|
||
|
user = create_user_noverify('foobar', 'foo@example.com', email_required=False)
|
||
|
|
||
|
# Add some robots.
|
||
|
create_robot('foo', user)
|
||
|
create_robot('bar', user)
|
||
|
|
||
|
assert lookup_robot('foobar+foo') is not None
|
||
|
assert lookup_robot('foobar+bar') is not None
|
||
|
assert len(list(list_namespace_robots('foobar'))) == 2
|
||
|
|
||
|
# Mark the user for deletion.
|
||
|
queue = WorkQueue('testgcnamespace', create_transaction)
|
||
|
mark_namespace_for_deletion(user, [], queue)
|
||
|
|
||
|
# Ensure the older user is still in the DB.
|
||
|
older_user = User.get(id=user.id)
|
||
|
assert older_user.username != 'foobar'
|
||
|
|
||
|
# Ensure the robots are deleted.
|
||
|
with pytest.raises(InvalidRobotException):
|
||
|
assert lookup_robot('foobar+foo')
|
||
|
|
||
|
with pytest.raises(InvalidRobotException):
|
||
|
assert lookup_robot('foobar+bar')
|
||
|
|
||
|
assert len(list(list_namespace_robots(older_user.username))) == 0
|
||
|
|
||
|
# Ensure we can create a user with the same namespace again.
|
||
|
new_user = create_user_noverify('foobar', 'foo@example.com', email_required=False)
|
||
|
assert new_user.id != user.id
|
||
|
|
||
|
# Ensure the older user is still in the DB.
|
||
|
assert User.get(id=user.id).username != 'foobar'
|
||
|
|
||
|
|
||
|
def test_delete_namespace_via_marker(initialized_db):
|
||
|
def create_transaction(db):
|
||
|
return db.transaction()
|
||
|
|
||
|
# Create a user and then mark it for deletion.
|
||
|
user = create_user_noverify('foobar', 'foo@example.com', email_required=False)
|
||
|
|
||
|
# Add some repositories.
|
||
|
create_repository('foobar', 'somerepo', user)
|
||
|
create_repository('foobar', 'anotherrepo', user)
|
||
|
|
||
|
# Mark the user for deletion.
|
||
|
queue = WorkQueue('testgcnamespace', create_transaction)
|
||
|
marker_id = mark_namespace_for_deletion(user, [], queue)
|
||
|
|
||
|
# Delete the user.
|
||
|
delete_namespace_via_marker(marker_id, [])
|
||
|
|
||
|
# Ensure the user was actually deleted.
|
||
|
with pytest.raises(User.DoesNotExist):
|
||
|
User.get(id=user.id)
|
||
|
|
||
|
with pytest.raises(DeletedNamespace.DoesNotExist):
|
||
|
DeletedNamespace.get(id=marker_id)
|
||
|
|
||
|
|
||
|
def test_delete_robot(initialized_db):
|
||
|
# Create a robot account.
|
||
|
user = create_user_noverify('foobar', 'foo@example.com', email_required=False)
|
||
|
robot, _ = create_robot('foo', user)
|
||
|
|
||
|
# Add some notifications and other rows pointing to the robot.
|
||
|
create_notification('repo_push', robot)
|
||
|
|
||
|
team = create_team('someteam', get_organization('buynlarge'), 'member')
|
||
|
add_user_to_team(robot, team)
|
||
|
|
||
|
# Ensure the robot exists.
|
||
|
assert lookup_robot(robot.username).id == robot.id
|
||
|
|
||
|
# Delete the robot.
|
||
|
delete_robot(robot.username)
|
||
|
|
||
|
# Ensure it is gone.
|
||
|
with pytest.raises(InvalidRobotException):
|
||
|
lookup_robot(robot.username)
|
||
|
|
||
|
|
||
|
def test_get_matching_users(initialized_db):
|
||
|
# Exact match.
|
||
|
for user in User.select().where(User.organization == False, User.robot == False):
|
||
|
assert list(get_matching_users(user.username))[0].username == user.username
|
||
|
|
||
|
# Prefix matching.
|
||
|
for user in User.select().where(User.organization == False, User.robot == False):
|
||
|
assert user.username in [r.username for r in get_matching_users(user.username[:2])]
|
||
|
|
||
|
|
||
|
def test_get_matching_users_with_same_prefix(initialized_db):
|
||
|
# Create a bunch of users with the same prefix.
|
||
|
for index in range(0, 20):
|
||
|
create_user_noverify('foo%s' % index, 'foo%s@example.com' % index, email_required=False)
|
||
|
|
||
|
# For each user, ensure that lookup of the exact name is found first.
|
||
|
for index in range(0, 20):
|
||
|
username = 'foo%s' % index
|
||
|
assert list(get_matching_users(username))[0].username == username
|
||
|
|
||
|
# Prefix matching.
|
||
|
found = list(get_matching_users('foo', limit=50))
|
||
|
assert len(found) == 20
|
||
|
|
||
|
|
||
|
def test_robot(initialized_db):
|
||
|
# Create a robot account.
|
||
|
user = create_user_noverify('foobar', 'foo@example.com', email_required=False)
|
||
|
robot, token = create_robot('foo', user)
|
||
|
assert retrieve_robot_token(robot) == token
|
||
|
|
||
|
# Ensure we can retrieve its information.
|
||
|
found = lookup_robot('foobar+foo')
|
||
|
assert found == robot
|
||
|
|
||
|
creds = get_pull_credentials('foobar+foo')
|
||
|
assert creds is not None
|
||
|
assert creds['username'] == 'foobar+foo'
|
||
|
assert creds['password'] == token
|
||
|
|
||
|
assert verify_robot('foobar+foo', token) == robot
|
||
|
|
||
|
with pytest.raises(InvalidRobotException):
|
||
|
assert verify_robot('foobar+foo', 'someothertoken')
|
||
|
|
||
|
with pytest.raises(InvalidRobotException):
|
||
|
assert verify_robot('foobar+unknownbot', token)
|