From ed484f43a2a42b0f62698525dea5c460f0b648f6 Mon Sep 17 00:00:00 2001 From: Tim Kelsey Date: Tue, 7 Apr 2015 16:31:56 +0100 Subject: [PATCH] Added tests to bring coverage up to 100% of validators Added validator tests and fixed up some bugs. Change-Id: I21862a8ee770fb8bb7e0413f4d05c6a40c523309 co-authored-by: Robert Clark does not describe a valid" - " network", cidr) - raise ValidationError("No network matched the request source <%s>", + raise ValidationError("Cidr '%s' does not describe a valid" + " network" % cidr) + raise ValidationError("No network matched the request source '%s'" % request.client_addr) diff --git a/tests/validators/test_callable_validators.py b/tests/validators/test_callable_validators.py index 249e1f2..736747b 100644 --- a/tests/validators/test_callable_validators.py +++ b/tests/validators/test_callable_validators.py @@ -14,131 +14,23 @@ # License for the specific language governing permissions and limitations # under the License. -import textwrap import unittest import mock from anchor import validators -from anchor.X509 import name -from anchor.X509 import signing_request +from anchor.X509 import name as x509_name class TestValidators(unittest.TestCase): - csr_data_good_subjectAltNames = textwrap.dedent(""" - -----BEGIN CERTIFICATE REQUEST----- - MIIEDzCCAncCAQAwcjELMAkGA1UEBhMCR0IxEzARBgNVBAgTCkNhbGlmb3JuaWEx - FjAUBgNVBAcTDVNhbiBGcmFuY3NpY28xDTALBgNVBAoTBE9TU0cxDTALBgNVBAsT - BE9TU0cxGDAWBgNVBAMTD21hc3Rlci50ZXN0LmNvbTCCAaIwDQYJKoZIhvcNAQEB - BQADggGPADCCAYoCggGBALnhCRvwMoaZa4car663lwcwn86PO3BS90X8b2wIZjkf - rq/eePz2J3Ox8/BbsYiYICHn8oSd/VVXUnqHMFU9xTeJwsDLbyc+0P4S9Fj+RkbM - W+YQZsG8Wy9M8aKi9hNtIGiqknyzcOfCQcGPpcKqXRXAW1afqLmifBcFqN1qcpT8 - OooGNtgo4Ix/fA7omZaKkIXSi5FovC8mFPUm2VqDyvctxBGq0EngIOB9rczloun0 - nO8PpWBsX2rg3uIs6GIejVrx1ZkcHxJbrze/Nt9vt4C11hJAiAUlHDl0cf50/Pck - g0T3ehEqr0zdzCx+wXr3AzStcoOow+REb8CbTt2QaUbZ5izrZFX0JC73mRtqDhuc - UxUaguLK9ufhUfA0I1j++w/pQkBEu5PGNX7YpRLImEp636lD8RJ9Ced7oii+gjY0 - OXlVPRv9MMPvkCWnjNjLapz8kzypJr94BQz1AffHxVfmGGQh60vq4KINm+etuI0Q - kfI9NRa/ficRhsuh7yxQRwIDAQABoFgwVgYJKoZIhvcNAQkOMUkwRzAJBgNVHRME - AjAAMAsGA1UdDwQEAwIF4DAtBgNVHREEJjAkghBzZXJ2ZXIxLnRlc3QuY29tghBz - ZXJ2ZXIyLnRlc3QuY29tMA0GCSqGSIb3DQEBCwUAA4IBgQBdyATuNnfVIeQL2odc - zV7f9c/tvN5/Mn4AmGt5S457FGO/s3J7hWX9L02VYPWwORbtkBvZZKtQWLjHbMzU - oGsfxeo6vUv+dSP6bjqKibFyMArdaRIobFMvM/5N6g9zcP4sQEnpUyIeV2g6b0Os - FoKGsLPIMiS69mAVdfKrgXnmXApXu5zjAoPnSzcc+wKTCbzVIRLZIopEtet84atN - 7Tf9xokgrDZppJE76w3zXYWPkUDbVuWTuO4afQxujHbJYiZblxJz/gRbMgugAt4V - ftlI3EGnGaBQHcZfmyZz1F8ti1jteWMMQZHtWr32cF9Lw/jd2adYFYVTez3BXtQW - pULCxdq8G2CFdrV/atIL8Vadf2dOzn2tZIFFihzuilWbcmTP7+8UI8MOKkrqfWN+ - Q6yV3I896rSprU7WAmWSq+jXkOOwNGDEbmaWsxu4AjvfGty5v2lZqdYJRkbjerXD - tR7XqQGqJKca/vRTfJ+zIAxMEeH1N9Lx7YBO6VdVja+yG1E= - -----END CERTIFICATE REQUEST-----""") - """ - Subject: C=GB, ST=California, L=San Francsico, - O=OSSG, OU=OSSG, CN=master.test.com - X509v3 Subject Alternative Name: - DNS:server1.test.com, DNS:server2.test.com - """ - - csr_data_bad_subjectAltNames = textwrap.dedent(""" - -----BEGIN CERTIFICATE REQUEST----- - MIID9zCCAl8CAQAwXTELMAkGA1UEBhMCR0IxEzARBgNVBAgTCkNhbGlmb3JuaWEx - ITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEWMBQGA1UEAxMNb3Nz - Zy50ZXN0LmNvbTCCAaIwDQYJKoZIhvcNAQEBBQADggGPADCCAYoCggGBALnhCRvw - MoaZa4car663lwcwn86PO3BS90X8b2wIZjkfrq/eePz2J3Ox8/BbsYiYICHn8oSd - /VVXUnqHMFU9xTeJwsDLbyc+0P4S9Fj+RkbMW+YQZsG8Wy9M8aKi9hNtIGiqknyz - cOfCQcGPpcKqXRXAW1afqLmifBcFqN1qcpT8OooGNtgo4Ix/fA7omZaKkIXSi5Fo - vC8mFPUm2VqDyvctxBGq0EngIOB9rczloun0nO8PpWBsX2rg3uIs6GIejVrx1Zkc - HxJbrze/Nt9vt4C11hJAiAUlHDl0cf50/Pckg0T3ehEqr0zdzCx+wXr3AzStcoOo - w+REb8CbTt2QaUbZ5izrZFX0JC73mRtqDhucUxUaguLK9ufhUfA0I1j++w/pQkBE - u5PGNX7YpRLImEp636lD8RJ9Ced7oii+gjY0OXlVPRv9MMPvkCWnjNjLapz8kzyp - Jr94BQz1AffHxVfmGGQh60vq4KINm+etuI0QkfI9NRa/ficRhsuh7yxQRwIDAQAB - oFUwUwYJKoZIhvcNAQkOMUYwRDAJBgNVHRMEAjAAMAsGA1UdDwQEAwIF4DAqBgNV - HREEIzAhghBzZXJ2ZXIxLnRlc3QuY29tgg1iYWRkb21haW4uY29tMA0GCSqGSIb3 - DQEBCwUAA4IBgQCIrwFbBoH35np+aKlxqQmNdy+QwK+S1cJvASzKSRmj+/7cCMsg - YRofCBtNykkfXIsPQUiU2IACtMu8glrWtAham888XcWx1qBH9W4NEAWiQflvvxEI - cGaTJhsLSmEhs74dnjQK+qvY6vIdp0+40VFUbTks5kb+EFnoqta0Ih92r9zX/S6X - 9JIFA2I5fBwoMZ0CouXuwdOz5rN1OUaHhVb13J6iWY6luJElQjTodyCsfyBP2F8q - ofPcJwH10Y2njV1L3n3R3DyCAJpRl4DVUJ5C8nyKAbrIDmwU1bTiY4xl4/HuKHWx - 3bJjouPkjVyipwhT5D+gWh9bYrZH0wHmdh/gHufyHAXngsYvK45Z+8nC9+ci7QAv - mU1RmrrwfirhG8RU40XwLs5Z+FTD54Hp5bqxpLXiyQfutnSGyQUQe9jRoa499xVE - zsp579hz6O9CjqfkRdjY187Rzwj5vGVf2ZN/pCMiyQd8HwQ/81QNe6hkZrOKbnuK - GEHeTtZwEvzgF9s= - -----END CERTIFICATE REQUEST-----""") - """ - Subject: C=GB, ST=California, CN=ossg.test.com - X509v3 Subject Alternative Name: - DNS:server1.test.com, DNS:baddomain.com - """ - - csr_data_good_subjectAltNames_IP = textwrap.dedent(""" - -----BEGIN CERTIFICATE REQUEST----- - MIID/zCCAmcCAQAwXzELMAkGA1UEBhMCR0IxEzARBgNVBAgTCkNhbGlmb3JuaWEx - ITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEYMBYGA1UEAxMPbWFz - dGVyLnRlc3QuY29tMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAueEJ - G/AyhplrhxqvrreXBzCfzo87cFL3RfxvbAhmOR+ur954/PYnc7Hz8FuxiJggIefy - hJ39VVdSeocwVT3FN4nCwMtvJz7Q/hL0WP5GRsxb5hBmwbxbL0zxoqL2E20gaKqS - fLNw58JBwY+lwqpdFcBbVp+ouaJ8FwWo3WpylPw6igY22CjgjH98DuiZloqQhdKL - kWi8LyYU9SbZWoPK9y3EEarQSeAg4H2tzOWi6fSc7w+lYGxfauDe4izoYh6NWvHV - mRwfEluvN78232+3gLXWEkCIBSUcOXRx/nT89ySDRPd6ESqvTN3MLH7BevcDNK1y - g6jD5ERvwJtO3ZBpRtnmLOtkVfQkLveZG2oOG5xTFRqC4sr25+FR8DQjWP77D+lC - QES7k8Y1ftilEsiYSnrfqUPxEn0J53uiKL6CNjQ5eVU9G/0ww++QJaeM2MtqnPyT - PKkmv3gFDPUB98fFV+YYZCHrS+rgog2b5624jRCR8j01Fr9+JxGGy6HvLFBHAgMB - AAGgWzBZBgkqhkiG9w0BCQ4xTDBKMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgXgMDAG - A1UdEQQpMCeCEHNlcnZlcjEudGVzdC5jb22CDWJhZGRvbWFpbi5jb22HBAoBAQEw - DQYJKoZIhvcNAQELBQADggGBAGLyVNz4W91YR68MSD1RLnaKuJJJLAMeRmx+F33A - gQ1gKK6w6que/Qd1jANvsZtSJ3Yei6elbDsepgso63lb6XjdxVDEKv565/PeixF2 - zCDkqtWAlVmcP7RV+zi+6yCNWOToLkbhxnMNYSnvFgXMZ8zoAyGyOVBOiejXxaLh - 84jMnVghGjaRrvUGt36NFptrTC7gHDkBIDoPrRzl3xNREGWijem+Z8PqdORp3LYl - KaekBJmrun4VMXyW+2+M70mWB/nBsT4SEnFOvWaIEnPrZ5E0GhLcQretmxwrTnV1 - FyYBoTpJUjgTruIj3TULrV/GGIfT5TPZtbEEcb4neX1XPzRAbkYcIrO44PUo8wZn - w4KlyZ1wtNxwCp6nKWw857P/gGMQ2hdvR57uw4EWv7ogbjZ24kKYqKiTrtoDyMXh - J3m40t2hSw0L5HNFXI5FlWMiF3oWAyhKA8GTQ9d95FM015KYsY0XeVbIT73Du+ai - yH77Of+HVIFAaZAG6NnPu8t1eg== - -----END CERTIFICATE REQUEST-----""") - """ - Subject: C=GB, ST=California, CN=master.test.com - X509v3 Subject Alternative Name: - DNS:server1.test.com, DNS:baddomain.com, IP Address:10.1.1.1 - """ - def setUp(self): super(TestValidators, self).setUp() - self.csr_good_sub_alt_names = signing_request.X509Csr() - self.csr_good_sub_alt_names.from_buffer( - TestValidators.csr_data_good_subjectAltNames) - - self.csr_bad_sub_alt_names = signing_request.X509Csr() - self.csr_bad_sub_alt_names.from_buffer( - TestValidators.csr_data_bad_subjectAltNames) - - self.csr_good_sub_alt_names_IP = signing_request.X509Csr() - self.csr_good_sub_alt_names_IP.from_buffer( - TestValidators.csr_data_good_subjectAltNames_IP) - def tearDown(self): super(TestValidators, self).tearDown() @mock.patch('socket.gethostbyname_ex') - def test_check_networks_both(self, gethostbyname_ex): + def test_check_networks_good(self, gethostbyname_ex): allowed_networks = ['15/8', '74.125/16'] gethostbyname_ex.return_value = ( 'example.com', @@ -155,6 +47,9 @@ class TestValidators(unittest.TestCase): self.assertTrue(validators.check_networks_strict( 'example.com', allowed_networks)) + @mock.patch('socket.gethostbyname_ex') + def test_check_networks_bad(self, gethostbyname_ex): + allowed_networks = ['15/8', '74.125/16'] gethostbyname_ex.return_value = ('example.com', [], ['12.2.2.2']) self.assertFalse(validators.check_networks( 'example.com', allowed_networks)) @@ -171,40 +66,64 @@ class TestValidators(unittest.TestCase): self.assertFalse(validators.check_networks_strict( 'example.com', allowed_networks)) - @mock.patch('anchor.X509.name.X509Name.get_entries_by_nid_name') - def test_common_name_with_two_CN(self, get_entries_by_nid_name): - get_entries_by_nid_name.return_value = ['1', '2'] - self.assertRaises( - validators.ValidationError, - validators.common_name, - self.csr_good_sub_alt_names, - allowed_domains=[], - allowed_networks=[] - ) + @mock.patch('socket.gethostbyname_ex') + def test_check_domains_empty(self, gethostbyname_ex): + gethostbyname_ex.return_value = ('example.com', [], ['12.2.2.2']) + self.assertTrue(validators.check_domains( + 'example.com', [])) - @mock.patch('anchor.X509.signing_request.X509Csr.get_subject') - @mock.patch('anchor.X509.name.X509Name.get_entries_by_nid_name') - @mock.patch('anchor.X509.signing_request.X509Csr.get_extensions') - def test_common_name_no_CN(self, get_extensions, get_entries_by_nid_name, - get_subject): - get_extensions.return_value = [] - get_entries_by_nid_name.return_value = None - get_subject.return_value = name.X509Name() - self.assertRaises( - validators.ValidationError, - validators.common_name, - self.csr_good_sub_alt_names, - allowed_domains=[], - allowed_networks=[] - ) + def test_common_name_with_two_CN(self): + ext_mock = mock.MagicMock() + ext_mock.get_name.return_value = "subjectAltName" + + csr_config = { + 'get_extensions.return_value': [ext_mock], + 'get_subject.return_value.get_entries_by_nid_name.return_value': + ['dummy_value', 'dummy_value'], + } + csr_mock = mock.MagicMock(**csr_config) + + with self.assertRaises(validators.ValidationError) as e: + validators.common_name( + csr=csr_mock, + allowed_domains=[], + allowed_networks=[]) + self.assertEqual("Too many CNs in the request", str(e.exception)) + + def test_common_name_no_CN(self): + csr_config = { + 'get_subject.return_value.__len__.return_value': 0, + 'get_subject.return_value.get_entries_by_nid_name.return_value': + [] + } + csr_mock = mock.MagicMock(**csr_config) + + with self.assertRaises(validators.ValidationError) as e: + validators.common_name( + csr=csr_mock, + allowed_domains=[], + allowed_networks=[]) + self.assertEqual("Alt subjects have to exist if the main subject" + " doesn't", str(e.exception)) @mock.patch('socket.gethostbyname_ex') def test_common_name_good_CN(self, gethostbyname_ex): gethostbyname_ex.return_value = ('master.test.com', [], ['10.0.0.1']) + + cn_mock = mock.MagicMock() + cn_mock.get_value.return_value = 'master.test.com' + + csr_config = { + 'get_subject.return_value.__len__.return_value': 1, + 'get_subject.return_value.get_entries_by_nid_name.return_value': + [cn_mock], + } + csr_mock = mock.MagicMock(**csr_config) + self.assertEqual( None, validators.common_name( - self.csr_good_sub_alt_names, + csr=csr_mock, allowed_domains=['.test.com'], allowed_networks=['10/8'] ) @@ -213,65 +132,517 @@ class TestValidators(unittest.TestCase): @mock.patch('socket.gethostbyname_ex') def test_common_name_bad_CN(self, gethostbyname_ex): gethostbyname_ex.return_value = ('master.test.com', [], ['10.0.0.1']) - self.assertRaises( - validators.ValidationError, - validators.common_name, - self.csr_good_sub_alt_names, - allowed_domains=['.fail.com'], - allowed_networks=['10/8'] + + name = x509_name.X509Name() + name.add_name_entry('CN', 'test.baddomain.com') + + csr_mock = mock.MagicMock() + csr_mock.get_subject.return_value = name + + with self.assertRaises(validators.ValidationError) as e: + validators.common_name( + csr=csr_mock, + allowed_domains=['.test.com'], + allowed_networks=['10/8']) + self.assertEqual("Domain 'test.baddomain.com' not allowed (does not " + "match known domains)", str(e.exception)) + + def test_common_name_good_ip_CN(self): + cn_mock = mock.MagicMock() + cn_mock.get_value.return_value = '10.0.0.1' + + csr_config = { + 'get_subject.return_value.__len__.return_value': 1, + 'get_subject.return_value.get_entries_by_nid_name.return_value': + [cn_mock], + } + csr_mock = mock.MagicMock(**csr_config) + + self.assertEqual( + None, + validators.common_name( + csr=csr_mock, + allowed_domains=[], + allowed_networks=['10/8'] + ) ) + def test_common_name_bad_ip_CN(self): + name = x509_name.X509Name() + name.add_name_entry('CN', '12.0.0.1') + + csr_mock = mock.MagicMock() + csr_mock.get_subject.return_value = name + + with self.assertRaises(validators.ValidationError) as e: + validators.common_name( + csr=csr_mock, + allowed_domains=[], + allowed_networks=['10/8']) + self.assertEqual("Network '12.0.0.1' not allowed (does not match " + "known networks)", str(e.exception)) + @mock.patch('socket.gethostbyname_ex') - def test_alternative_names_all_good(self, gethostbyname_ex): + def test_alternative_names_good_domain(self, gethostbyname_ex): gethostbyname_ex.return_value = ('master.test.com', [], ['10.0.0.1']) + + ext_mock = mock.MagicMock() + ext_mock.get_value.return_value = 'DNS:master.test.com' + ext_mock.get_name.return_value = 'subjectAltName' + + csr_mock = mock.MagicMock() + csr_mock.get_extensions.return_value = [ext_mock] self.assertEqual( None, validators.alternative_names( - self.csr_good_sub_alt_names, + csr=csr_mock, allowed_domains=['.test.com'], ) ) @mock.patch('socket.gethostbyname_ex') - def test_alternative_names_bad_domains(self, gethostbyname_ex): + def test_alternative_names_bad_domain(self, gethostbyname_ex): gethostbyname_ex.return_value = ('master.test.com', [], ['10.0.0.1']) - self.assertRaises( - validators.ValidationError, - validators.alternative_names, - self.csr_bad_sub_alt_names, - allowed_domains=['.test.com'], # CSR has a subjalt 'baddomain.com' - ) + + ext_mock = mock.MagicMock() + ext_mock.get_value.return_value = 'DNS:test.baddomain.com' + ext_mock.get_name.return_value = 'subjectAltName' + + csr_mock = mock.MagicMock() + csr_mock.get_extensions.return_value = [ext_mock] + + with self.assertRaises(validators.ValidationError) as e: + validators.alternative_names( + csr=csr_mock, + allowed_domains=['.test.com']) + self.assertEqual("Domain 'test.baddomain.com' not allowed (doesn't " + "match known domains or networks)", str(e.exception)) + + def test_alternative_names_ext(self): + ext_mock = mock.MagicMock() + ext_mock.get_value.return_value = 'BAD,10.1.1.1' + ext_mock.get_name.return_value = 'subjectAltName' + + csr_mock = mock.MagicMock() + csr_mock.get_extensions.return_value = [ext_mock] + + with self.assertRaises(validators.ValidationError) as e: + validators.alternative_names( + csr=csr_mock, + allowed_domains=['.test.com']) + self.assertEqual("Alt name 'BAD' does not have a known type", + str(e.exception)) @mock.patch('socket.gethostbyname_ex') - def test_alternative_names_ip_all_good(self, gethostbyname_ex): + def test_alternative_names_ip_good(self, gethostbyname_ex): gethostbyname_ex.return_value = ('master.test.com', [], ['10.0.0.1']) + + ext_mock = mock.MagicMock() + ext_mock.get_value.return_value = 'IP Address:10.1.1.1' + ext_mock.get_name.return_value = 'subjectAltName' + + csr_mock = mock.MagicMock() + csr_mock.get_extensions.return_value = [ext_mock] + self.assertEqual( None, validators.alternative_names_ip( - self.csr_good_sub_alt_names_IP, + csr=csr_mock, allowed_domains=['.test.com'], allowed_networks=['10/8'] ) ) @mock.patch('socket.gethostbyname_ex') - def test_alternative_names_ip_bad_network(self, gethostbyname_ex): + def test_alternative_names_ip_bad(self, gethostbyname_ex): gethostbyname_ex.return_value = ('master.test.com', [], ['10.0.0.1']) - self.assertRaises( - validators.ValidationError, - validators.alternative_names_ip, - self.csr_good_sub_alt_names_IP, - allowed_domains=['.test.com'], - allowed_networks=['99/8'] + + ext_mock = mock.MagicMock() + ext_mock.get_value.return_value = 'IP Address:10.1.1.1' + ext_mock.get_name.return_value = 'subjectAltName' + + csr_mock = mock.MagicMock() + csr_mock.get_extensions.return_value = [ext_mock] + + with self.assertRaises(validators.ValidationError) as e: + validators.alternative_names_ip( + csr=csr_mock, + allowed_domains=['.test.com'], + allowed_networks=['99/8']) + self.assertEqual("Domain '10.1.1.1' not allowed (doesn't match known " + "domains or networks)", str(e.exception)) + + def test_alternative_names_ip_ext(self): + ext_mock = mock.MagicMock() + ext_mock.get_value.return_value = 'BAD,10.1.1.1' + ext_mock.get_name.return_value = 'subjectAltName' + + csr_mock = mock.MagicMock() + csr_mock.get_extensions.return_value = [ext_mock] + + with self.assertRaises(validators.ValidationError) as e: + validators.alternative_names_ip( + csr=csr_mock, + allowed_domains=['.test.com']) + self.assertEqual("Alt name 'BAD' does not have a known type", + str(e.exception)) + + def test_alternative_names_ip_bad_ext(self): + ext_mock = mock.MagicMock() + ext_mock.get_value.return_value = 'BAD:VALUE' + ext_mock.get_name.return_value = 'subjectAltName' + + csr_mock = mock.MagicMock() + csr_mock.get_extensions.return_value = [ext_mock] + + with self.assertRaises(validators.ValidationError) as e: + validators.alternative_names_ip( + csr=csr_mock, + allowed_domains=['.test.com'], + allowed_networks=['99/8']) + self.assertEqual("Alt name 'BAD' does not have a known type", + str(e.exception)) + + def test_server_group_no_prefix1(self): + cn_mock = mock.MagicMock() + cn_mock.get_value.return_value = 'master.test.com' + + csr_config = { + 'get_subject.return_value.get_entries_by_nid_name.return_value': + [cn_mock], + } + csr_mock = mock.MagicMock(**csr_config) + + self.assertEqual( + None, + validators.server_group( + auth_result=None, + csr=csr_mock, + group_prefixes={} + ) ) - @mock.patch('anchor.X509.name.X509Name.Entry.get_name') - def test_alternative_names_ip_bad_ext(self, get_name): - get_name.return_value = ('BAD:VALUE') - self.assertRaises( - validators.ValidationError, - validators.alternative_names_ip, - self.csr_good_sub_alt_names_IP, - allowed_domains=['mock'], - allowed_networks=['99/8'] + def test_server_group_no_prefix2(self): + cn_mock = mock.MagicMock() + cn_mock.get_value.return_value = 'nv_master.test.com' + + csr_config = { + 'get_subject.return_value.get_entries_by_nid_name.return_value': + [cn_mock], + } + csr_mock = mock.MagicMock(**csr_config) + + self.assertEqual( + None, + validators.server_group( + auth_result=None, + csr=csr_mock, + group_prefixes={} + ) ) + + def test_server_group_good_prefix(self): + # 'nv' in prefix means only Nova members should be able to issue + auth_result = mock.Mock() + auth_result.groups = ['nova'] + + cn_mock = mock.MagicMock() + cn_mock.get_value.return_value = 'nv_master.test.com' + + csr_config = { + 'get_subject.return_value.get_entries_by_nid_name.return_value': + [cn_mock], + } + csr_mock = mock.MagicMock(**csr_config) + + self.assertEqual( + None, + validators.server_group( + auth_result=auth_result, + csr=csr_mock, + group_prefixes={'nv': 'nova', 'sw': 'swift'} + ) + ) + + def test_server_group_bad(self): + auth_result = mock.Mock() + auth_result.groups = ['glance'] + + cn_mock = mock.MagicMock() + cn_mock.get_value.return_value = 'nv-master.test.com' + + csr_config = { + 'get_subject.return_value.get_entries_by_nid_name.return_value': + [cn_mock], + } + csr_mock = mock.MagicMock(**csr_config) + + with self.assertRaises(validators.ValidationError) as e: + validators.server_group( + auth_result=auth_result, + csr=csr_mock, + group_prefixes={'nv': 'nova', 'sw': 'swift'}) + self.assertEqual("Server prefix doesn't match user groups", + str(e.exception)) + + def test_extensions_bad(self): + ext_mock = mock.MagicMock() + ext_mock.get_name.return_value = 'BAD' + ext_mock.get_value.return_value = 'BAD' + + csr_mock = mock.MagicMock() + csr_mock.get_extensions.return_value = [ext_mock] + + with self.assertRaises(validators.ValidationError) as e: + validators.extensions( + csr=csr_mock, + allowed_extensions=['GOOD-1', 'GOOD-2']) + self.assertEqual("Extension 'BAD' not allowed", str(e.exception)) + + def test_extensions_good(self): + ext_mock = mock.MagicMock() + ext_mock.get_name.return_value = 'GOOD-1' + ext_mock.get_value.return_value = 'GOOD-1' + + csr_mock = mock.MagicMock() + csr_mock.get_extensions.return_value = [ext_mock] + + self.assertEqual( + None, + validators.extensions( + csr=csr_mock, + allowed_extensions=['GOOD-1', 'GOOD-2'] + ) + ) + + def test_key_usage_bad(self): + allowed_usage = ['Digital Signature', + 'Non Repudiation', + 'Key Encipherment'] + + ext_mock = mock.MagicMock() + ext_mock.get_name.return_value = 'keyUsage' + ext_mock.get_value.return_value = 'Domination' + + csr_mock = mock.MagicMock() + csr_mock.get_extensions.return_value = [ext_mock] + + with self.assertRaises(validators.ValidationError) as e: + validators.key_usage( + csr=csr_mock, + allowed_usage=allowed_usage) + self.assertEqual("Found some not allowed key usages: " + "set(['Domination'])", str(e.exception)) + + def test_key_usage_good(self): + allowed_usage = ['Digital Signature', + 'Non Repudiation', + 'Key Encipherment'] + + ext_mock = mock.MagicMock() + ext_mock.get_name.return_value = 'keyUsage' + ext_mock.get_value.return_value = 'Key Encipherment, Digital Signature' + + csr_mock = mock.MagicMock() + csr_mock.get_extensions.return_value = [ext_mock] + + self.assertEqual( + None, + validators.key_usage( + csr=csr_mock, + allowed_usage=allowed_usage + ) + ) + + def test_ca_status_good1(self): + ext_mock = mock.MagicMock() + ext_mock.get_name.return_value = 'basicConstraints' + ext_mock.get_value.return_value = 'CA:TRUE' + + csr_mock = mock.MagicMock() + csr_mock.get_extensions.return_value = [ext_mock] + + self.assertEqual( + None, + validators.ca_status( + csr=csr_mock, + ca_requested=True + ) + ) + + def test_ca_status_good2(self): + ext_mock = mock.MagicMock() + ext_mock.get_name.return_value = 'basicConstraints' + ext_mock.get_value.return_value = 'CA:FALSE' + + csr_mock = mock.MagicMock() + csr_mock.get_extensions.return_value = [ext_mock] + + self.assertEqual( + None, + validators.ca_status( + csr=csr_mock, + ca_requested=False + ) + ) + + def test_ca_status_bad(self): + ext_mock = mock.MagicMock() + ext_mock.get_name.return_value = 'basicConstraints' + ext_mock.get_value.return_value = 'CA:FALSE' + + csr_mock = mock.MagicMock() + csr_mock.get_extensions.return_value = [ext_mock] + + with self.assertRaises(validators.ValidationError) as e: + validators.ca_status( + csr=csr_mock, + ca_requested=True) + self.assertEqual("Invalid CA status, 'CA:FALSE' requested", + str(e.exception)) + + def test_ca_status_bad_format1(self): + ext_mock = mock.MagicMock() + ext_mock.get_name.return_value = 'basicConstraints' + ext_mock.get_value.return_value = 'CA~FALSE' + + csr_mock = mock.MagicMock() + csr_mock.get_extensions.return_value = [ext_mock] + + with self.assertRaises(validators.ValidationError) as e: + validators.ca_status( + csr=csr_mock, + ca_requested=False) + self.assertEqual("Invalid basic constraints flag", str(e.exception)) + + def test_ca_status_bad_format2(self): + ext_mock = mock.MagicMock() + ext_mock.get_name.return_value = 'basicConstraints' + ext_mock.get_value.return_value = 'CA:FALSE:DERP' + + csr_mock = mock.MagicMock() + csr_mock.get_extensions.return_value = [ext_mock] + + with self.assertRaises(validators.ValidationError) as e: + validators.ca_status( + csr=csr_mock, + ca_requested=False) + self.assertEqual("Invalid basic constraints flag", str(e.exception)) + + def test_ca_status_pathlen(self): + ext_mock = mock.MagicMock() + ext_mock.get_name.return_value = 'basicConstraints' + ext_mock.get_value.return_value = 'pathlen:somthing' + + csr_mock = mock.MagicMock() + csr_mock.get_extensions.return_value = [ext_mock] + + self.assertEqual( + None, + validators.ca_status( + csr=csr_mock, + ca_requested=False + ) + ) + + def test_ca_status_bad_value(self): + ext_mock = mock.MagicMock() + ext_mock.get_name.return_value = 'basicConstraints' + ext_mock.get_value.return_value = 'BAD:VALUE' + + csr_mock = mock.MagicMock() + csr_mock.get_extensions.return_value = [ext_mock] + + with self.assertRaises(validators.ValidationError) as e: + validators.ca_status( + csr=csr_mock, + ca_requested=False) + self.assertEqual("Invalid basic constraints option", str(e.exception)) + + def test_ca_status_key_usage_bad1(self): + ext_mock = mock.MagicMock() + ext_mock.get_name.return_value = 'keyUsage' + ext_mock.get_value.return_value = 'Certificate Sign' + + csr_mock = mock.MagicMock() + csr_mock.get_extensions.return_value = [ext_mock] + + with self.assertRaises(validators.ValidationError) as e: + validators.ca_status( + csr=csr_mock, + ca_requested=False) + self.assertEqual("Key usage doesn't match requested CA status " + "(keyCertSign/cRLSign: True/False)", str(e.exception)) + + def test_ca_status_key_usage_good1(self): + ext_mock = mock.MagicMock() + ext_mock.get_name.return_value = 'keyUsage' + ext_mock.get_value.return_value = 'Certificate Sign' + + csr_mock = mock.MagicMock() + csr_mock.get_extensions.return_value = [ext_mock] + + with self.assertRaises(validators.ValidationError) as e: + validators.ca_status( + csr=csr_mock, + ca_requested=True) + self.assertEqual("Key usage doesn't match requested CA status " + "(keyCertSign/cRLSign: True/False)", str(e.exception)) + + def test_ca_status_key_usage_bad2(self): + ext_mock = mock.MagicMock() + ext_mock.get_name.return_value = 'keyUsage' + ext_mock.get_value.return_value = 'CRL Sign' + + csr_mock = mock.MagicMock() + csr_mock.get_extensions.return_value = [ext_mock] + + with self.assertRaises(validators.ValidationError) as e: + validators.ca_status( + csr=csr_mock, + ca_requested=False) + self.assertEqual("Key usage doesn't match requested CA status " + "(keyCertSign/cRLSign: False/True)", str(e.exception)) + + def test_ca_status_key_usage_good2(self): + ext_mock = mock.MagicMock() + ext_mock.get_name.return_value = 'keyUsage' + ext_mock.get_value.return_value = 'CRL Sign' + + csr_mock = mock.MagicMock() + csr_mock.get_extensions.return_value = [ext_mock] + + with self.assertRaises(validators.ValidationError) as e: + validators.ca_status( + csr=csr_mock, + ca_requested=True) + self.assertEqual("Key usage doesn't match requested CA status " + "(keyCertSign/cRLSign: False/True)", str(e.exception)) + + def test_source_cidrs_good(self): + request = mock.Mock(client_addr='127.0.0.1') + self.assertEqual( + None, + validators.source_cidrs( + request=request, + cidrs=['127/8', '10/8'] + ) + ) + + def test_source_cidrs_out_of_range(self): + request = mock.Mock(client_addr='99.0.0.1') + with self.assertRaises(validators.ValidationError) as e: + validators.source_cidrs( + request=request, + cidrs=['127/8', '10/8']) + self.assertEqual("No network matched the request source '99.0.0.1'", + str(e.exception)) + + def test_source_cidrs_bad_cidr(self): + request = mock.Mock(client_addr='127.0.0.1') + with self.assertRaises(validators.ValidationError) as e: + validators.source_cidrs( + request=request, + cidrs=['bad']) + self.assertEqual("Cidr 'bad' does not describe a valid network", + str(e.exception))