diff --git a/awx/api/views.py b/awx/api/views.py index 287d44cad1..df0c00061e 100644 --- a/awx/api/views.py +++ b/awx/api/views.py @@ -1681,6 +1681,7 @@ class JobTemplateList(ListCreateAPIView): model = JobTemplate serializer_class = JobTemplateSerializer + always_allow_superuser = False class JobTemplateDetail(RetrieveUpdateDestroyAPIView): diff --git a/awx/main/access.py b/awx/main/access.py index 5a609658ea..a63063469a 100644 --- a/awx/main/access.py +++ b/awx/main/access.py @@ -17,6 +17,7 @@ from rest_framework.exceptions import ParseError, PermissionDenied # AWX from awx.main.utils import * # noqa from awx.main.models import * # noqa +from awx.api.license import LicenseForbids from awx.main.task_engine import TaskSerializer __all__ = ['get_user_queryset', 'check_user_access'] @@ -145,7 +146,7 @@ class BaseAccess(object): def can_unattach(self, obj, sub_obj, relationship): return self.can_change(obj, None) - def check_license(self, add_host=False): + def check_license(self, add_host=False, feature=None): reader = TaskSerializer() validation_info = reader.from_file() if ('test' in sys.argv or 'jenkins' in sys.argv) and not os.environ.get('SKIP_LICENSE_FIXUP_FOR_TEST', ''): @@ -167,6 +168,12 @@ class BaseAccess(object): elif not add_host and free_instances < 0: raise PermissionDenied("host count exceeds available instances") + if feature is not None: + if "features" in validation_info and not validation_info["features"].get(feature, False): + raise LicenseForbids("Feature %s is not enabled in the active license" % feature) + elif "features" not in validation_info: + raise LicenseForbids("Features not found in active license") + class UserAccess(BaseAccess): ''' @@ -914,6 +921,10 @@ class JobTemplateAccess(BaseAccess): ''' if not data or '_method' in data: # So the browseable API will work? return True + + if 'job_type' in data and data['job_type'] == PERM_INVENTORY_SCAN: + self.check_license(feature='system_tracking') + if self.user.is_superuser: return True diff --git a/awx/main/tests/base.py b/awx/main/tests/base.py index f98fdef2dc..c9dbd34c2b 100644 --- a/awx/main/tests/base.py +++ b/awx/main/tests/base.py @@ -173,14 +173,15 @@ class BaseTestMixin(QueueTestMixin, MockCommonlySlowTestMixin): rnd_str = '____' + str(random.randint(1, 9999999)) return __name__ + '-generated-' + string + rnd_str - def create_test_license_file(self, instance_count=10000, license_date=int(time.time() + 3600)): + def create_test_license_file(self, instance_count=10000, license_date=int(time.time() + 3600), features=None): writer = LicenseWriter( company_name='AWX', contact_name='AWX Admin', contact_email='awx@example.com', license_date=license_date, instance_count=instance_count, - license_type='enterprise') + license_type='enterprise', + features=features) handle, license_path = tempfile.mkstemp(suffix='.json') os.close(handle) writer.write_file(license_path) diff --git a/awx/main/tests/jobs/jobs_monolithic.py b/awx/main/tests/jobs/jobs_monolithic.py index 559b952e14..bb2b2cfd58 100644 --- a/awx/main/tests/jobs/jobs_monolithic.py +++ b/awx/main/tests/jobs/jobs_monolithic.py @@ -429,6 +429,11 @@ class JobTemplateTest(BaseJobTestMixin, django.test.TestCase): job_type = PERM_INVENTORY_SCAN, inventory = self.inv_eng.pk, ) + # Without the system tracking license feature even super users can't create scan jobs + with self.current_user(self.user_sue): + data['credential'] = self.cred_sue.pk + response = self.post(url, data, expect=402) + self.create_test_license_file(features=dict(system_tracking=True)) # Regular users, even those who have access to the inv and cred can't create scan jobs templates with self.current_user(self.user_doug): data['credential'] = self.cred_doug.pk