Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support for TenableAD license APIs #510

Merged
merged 2 commits into from
May 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/api/ad/license.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.. automodule:: tenable.ad.license.api
1 change: 1 addition & 0 deletions tenable/ad/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
event
infrastructure
ldap_configuration
license
lockout_policy
preference
profiles
Expand Down
1 change: 1 addition & 0 deletions tenable/ad/base/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ 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

Expand Down
Empty file.
53 changes: 53 additions & 0 deletions tenable/ad/license/api.py
Original file line number Diff line number Diff line change
@@ -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))
18 changes: 18 additions & 0 deletions tenable/ad/license/schema.py
Original file line number Diff line number Diff line change
@@ -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')
9 changes: 9 additions & 0 deletions tenable/ad/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from .event.api import EventAPI
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
Expand Down Expand Up @@ -151,6 +152,14 @@ def ldap_configuration(self):
'''
return LDAPConfigurationAPI(self)

@property
def license(self):
'''
The interface object for the
:doc:`Tenable.ad License APIs <license>`.
'''
return LicenseAPI(self)

@property
def lockout_policy(self):
'''
Expand Down
56 changes: 56 additions & 0 deletions tests/ad/license/test_license_api.py
Original file line number Diff line number Diff line change
@@ -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'
51 changes: 51 additions & 0 deletions tests/ad/license/test_license_schema.py
Original file line number Diff line number Diff line change
@@ -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'