From 545a76e477a2e74e49961a150421f069961b95e1 Mon Sep 17 00:00:00 2001 From: Steve McGrath Date: Fri, 7 Jan 2022 10:24:35 -0600 Subject: [PATCH] Added feature - TenableAD license APIs Added feature - TenableAD license APIs rebased to master and done requested changes --- docs/api/ad/license.rst | 1 + tenable/ad/__init__.py | 1 + tenable/ad/base/schema.py | 1 + tenable/ad/license/__init__.py | 0 tenable/ad/license/api.py | 53 +++++++++++++++++++++++ tenable/ad/license/schema.py | 18 ++++++++ tenable/ad/session.py | 9 ++++ tests/ad/license/test_license_api.py | 56 +++++++++++++++++++++++++ tests/ad/license/test_license_schema.py | 51 ++++++++++++++++++++++ 9 files changed, 190 insertions(+) create mode 100644 docs/api/ad/license.rst create mode 100644 tenable/ad/license/__init__.py create mode 100644 tenable/ad/license/api.py create mode 100644 tenable/ad/license/schema.py create mode 100644 tests/ad/license/test_license_api.py create mode 100644 tests/ad/license/test_license_schema.py diff --git a/docs/api/ad/license.rst b/docs/api/ad/license.rst new file mode 100644 index 000000000..f0ac2ed10 --- /dev/null +++ b/docs/api/ad/license.rst @@ -0,0 +1 @@ +.. automodule:: tenable.ad.license.api diff --git a/tenable/ad/__init__.py b/tenable/ad/__init__.py index 638c04dd1..58b1d780c 100644 --- a/tenable/ad/__init__.py +++ b/tenable/ad/__init__.py @@ -22,6 +22,7 @@ directories infrastructure ldap_configuration + license lockout_policy preference profiles diff --git a/tenable/ad/base/schema.py b/tenable/ad/base/schema.py index 4daea53e8..5f932b5cc 100644 --- a/tenable/ad/base/schema.py +++ b/tenable/ad/base/schema.py @@ -76,5 +76,6 @@ def convert_snake_to_camel(self, data, **kwargs) -> Dict: class BoolInt(fields.Boolean): '''Schema to return an integer value for given boolean value''' + def _serialize(self, value, attr, obj, **kwargs) -> int: return int(value) if value else 0 diff --git a/tenable/ad/license/__init__.py b/tenable/ad/license/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tenable/ad/license/api.py b/tenable/ad/license/api.py new file mode 100644 index 000000000..c89788601 --- /dev/null +++ b/tenable/ad/license/api.py @@ -0,0 +1,53 @@ +''' +License +============= + +Methods described in this section relate to the license API. +These methods can be accessed at ``TenableAD.license``. + +.. rst-class:: hide-signature +.. autoclass:: LicenseAPI + :members: +''' +from typing import Dict +from tenable.ad.license.schema import LicenseSchema +from tenable.base.endpoint import APIEndpoint + + +class LicenseAPI(APIEndpoint): + _path = 'license' + _schema = LicenseSchema() + + def details(self) -> Dict: + ''' + Get license singleton + + Returns: + dict: + The license object + + Examples: + >>> tad.license.details() + ''' + return self._schema.load(self._get()) + + def create(self, license: str) -> Dict: + ''' + Create new license singleton + + Args: + license (str): + The license string object. + + Return: + The license object + + Example: + >>> tad.license.create( + ... license='license' + ... ) + ''' + payload = self._schema.dump(self._schema.load({ + 'license': license + })) + return self._schema.load(self._post(json=payload)) diff --git a/tenable/ad/license/schema.py b/tenable/ad/license/schema.py new file mode 100644 index 000000000..4e7fca9ae --- /dev/null +++ b/tenable/ad/license/schema.py @@ -0,0 +1,18 @@ +from marshmallow import fields +from tenable.ad.base.schema import CamelCaseSchema, last_word_uppercase + + +class LicenseSchema(CamelCaseSchema): + class Meta: + case_convertors = { + 'expiration_date_utc': last_word_uppercase + } + + customer_name = fields.Str() + max_active_user_count = fields.Int() + current_active_user_count = fields.Int() + expiration_date_utc = fields.DateTime() + in_app_eula = fields.Bool() + features = fields.List(fields.Str()) + license = fields.Str() + license_type = fields.Str(data_key='type') diff --git a/tenable/ad/session.py b/tenable/ad/session.py index e3c08e7f8..d78b28001 100644 --- a/tenable/ad/session.py +++ b/tenable/ad/session.py @@ -16,6 +16,7 @@ from .directories.api import DirectoriesAPI from .infrastructure.api import InfrastructureAPI from .ldap_configuration.api import LDAPConfigurationAPI +from .license.api import LicenseAPI from .lockout_policy.api import LockoutPolicyAPI from .preference.api import PreferenceAPI from .profiles.api import ProfilesAPI @@ -132,6 +133,14 @@ def ldap_configuration(self): ''' return LDAPConfigurationAPI(self) + @property + def license(self): + ''' + The interface object for the + :doc:`Tenable.ad License APIs `. + ''' + return LicenseAPI(self) + @property def lockout_policy(self): ''' diff --git a/tests/ad/license/test_license_api.py b/tests/ad/license/test_license_api.py new file mode 100644 index 000000000..485d075d7 --- /dev/null +++ b/tests/ad/license/test_license_api.py @@ -0,0 +1,56 @@ +import datetime +import responses + +from tests.ad.conftest import RE_BASE + + +@responses.activate +def test_license_details(api): + responses.add(responses.GET, + f'{RE_BASE}/license', + json={ + 'customerName': 'pytenable', + 'maxActiveUserCount': 100, + 'currentActiveUserCount': 0, + 'expirationDateUTC': '2021-11-17T13:44:24.259Z', + 'inAppEula': False, + 'features': ['something'], + 'type': 'license type' + } + ) + resp = api.license.details() + assert isinstance(resp, dict) + assert resp['customer_name'] == 'pytenable' + assert resp['max_active_user_count'] == 100 + assert resp['current_active_user_count'] == 0 + assert resp['expiration_date_utc'] == datetime.datetime( + 2021, 11, 17, 13, 44, 24, 259000, tzinfo=datetime.timezone.utc) + assert resp['in_app_eula'] == False + assert resp['features'][0] == 'something' + assert resp['license_type'] == 'license type' + + +@responses.activate +def test_license_create(api): + responses.add(responses.POST, + f'{RE_BASE}/license', + json={ + 'customerName': 'pytenable', + 'maxActiveUserCount': 100, + 'currentActiveUserCount': 0, + 'expirationDateUTC': '2021-11-17T13:44:24.259Z', + 'inAppEula': False, + 'features': ['something'], + 'type': 'license type' + } + ) + resp = api.license.create(license='license') + assert isinstance(resp, dict) + assert resp['customer_name'] == 'pytenable' + assert resp['max_active_user_count'] == 100 + assert resp['current_active_user_count'] == 0 + assert resp['expiration_date_utc'] == datetime.datetime( + 2021, 11, 17, 13, 44, 24, 259000, tzinfo=datetime.timezone.utc) + assert resp['in_app_eula'] == False + assert resp['features'][0] == 'something' + assert resp['license_type'] == 'license type' diff --git a/tests/ad/license/test_license_schema.py b/tests/ad/license/test_license_schema.py new file mode 100644 index 000000000..07f93f56e --- /dev/null +++ b/tests/ad/license/test_license_schema.py @@ -0,0 +1,51 @@ +''' +Testing the license schemas +''' +import datetime +import pytest +from tenable.ad.license.schema import LicenseSchema + + +@pytest.fixture() +def license_schema_request_payload(): + return { + 'license': 'license' + } + + +def test_license_schema_request_payload(license_schema_request_payload): + ''' + test license create payload input to schema + ''' + schema = LicenseSchema() + req = schema.dump(schema.load(license_schema_request_payload)) + assert req['license'] == 'license' + + +@pytest.fixture() +def license_schema_response(): + return { + 'customerName': 'pytenable', + 'maxActiveUserCount': 100, + 'currentActiveUserCount': 0, + 'expirationDateUTC': '2021-11-17T13:44:24.259Z', + 'inAppEula': False, + 'features': ['something'], + 'type': 'license type' + } + + +def test_license_schema_response(license_schema_response): + ''' + test license get response to schema + ''' + schema = LicenseSchema() + req = schema.load(license_schema_response) + assert req['customer_name'] == 'pytenable' + assert req['max_active_user_count'] == 100 + assert req['current_active_user_count'] == 0 + assert req['expiration_date_utc'] == datetime.datetime( + 2021, 11, 17, 13, 44, 24, 259000, tzinfo=datetime.timezone.utc) + assert req['in_app_eula'] is False + assert req['features'][0] == 'something' + assert req['license_type'] == 'license type'