from django.contrib.auth.models import User
from rest_framework.authtoken.models import Token
from django.test.utils import override_settings
from api.tests import DeisTestCase


class TestAdminPerms(DeisTestCase):

    def test_first_signup(self):
        # register a first user
        username, password = 'firstuser', 'password'
        email = 'autotest@deis.io'
        submit = {
            'username': username,
            'password': password,
            'email': email,
        }
        url = '/v2/auth/register'
        response = self.client.post(url, submit)
        self.assertEqual(response.status_code, 201, response.data)
        self.assertTrue(response.data['is_superuser'])

        # register a second user
        username, password = 'seconduser', 'password'
        email = 'autotest@deis.io'
        submit = {
            'username': username,
            'password': password,
            'email': email,
        }
        url = '/v2/auth/register'
        response = self.client.post(url, submit)
        self.assertEqual(response.status_code, 201, response.data)
        self.assertFalse(response.data['is_superuser'])

    @override_settings(REGISTRATION_MODE="admin_only")
    def test_first_signup_admin_only(self):
        # register a first user
        username, password = 'firstuser', 'password'
        email = 'autotest@deis.io'
        submit = {
            'username': username,
            'password': password,
            'email': email,
        }
        url = '/v2/auth/register'
        response = self.client.post(url, submit)
        self.assertEqual(response.status_code, 201, response.data)
        self.assertTrue(response.data['is_superuser'])

    def test_list(self):
        submit = {
            'username': 'firstuser',
            'password': 'password',
            'email': 'autotest@deis.io',
        }
        url = '/v2/auth/register'
        response = self.client.post(url, submit)
        self.assertEqual(response.status_code, 201, response.data)
        self.assertTrue(response.data['is_superuser'])

        user = User.objects.get(username='firstuser')
        token = Token.objects.get(user=user).key
        self.client.credentials(HTTP_AUTHORIZATION='Token ' + token)

        response = self.client.get('/v2/admin/perms')
        self.assertEqual(response.status_code, 200, response.data)
        self.assertEqual(len(response.data['results']), 1)
        self.assertEqual(response.data['results'][0]['username'], 'firstuser')
        self.assertTrue(response.data['results'][0]['is_superuser'])

        # register a non-superuser
        submit = {
            'username': 'seconduser',
            'password': 'password',
            'email': 'autotest@deis.io',
        }
        url = '/v2/auth/register'
        response = self.client.post(url, submit)
        self.assertEqual(response.status_code, 201, response.data)
        self.assertFalse(response.data['is_superuser'])

        user = User.objects.get(username='seconduser')
        token = Token.objects.get(user=user).key
        self.client.credentials(HTTP_AUTHORIZATION='Token ' + token)

        response = self.client.get('/v2/admin/perms')
        self.assertEqual(response.status_code, 403)
        self.assertIn('You do not have permission', response.data['detail'])

    def test_create(self):
        submit = {
            'username': 'first',
            'password': 'password',
            'email': 'autotest@deis.io',
        }
        url = '/v2/auth/register'
        response = self.client.post(url, submit)
        self.assertEqual(response.status_code, 201, response.data)
        self.assertTrue(response.data['is_superuser'])

        submit = {
            'username': 'second',
            'password': 'password',
            'email': 'autotest@deis.io',
        }
        url = '/v2/auth/register'
        response = self.client.post(url, submit)
        self.assertEqual(response.status_code, 201, response.data)
        self.assertFalse(response.data['is_superuser'])

        user = User.objects.get(username='first')
        token = Token.objects.get(user=user).key
        self.client.credentials(HTTP_AUTHORIZATION='Token ' + token)
        # grant user 2 the superuser perm
        url = '/v2/admin/perms'
        body = {'username': 'second'}
        response = self.client.post(url, body)
        self.assertEqual(response.status_code, 201, response.data)
        response = self.client.get(url)
        self.assertEqual(response.status_code, 200, response.data)
        self.assertEqual(len(response.data['results']), 2)
        self.assertIn('second', str(response.data['results']))

    def test_delete(self):
        submit = {
            'username': 'first',
            'password': 'password',
            'email': 'autotest@deis.io',
        }
        url = '/v2/auth/register'
        response = self.client.post(url, submit)
        self.assertEqual(response.status_code, 201, response.data)
        self.assertTrue(response.data['is_superuser'])

        submit = {
            'username': 'second.lastname',
            'password': 'password',
            'email': 'autotest@deis.io',
        }
        url = '/v2/auth/register'
        response = self.client.post(url, submit)
        self.assertEqual(response.status_code, 201, response.data)
        self.assertFalse(response.data['is_superuser'])

        user = User.objects.get(username='first')
        token = Token.objects.get(user=user).key
        self.client.credentials(HTTP_AUTHORIZATION='Token ' + token)
        # grant user 2 the superuser perm
        url = '/v2/admin/perms'
        body = {'username': 'second.lastname'}
        response = self.client.post(url, body)
        self.assertEqual(response.status_code, 201, response.data)

        # revoke the superuser perm
        response = self.client.delete(url + '/second.lastname')
        self.assertEqual(response.status_code, 204, response.data)
        response = self.client.get(url)
        self.assertEqual(response.status_code, 200, response.data)
        self.assertEqual(len(response.data['results']), 1)
        self.assertNotIn('two', str(response.data['results']))


class TestAppPerms(DeisTestCase):

    fixtures = ['test_sharing.json']

    def setUp(self):
        self.user = User.objects.get(username='autotest-1')
        self.token = Token.objects.get(user=self.user).key
        # Always have first user authed coming into tests
        self.client.credentials(HTTP_AUTHORIZATION='Token ' + self.token)

        self.user2 = User.objects.get(username='autotest-2')
        self.token2 = Token.objects.get(user=self.user2).key
        self.user3 = User.objects.get(username='autotest-3')
        self.token3 = Token.objects.get(user=self.user3).key

    def test_create(self):
        # check that user 1 sees her lone app and user 2's app
        self.client.credentials(HTTP_AUTHORIZATION='Token ' + self.token)
        response = self.client.get('/v2/apps')
        self.assertEqual(response.status_code, 200, response.data)
        self.assertEqual(len(response.data['results']), 2)
        app_id = response.data['results'][0]['id']

        # check that user 2 can only see his app
        self.client.credentials(HTTP_AUTHORIZATION='Token ' + self.token2)
        response = self.client.get('/v2/apps')
        self.assertEqual(len(response.data['results']), 1)
        # check that user 2 can't see any of the app's builds, configs,
        # containers, limits, or releases
        for model in ['builds', 'config', 'pods', 'releases']:
            response = self.client.get("/v2/apps/{}/{}/".format(app_id, model))
            msg = "Failed: status '%s', and data '%s'" % (response.status_code, response.data)
            self.assertEqual(response.status_code, 403, msg=msg)
            self.assertEqual(response.data['detail'],
                             'You do not have permission to perform this action.', msg=msg)

        # TODO: test that git pushing to the app fails
        # give user 2 permission to user 1's app
        url = "/v2/apps/{}/perms".format(app_id)
        body = {'username': 'autotest-2'}
        self.client.credentials(HTTP_AUTHORIZATION='Token ' + self.token)
        response = self.client.post(url, body)
        self.assertEqual(response.status_code, 201, response.data)

        # check that user 2 can see the app
        self.client.credentials(HTTP_AUTHORIZATION='Token ' + self.token2)
        response = self.client.get('/v2/apps')
        self.assertEqual(response.status_code, 200, response.data)
        self.assertEqual(len(response.data['results']), 2)

        # check that user 2 sees (empty) results now for builds, containers,
        # and releases. (config and limit will still give 404s since we didn't
        # push a build here.)
        for model in ['builds', 'releases']:
            response = self.client.get("/v2/apps/{}/{}/".format(app_id, model))
            self.assertEqual(len(response.data['results']), 0)
        # TODO:  check that user 2 can git push the app

    def test_create_errors(self):
        # check that user 1 sees her lone app
        response = self.client.get('/v2/apps')
        app_id = response.data['results'][0]['id']

        # check that user 2 can't create a permission
        url = "/v2/apps/{}/perms".format(app_id)
        body = {'username': 'autotest-2'}
        self.client.credentials(HTTP_AUTHORIZATION='Token ' + self.token2)
        response = self.client.post(url, body)
        self.assertEqual(response.status_code, 403)

    def test_delete(self):
        # give user 2 permission to user 1's app
        response = self.client.get('/v2/apps')
        app_id = response.data['results'][0]['id']
        url = "/v2/apps/{}/perms".format(app_id)
        body = {'username': 'autotest-2'}
        response = self.client.post(url, body)
        self.assertEqual(response.status_code, 201, response.data)

        # check that user 2 can see the app as well as his own
        self.client.credentials(HTTP_AUTHORIZATION='Token ' + self.token2)
        response = self.client.get('/v2/apps')
        self.assertEqual(response.status_code, 200, response.data)
        self.assertEqual(len(response.data['results']), 2)

        # delete permission to user 1's app
        self.client.credentials(HTTP_AUTHORIZATION='Token ' + self.token)
        url = "/v2/apps/{}/perms/{}".format(app_id, 'autotest-2')
        response = self.client.delete(url)
        self.assertEqual(response.status_code, 204, response.data)
        self.assertIsNone(response.data)

        # check that user 2 can only see his app
        self.client.credentials(HTTP_AUTHORIZATION='Token ' + self.token2)
        response = self.client.get('/v2/apps')
        self.assertEqual(len(response.data['results']), 1)

        # delete permission to user 1's app again, expecting an error
        self.client.credentials(HTTP_AUTHORIZATION='Token ' + self.token)
        response = self.client.delete(url)
        self.assertEqual(response.status_code, 403)

    def test_list(self):
        # check that user 1 sees her lone app and user 2's app
        response = self.client.get('/v2/apps')
        self.assertEqual(response.status_code, 200, response.data)
        self.assertEqual(len(response.data['results']), 2)
        app_id = response.data['results'][0]['id']

        # create a new object permission
        url = "/v2/apps/{}/perms".format(app_id)
        body = {'username': 'autotest-2'}
        response = self.client.post(url, body)
        self.assertEqual(response.status_code, 201, response.data)

        # list perms on the app
        response = self.client.get("/v2/apps/{}/perms".format(app_id))
        self.assertEqual(response.data, {'users': ['autotest-2']})

    def test_admin_can_list(self):
        """Check that an administrator can list an app's perms"""
        response = self.client.get('/v2/apps')
        self.assertEqual(response.status_code, 200, response.data)
        self.assertEqual(len(response.data['results']), 2)

    def test_list_errors(self):
        response = self.client.get('/v2/apps')
        app_id = response.data['results'][0]['id']

        # login as user 2, list perms on the app
        self.client.credentials(HTTP_AUTHORIZATION='Token ' + self.token2)
        response = self.client.get("/v2/apps/{}/perms".format(app_id))
        self.assertEqual(response.status_code, 403)

    def test_unauthorized_user_cannot_modify_perms(self):
        """
        An unauthorized user should not be able to modify other apps' permissions.

        Since an unauthorized user should not know about the application at all, these
        requests should return a 404.
        """
        app_id = 'autotest'
        url = '/v2/apps'
        body = {'id': app_id}
        response = self.client.post(url, body)

        url = '{}/{}/perms'.format(url, app_id)
        body = {'username': self.user2.username}
        self.client.credentials(HTTP_AUTHORIZATION='Token ' + self.token2)
        response = self.client.post(url, body)
        self.assertEqual(response.status_code, 403)

    def test_collaborator_cannot_share(self):
        """
        An collaborator should not be able to modify the app's permissions.
        """
        app_id = "autotest-1-app"
        owner_token = self.token
        collab = self.user2
        collab_token = self.token2
        url = '/v2/apps/{}/perms'.format(app_id)

        # Share app with collaborator
        body = {'username': collab.username}
        self.client.credentials(HTTP_AUTHORIZATION='Token ' + owner_token)
        response = self.client.post(url, body)
        self.assertEqual(response.status_code, 201, response.data)

        # Collaborator should fail to share app
        self.client.credentials(HTTP_AUTHORIZATION='Token ' + collab_token)
        body = {'username': self.user3.username}
        response = self.client.post(url, body)
        self.assertEqual(response.status_code, 403)

        # Collaborator can list
        response = self.client.get(url)
        self.assertEqual(response.status_code, 200, response.data)

        # Share app with user 3 for rest of tests
        self.client.credentials(HTTP_AUTHORIZATION='Token ' + owner_token)
        response = self.client.post(url, body)
        self.assertEqual(response.status_code, 201, response.data)

        self.client.credentials(HTTP_AUTHORIZATION='Token ' + collab_token)
        response = self.client.get(url)
        self.assertEqual(response.status_code, 200, response.data)

        # Collaborator cannot delete other collaborator
        url += "/{}".format(self.user3.username)
        response = self.client.delete(url)
        self.assertEqual(response.status_code, 403)

        # Collaborator can delete themselves
        url = '/v2/apps/{}/perms/{}'.format(app_id, collab.username)
        response = self.client.delete(url)
        self.assertEqual(response.status_code, 204, response.data)