# -*- coding: utf-8 -*-
'''
    tests.integration.shell.auth
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
'''

# Import Python libs
from __future__ import absolute_import, print_function, unicode_literals
import logging
try:
    import pwd
    import grp
except ImportError:
    pwd, grp = None, None
import random

# Import Salt Testing libs
from tests.support.case import ShellCase
from tests.support.unit import skipIf
from tests.support.helpers import destructiveTest, skip_if_not_root

# Import Salt libs
import salt.utils.platform
from salt.utils.pycrypto import gen_hash

# Import 3rd-party libs
from salt.ext.six.moves import range  # pylint: disable=import-error,redefined-builtin

log = logging.getLogger(__name__)


def gen_password():
    '''
    generate a password and hash it
    '''
    alphabet = ('abcdefghijklmnopqrstuvwxyz'
                '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ')
    password = ''
    # generate password
    for _ in range(20):
        next_index = random.randrange(len(alphabet))
        password += alphabet[next_index]

    # hash the password
    hashed_pwd = gen_hash('salt', password, 'sha512')

    return (password, hashed_pwd)


@skip_if_not_root
@skipIf(pwd is None, 'Skip if no pwd module exists')
@destructiveTest
class AuthTest(ShellCase):
    '''
    Test auth mechanisms
    '''

    _call_binary_ = 'salt'

    userA = 'saltdev'
    userB = 'saltadm'
    group = 'saltops'

    def setUp(self):
        for user in (self.userA, self.userB):
            try:
                if salt.utils.platform.is_darwin() and user not in str(self.run_call('user.list_users')):
                    # workaround for https://github.com/saltstack/salt-jenkins/issues/504
                    raise KeyError
                pwd.getpwnam(user)
            except KeyError:
                self.run_call('user.add {0} createhome=False'.format(user))

        # only put userB into the group for the group auth test
        try:
            if salt.utils.platform.is_darwin() and self.group not in str(self.run_call('group.info {0}'.format(self.group))):
                # workaround for https://github.com/saltstack/salt-jenkins/issues/504
                raise KeyError
            grp.getgrnam(self.group)
        except KeyError:
            self.run_call('group.add {0}'.format(self.group))
            self.run_call('user.chgroups {0} {1} True'.format(self.userB, self.group))

    def tearDown(self):
        for user in (self.userA, self.userB):
            try:
                pwd.getpwnam(user)
            except KeyError:
                pass
            else:
                self.run_call('user.delete {0}'.format(user))
        try:
            grp.getgrnam(self.group)
        except KeyError:
            pass
        else:
            self.run_call('group.delete {0}'.format(self.group))

    def test_pam_auth_valid_user(self):
        '''
        test that pam auth mechanism works with a valid user
        '''
        password, hashed_pwd = gen_password()

        # set user password
        set_pw_cmd = "shadow.set_password {0} '{1}'".format(
                self.userA,
                password if salt.utils.platform.is_darwin() else hashed_pwd
        )
        self.run_call(set_pw_cmd)

        # test user auth against pam
        cmd = ('-a pam "*" test.ping '
               '--username {0} --password {1}'.format(self.userA, password))
        resp = self.run_salt(cmd)
        log.debug('resp = %s', resp)
        self.assertTrue(
            'minion:' in resp
        )

    def test_pam_auth_invalid_user(self):
        '''
        test pam auth mechanism errors for an invalid user
        '''
        cmd = ('-a pam "*" test.ping '
               '--username nouser --password {0}'.format('abcd1234'))
        resp = self.run_salt(cmd)
        self.assertTrue(
            'Authentication error occurred.' in ''.join(resp)
        )

    def test_pam_auth_valid_group(self):
        '''
        test that pam auth mechanism works for a valid group
        '''
        password, hashed_pwd = gen_password()

        # set user password
        set_pw_cmd = "shadow.set_password {0} '{1}'".format(
                self.userB,
                password if salt.utils.platform.is_darwin() else hashed_pwd
        )
        self.run_call(set_pw_cmd)

        # test group auth against pam: saltadm is not configured in
        # external_auth, but saltops is and saldadm is a member of saltops
        cmd = ('-a pam "*" test.ping '
               '--username {0} --password {1}'.format(self.userB, password))
        resp = self.run_salt(cmd)
        self.assertTrue(
            'minion:' in resp
        )
