From 11dbe76c38a9628fec4072a3b520ed02b3ccfac2 Mon Sep 17 00:00:00 2001 From: Dileep Ranganathan Date: Fri, 14 Sep 2018 11:29:39 -0700 Subject: Secret Management Service feature Added supporting library required for enabling SMS integration. Added Unit tests and manual tests for store/retrieve/delete secrets. Added AAF cacert, preload_secrets config for testing. Integration with application NOT Done in this patch. Change-Id: Ic2be41f825b327064127fbf83f9e1057a68f19c2 Issue-ID: OPTFRA-343 Signed-off-by: Dileep Ranganathan --- config/osdf_config.yaml | 6 +++ config/preload_secrets.yaml | 43 ++++++++++++++++++++ osdf/adapters/aaf/sms.py | 97 +++++++++++++++++++++++++++++++++++++++++++++ requirements.txt | 1 + ssl_certs/aaf_root_ca.cer | 31 +++++++++++++++ test/adapters/test_sms.py | 89 +++++++++++++++++++++++++++++++++++++++++ test/test-requirements.txt | 1 + 7 files changed, 268 insertions(+) create mode 100755 config/preload_secrets.yaml create mode 100644 osdf/adapters/aaf/sms.py create mode 100644 ssl_certs/aaf_root_ca.cer create mode 100644 test/adapters/test_sms.py diff --git a/config/osdf_config.yaml b/config/osdf_config.yaml index 022104d..4ef11f8 100755 --- a/config/osdf_config.yaml +++ b/config/osdf_config.yaml @@ -50,3 +50,9 @@ aaf_cache_expiry_hrs: 3 aaf_url: https://aaftest.simpledemo.onap.org:8095 aaf_user_roles: - /api/oof/v1/placement:org.onap.osdf.access|*|read ALL + +# Secret Management Service from AAF +aaf_sms_url: https://aaf-sms.onap:10443 +aaf_sms_timeout: 30 +secret_domain: osdf #Replace with the UUID +aaf_ca_certs: ssl_certs/aaf_root_ca.cer diff --git a/config/preload_secrets.yaml b/config/preload_secrets.yaml new file mode 100755 index 0000000..6e877e7 --- /dev/null +++ b/config/preload_secrets.yaml @@ -0,0 +1,43 @@ +--- +domain: osdf +secrets: +- name: so + values: + UserName: '' + Password: '' +- name: conductor + values: + UserName: admin1 + Password: plan.15 +- name: policy_platform + values: + UserName: testpdp + Password: alpha123 +- name: policy_client + values: + UserName: python + Password: test +- name: dmaap + values: + UserName: NA + Password: NA +- name: sdc + values: + UserName: NA + Password: NA +- name: osdfPlacement + values: + UserName: test + Password: testpwd +- name: osdfPlacementSO + values: + UserName: so_test + Password: so_testpwd +- name: osdfPlacementVFC + values: + UserName: vfc_test + Password: vfc_testpwd +- name: osdfCMScheduler + values: + UserName: test1 + Password: test_pwd1 diff --git a/osdf/adapters/aaf/sms.py b/osdf/adapters/aaf/sms.py new file mode 100644 index 0000000..976eb61 --- /dev/null +++ b/osdf/adapters/aaf/sms.py @@ -0,0 +1,97 @@ +# +# ------------------------------------------------------------------------- +# Copyright (c) 2018 Intel Corporation Intellectual Property +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# ------------------------------------------------------------------------- +# + +'''Secret Management Service Integration''' + +from onapsmsclient import Client + +import osdf.config.loader as config_loader +from osdf.config.base import osdf_config +from osdf.logging.osdf_logging import debug_log + +config_spec = { + "preload_secrets": "config/preload_secrets.yaml" +} + +secret_cache = {} + + +def preload_secrets(): + """ This is intended to load the secrets required for testing Application + Actual deployment will have a preload script. Make sure the config is + in sync""" + preload_config = config_loader.load_config_file( + config_spec.get("preload_secrets")) + domain = preload_config.get("domain") + config = osdf_config.deployment + sms_url = config["aaf_sms_url"] + timeout = config["aaf_sms_timeout"] + cacert = config["aaf_ca_certs"] + sms_client = Client(url=sms_url, timeout=timeout, cacert=cacert) + domain = sms_client.createDomain(domain) + config["secret_domain"] = domain # uuid + secrets = preload_config.get("secrets") + for secret in secrets: + sms_client.storeSecret(domain, secret.get('name'), + secret.get('values')) + debug_log.debug("Preload secrets complete") + + +def retrieve_secrets(): + """Get all secrets under the domain name""" + secret_dict = dict() + config = osdf_config.deployment + sms_url = config["aaf_sms_url"] + timeout = config["aaf_sms_timeout"] + cacert = config["aaf_ca_certs"] + domain = config["secret_domain"] + sms_client = Client(url=sms_url, timeout=timeout, cacert=cacert) + secrets = sms_client.getSecretNames(domain) + for secret in secrets: + values = sms_client.getSecret(domain, secret) + secret_dict[secret] = values + debug_log.debug("Secret Dictionary Retrieval Success") + return secret_dict + + +def delete_secrets(): + """ This is intended to delete the secrets for a clean initialization for + testing Application. Actual deployment will have a preload script. + Make sure the config is in sync""" + config = osdf_config.deployment + sms_url = config["aaf_sms_url"] + timeout = config["aaf_sms_timeout"] + cacert = config["aaf_ca_certs"] + domain = config["secret_domain"] + sms_client = Client(url=sms_url, timeout=timeout, cacert=cacert) + ret_val = sms_client.deleteDomain(domain) + debug_log.debug("Clean up complete") + return ret_val + + +if __name__ == "__main__": + # Initialize Secrets from SMS + preload_secrets() + + # Retrieve Secrets from SMS and load to secret cache + # Use the secret_cache instead of config files + secret_cache = retrieve_secrets() + + # Clean up Delete secrets and domain + delete_secrets() diff --git a/requirements.txt b/requirements.txt index 351f97e..f05dadf 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,3 +11,4 @@ requests>=2.14.2 schematics>=2.0.0 docopt>=0.6.2 pydevd>=1.0.0 +onapsmsclient>=0.0.3 diff --git a/ssl_certs/aaf_root_ca.cer b/ssl_certs/aaf_root_ca.cer new file mode 100644 index 0000000..e9a50d7 --- /dev/null +++ b/ssl_certs/aaf_root_ca.cer @@ -0,0 +1,31 @@ +-----BEGIN CERTIFICATE----- +MIIFPjCCAyagAwIBAgIJAJ6u7cCnzrWdMA0GCSqGSIb3DQEBCwUAMCwxDjAMBgNV +BAsMBU9TQUFGMQ0wCwYDVQQKDARPTkFQMQswCQYDVQQGEwJVUzAeFw0xODA0MDUx +NDE1MjhaFw0zODAzMzExNDE1MjhaMCwxDjAMBgNVBAsMBU9TQUFGMQ0wCwYDVQQK +DARPTkFQMQswCQYDVQQGEwJVUzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC +ggIBAMA5pkgRs7NhGG4ew5JouhyYakgYUyFaG121+/h8qbSdt0hVQv56+EA41Yq7 +XGie7RYDQK9NmAFF3gruE+6X7wvJiChp+Cyd7sFMnb65uWhxEdxWTM2BJFrgfzUn +H8ZCxgaCo3XH4PzlKRy2LQQJEJECwl/RZmRCXijMt5e9h8XoZY/fKkKcZZUsWNCM +pTo266wjvA9MXLmdgReRj0+vrCjrNqy+htwJDztoiHWiYPqT6o8EvGcgjNqjlZx7 +NUNf8MfLDByqKF6+wRbHv1GKjn3/Vijd45Fv8riyRYROiFanvbV6jIfBkv8PZbXg +2VDWsYsgp8NAvMxK+iV8cO+Ck3lBI2GOPZbCEqpPVTYbLUz6sczAlCXwQoPzDIZY +wYa3eR/gYLY1gP2iEVHORag3bLPap9ZX5E8DZkzTNTjovvLk8KaCmfcaUMJsBtDd +ApcUitz10cnRyZc1sX3gE1f3DpzQM6t9C5sOVyRhDcSrKqqwb9m0Ss04XAS9FsqM +P3UWYQyqDXSxlUAYaX892u8mV1hxnt2gjb22RloXMM6TovM3sSrJS0wH+l1nznd6 +aFXftS/G4ZVIVZ/LfT1is4StoyPWZCwwwly1z8qJQ/zhip5NgZTxQw4mi7ww35DY +PdAQOCoajfSvFjqslQ/cPRi/MRCu079heVb5fQnnzVtnpFQRAgMBAAGjYzBhMB0G +A1UdDgQWBBRTVTPyS+vQUbHBeJrBKDF77+rtSTAfBgNVHSMEGDAWgBRTVTPyS+vQ +UbHBeJrBKDF77+rtSTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAN +BgkqhkiG9w0BAQsFAAOCAgEAPx/IaK94n02wPxpnYTy+LVLIxwdq/kawNd6IbiMz +L87zmNMDmHcGbfoRCj8OkhuggX9Lx1/CkhpXimuYsZOFQi5blr/u+v4mIbsgbmi9 +7j+cUHDP0zLycvSvxKHty51LwmaX9a4wkJl5zBU4O1sd/H9tWcEmwJ39ltKoBKBx +c94Zc3iMm5ytRWGj+0rKzLDAXEWpoZ5bE5PLJauA6UDCxDLfs3FwhbS7uDggxYvf +jySF5FCNET94oJ+m8s7VeHvoa8iPGKvXrIqdd7XDHnqJJlVKr7m9S0fMbyEB8ci2 +RtOXDt93ifY1uhoEtEykn4dqBSp8ezvNMnwoXdYPDvTd9uCAFeWFLVreBAWxd25h +PsBTkZA5hpa/rA+mKv6Af4VBViYr8cz4dZCsFChuioVebe9ighrfjB//qKepFjPF +CyjzKN1u0JKm/2x/ORqxkTONG8p3uDwoIOyimUcTtTMv42bfYD88RKakqSFXE9G+ +Z0LlaKABqfjK49o/tsAp+c5LoNlYllKhnetO3QAdraHwdmC36BhoghzR1jpX751A +cZn2VH3Q4XKyp01cJNCJIrua+A+bx6zh3RyW6zIIkbRCbET+UD+4mr8WIcSE3mtR +ZVlnhUDO4z9//WKMVzwS9Rh8/kuszrGFI1KQozXCHLrce3YP6RYZfOed79LXaRwX +dYY= +-----END CERTIFICATE----- diff --git a/test/adapters/test_sms.py b/test/adapters/test_sms.py new file mode 100644 index 0000000..ed6c9c9 --- /dev/null +++ b/test/adapters/test_sms.py @@ -0,0 +1,89 @@ +# +# ------------------------------------------------------------------------- +# Copyright (c) 2018 Intel Corporation Intellectual Property +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# ------------------------------------------------------------------------- +# +import unittest +from uuid import uuid4 + +import requests_mock + +import osdf.adapters.aaf.sms as SMS +from osdf.config.base import osdf_config + + +class TestSMS(unittest.TestCase): + + def setUp(self): + self.config = osdf_config.deployment + self.base_domain_url = '{}/v1/sms/domain' + self.domain_url = '{}/v1/sms/domain/{}' + self.secret_url = self.domain_url + '/secret' + + @requests_mock.mock() + def test_sms(self, mock_sms): + ''' NOTE: preload_secret generate the uuid for the domain + Create Domain API is called during the deployment using a + preload script. So the application oly knows the domain_uuid. + All sub-sequent SMS API calls needs the uuid. + For test purposes we need to do preload ourselves''' + sms_url = self.config["aaf_sms_url"] + + # JSON Data responses + secretnames = {'secretnames': ['s1', 's2', 's3', 's4']} + secretvalues = {'values': {'Password': '', 'UserName': ''}} + expecect_secret_dict = dict() + for secret in secretnames['secretnames']: + expecect_secret_dict[secret] = secretvalues['values'] + + # Part 1 : Preload Secrets ONLY FOR TEST + # Mock requests for preload_secret + cd_url = self.base_domain_url.format(sms_url) + domain_uuid1 = str(uuid4()) + s_url = self.secret_url.format(sms_url, domain_uuid1) + mock_sms.post(cd_url, status_code=200, json={'uuid': domain_uuid1}) + mock_sms.post(s_url, status_code=200) + # Initialize Secrets from SMS + SMS.preload_secrets() + + # Part 2: Retrieve Secret Test + # Mock requests for retrieve_secrets + # IMPORTANT: Read the config again as the preload_secrets has + # updated the config with uuid + domain_uuid2 = self.config["secret_domain"] + self.assertEqual(domain_uuid1, domain_uuid2) + + d_url = self.domain_url.format(sms_url, domain_uuid2) + s_url = self.secret_url.format(sms_url, domain_uuid2) + + # Retrieve Secrets from SMS and load to secret cache + # Use the secret_cache instead of config files + mock_sms.get(s_url, status_code=200, json=secretnames) + for secret in secretnames['secretnames']: + mock_sms.get('{}/{}'.format(s_url, secret), + status_code=200, json=secretvalues) + secret_cache = SMS.retrieve_secrets() + self.assertDictEqual(expecect_secret_dict, secret_cache, + 'Failed to retrieve secrets') + + # Part 3: Clean up Delete secrets and domain + # Mock requests for delete_secrets + mock_sms.delete(d_url, status_code=200) + self.assertTrue(SMS.delete_secrets()) + + +if __name__ == "__main__": + unittest.main() diff --git a/test/test-requirements.txt b/test/test-requirements.txt index b16a37e..043d87a 100644 --- a/test/test-requirements.txt +++ b/test/test-requirements.txt @@ -2,3 +2,4 @@ coverage moto pytest pytest-tap +requests-mock -- cgit 1.2.3-korg