# -*- coding: utf-8 -*-
"""
    :codeauthor: :email:`Gareth J. Greenaway <gareth@saltstack.com>`
    :codeauthor: :email:`David Murphy <dmurphy@saltstack.com>`
"""

from __future__ import absolute_import, print_function, unicode_literals

import datetime
import os
import shutil
import time

import salt.modules.gpg as gpg
import salt.utils.files
import salt.utils.platform
from tests.support.helpers import destructiveTest, slowTest
from tests.support.mixins import LoaderModuleMockMixin
from tests.support.mock import MagicMock, patch
from tests.support.runtests import RUNTIME_VARS
from tests.support.unit import TestCase, skipIf

GPG_TEST_KEY_PASSPHRASE = "testkeypassphrase"
GPG_TEST_KEY_ID = "7416F045"
GPG_TEST_PUB_KEY = """-----BEGIN PGP PUBLIC KEY BLOCK-----

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
ylirh0aqUQdSeIuJTg==
=yF8M
-----END PGP PUBLIC KEY BLOCK-----
"""

GPG_TEST_PRIV_KEY = """-----BEGIN PGP PRIVATE KEY BLOCK-----


OZV2Hg+93dg3Wi6g/JW4OuTKWKuHRqpRB1J4i4lO
=WRTN
-----END PGP PRIVATE KEY BLOCK-----
"""

try:
    import gnupg  # pylint: disable=import-error,unused-import

    HAS_GPG = True
except ImportError:
    HAS_GPG = False


@destructiveTest
@skipIf(not salt.utils.platform.is_linux(), "These tests can only be run on linux")
class GpgTestCase(TestCase, LoaderModuleMockMixin):
    """
    Validate the gpg module
    """

    def setup_loader_modules(self):
        return {gpg: {"__salt__": {}}}

    @skipIf(not HAS_GPG, "GPG Module Unavailable")
    def setUp(self):
        super(GpgTestCase, self).setUp()
        self.gpghome = os.path.join(RUNTIME_VARS.TMP, "gpghome")
        if not os.path.isdir(self.gpghome):
            # left behind... Don't fail because of this!
            os.makedirs(self.gpghome)
        self.gpgfile_pub = os.path.join(RUNTIME_VARS.TMP, "gpgfile.pub")
        with salt.utils.files.fopen(self.gpgfile_pub, "wb") as fp:
            fp.write(salt.utils.stringutils.to_bytes(GPG_TEST_PUB_KEY))
        self.gpgfile_priv = os.path.join(RUNTIME_VARS.TMP, "gpgfile.priv")
        with salt.utils.files.fopen(self.gpgfile_priv, "wb") as fp:
            fp.write(salt.utils.stringutils.to_bytes(GPG_TEST_PRIV_KEY))
        self.user = "salt"

    @skipIf(not HAS_GPG, "GPG Module Unavailable")
    def tearDown(self):
        if os.path.isfile(self.gpgfile_pub):
            os.remove(self.gpgfile_pub)
        shutil.rmtree(self.gpghome, ignore_errors=True)
        super(GpgTestCase, self).tearDown()

    @skipIf(not HAS_GPG, "GPG Module Unavailable")
    def test_list_keys(self):
        """
        Test gpg.list_keys
        """

        _user_mock = {
            "shell": "/bin/bash",
            "workphone": "",
            "uid": 0,
            "passwd": "x",
            "roomnumber": "",
            "gid": 0,
            "groups": ["root"],
            "home": "/root",
            "fullname": "root",
            "homephone": "",
            "name": "root",
        }

        _list_result = [
            {
                "dummy": "",
                "keyid": "xxxxxxxxxxxxxxxx",
                "expires": "2011188692",
                "sigs": [],
                "subkeys": [
                    [
                        "xxxxxxxxxxxxxxxx",
                        "e",
                        "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
                    ]
                ],
                "length": "4096",
                "ownertrust": "-",
                "sig": "",
                "algo": "1",
                "fingerprint": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
                "date": "1506612692",
                "trust": "-",
                "type": "pub",
                "uids": ["GPG Person <person@example.com>"],
            }
        ]

        _expected_result = [
            {
                "keyid": "xxxxxxxxxxxxxxxx",
                "uids": ["GPG Person <person@example.com>"],
                "created": "2017-09-28",
                "expires": "2033-09-24",
                "keyLength": "4096",
                "ownerTrust": "Unknown",
                "fingerprint": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
                "trust": "Unknown",
            }
        ]

        mock_opt = MagicMock(return_value="root")
        with patch.dict(
            gpg.__salt__, {"user.info": MagicMock(return_value=_user_mock)}
        ):
            with patch.dict(gpg.__salt__, {"config.option": mock_opt}):
                with patch.object(gpg, "_list_keys", return_value=_list_result):
                    self.assertEqual(gpg.list_keys(), _expected_result)

    @skipIf(not HAS_GPG, "GPG Module Unavailable")
    def test_get_key(self):
        """
        Test gpg.get_key
        """

        _user_mock = {
            "shell": "/bin/bash",
            "workphone": "",
            "uid": 0,
            "passwd": "x",
            "roomnumber": "",
            "gid": 0,
            "groups": ["root"],
            "home": "/root",
            "fullname": "root",
            "homephone": "",
            "name": "root",
        }

        _list_result = [
            {
                "dummy": "",
                "keyid": "xxxxxxxxxxxxxxxx",
                "expires": "2011188692",
                "sigs": [],
                "subkeys": [
                    [
                        "xxxxxxxxxxxxxxxx",
                        "e",
                        "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
                    ]
                ],
                "length": "4096",
                "ownertrust": "-",
                "sig": "",
                "algo": "1",
                "fingerprint": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
                "date": "1506612692",
                "trust": "-",
                "type": "pub",
                "uids": ["GPG Person <person@example.com>"],
            }
        ]

        _expected_result = {
            "fingerprint": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
            "keyid": "xxxxxxxxxxxxxxxx",
            "uids": ["GPG Person <person@example.com>"],
            "created": "2017-09-28",
            "trust": "Unknown",
            "ownerTrust": "Unknown",
            "expires": "2033-09-24",
            "keyLength": "4096",
        }

        mock_opt = MagicMock(return_value="root")
        with patch.dict(
            gpg.__salt__, {"user.info": MagicMock(return_value=_user_mock)}
        ):
            with patch.dict(gpg.__salt__, {"config.option": mock_opt}):
                with patch.object(gpg, "_list_keys", return_value=_list_result):
                    ret = gpg.get_key("xxxxxxxxxxxxxxxx")
                    self.assertEqual(ret, _expected_result)

    @destructiveTest  # Need to run as root!?
    @skipIf(not HAS_GPG, "GPG Module Unavailable")
    def test_delete_key(self):
        """
        Test gpg.delete_key
        """

        _user_mock = {
            "shell": "/bin/bash",
            "workphone": "",
            "uid": 0,
            "passwd": "x",
            "roomnumber": "",
            "gid": 0,
            "groups": ["root"],
            "home": self.gpghome,
            "fullname": "root",
            "homephone": "",
            "name": "root",
        }

        _list_result = [
            {
                "dummy": "",
                "keyid": "xxxxxxxxxxxxxxxx",
                "expires": "2011188692",
                "sigs": [],
                "subkeys": [
                    [
                        "xxxxxxxxxxxxxxxx",
                        "e",
                        "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
                    ]
                ],
                "length": "4096",
                "ownertrust": "-",
                "sig": "",
                "algo": "1",
                "fingerprint": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
                "date": "1506612692",
                "trust": "-",
                "type": "pub",
                "uids": ["GPG Person <person@example.com>"],
            }
        ]

        _expected_result = {
            "res": True,
            "message": "Secret key for xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx deleted\nPublic key for xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx deleted",
        }

        mock_opt = MagicMock(return_value="root")
        with patch.dict(
            gpg.__salt__, {"user.info": MagicMock(return_value=_user_mock)}
        ):
            with patch.dict(gpg.__salt__, {"config.option": mock_opt}):
                with patch.object(gpg, "_list_keys", return_value=_list_result):
                    with patch(
                        "salt.modules.gpg.gnupg.GPG.delete_keys",
                        MagicMock(return_value="ok"),
                    ):
                        ret = gpg.delete_key("xxxxxxxxxxxxxxxx", delete_secret=True)
                        self.assertEqual(ret, _expected_result)

    @skipIf(not HAS_GPG, "GPG Module Unavailable")
    def test_search_keys(self):
        """
        Test gpg.search_keys
        """

        _user_mock = {
            "shell": "/bin/bash",
            "workphone": "",
            "uid": 0,
            "passwd": "x",
            "roomnumber": "",
            "gid": 0,
            "groups": ["root"],
            "home": self.gpghome,
            "fullname": "root",
            "homephone": "",
            "name": "root",
        }

        _search_result = [
            {
                "keyid": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
                "uids": ["GPG Person <person@example.com>"],
                "expires": "",
                "sigs": [],
                "length": "1024",
                "algo": "17",
                "date": int(time.mktime(datetime.datetime(2004, 11, 13).timetuple())),
                "type": "pub",
            }
        ]

        _expected_result = [
            {
                "uids": ["GPG Person <person@example.com>"],
                "created": "2004-11-13",
                "keyLength": "1024",
                "keyid": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
            }
        ]

        mock_opt = MagicMock(return_value="root")
        with patch.dict(
            gpg.__salt__, {"user.info": MagicMock(return_value=_user_mock)}
        ):
            with patch.dict(gpg.__salt__, {"config.option": mock_opt}):
                with patch.object(gpg, "_search_keys", return_value=_search_result):
                    ret = gpg.search_keys("person@example.com")
                    self.assertEqual(ret, _expected_result)

    @skipIf(not HAS_GPG, "GPG Module Unavailable")
    @slowTest
    def test_gpg_import_pub_key(self):
        config_user = MagicMock(return_value="salt")
        user_info = MagicMock(
            return_value={"name": "salt", "home": self.gpghome, "uid": 1000}
        )
        with patch.dict(gpg.__salt__, {"config.option": config_user}):
            with patch.dict(gpg.__salt__, {"user.info": user_info}):
                ret = gpg.import_key(None, self.gpgfile_pub, "salt", self.gpghome)
                self.assertEqual(ret["res"], True)

    @skipIf(not HAS_GPG, "GPG Module Unavailable")
    @slowTest
    def test_gpg_import_priv_key(self):
        config_user = MagicMock(return_value="salt")
        user_info = MagicMock(
            return_value={"name": "salt", "home": self.gpghome, "uid": 1000}
        )
        with patch.dict(gpg.__salt__, {"config.option": config_user}):
            with patch.dict(gpg.__salt__, {"user.info": user_info}):
                ret = gpg.import_key(None, self.gpgfile_priv, "salt", self.gpghome)
                self.assertEqual(ret["res"], True)

    @skipIf(not HAS_GPG, "GPG Module Unavailable")
    @slowTest
    def test_gpg_sign(self):
        config_user = MagicMock(return_value="salt")
        user_info = MagicMock(
            return_value={"name": "salt", "home": self.gpghome, "uid": 1000}
        )
        pillar_mock = MagicMock(
            return_value={"gpg_passphrase": GPG_TEST_KEY_PASSPHRASE}
        )
        with patch.dict(gpg.__salt__, {"config.option": config_user}):
            with patch.dict(gpg.__salt__, {"user.info": user_info}):
                with patch.dict(gpg.__salt__, {"pillar.get": pillar_mock}):
                    ret = gpg.import_key(None, self.gpgfile_priv, "salt", self.gpghome)
                    self.assertEqual(ret["res"], True)
                    gpg_text_input = "The quick brown fox jumped over the lazy dog"
                    gpg_sign_output = gpg.sign(
                        config_user,
                        GPG_TEST_KEY_ID,
                        gpg_text_input,
                        None,
                        None,
                        True,
                        self.gpghome,
                    )
                    self.assertIsNotNone(gpg_sign_output)
