From de9ae86ecc09e9adc44a77c65e3d28c570ec3fb0 Mon Sep 17 00:00:00 2001 From: Akita Noek Date: Thu, 21 Jul 2016 10:35:43 -0400 Subject: [PATCH 01/26] Fix migration problems with inventoryless job templates #3067 --- awx/main/migrations/_rbac.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/awx/main/migrations/_rbac.py b/awx/main/migrations/_rbac.py index 4a1115c4b3..4fe3504e7a 100644 --- a/awx/main/migrations/_rbac.py +++ b/awx/main/migrations/_rbac.py @@ -420,6 +420,11 @@ def migrate_job_templates(apps, schema_editor): jt_queryset = JobTemplate.objects.select_related('inventory', 'project', 'inventory__organization', 'execute_role') for jt in jt_queryset.iterator(): + if jt.inventory is None: + # If inventory is None, then only system admins and org admins can + # do anything with the JT in 2.4 + continue + jt_permission_qs = Permission.objects.filter( inventory=jt.inventory, project=jt.project, From 495115af6a37fe1d1bcb5809f709863fae5237f1 Mon Sep 17 00:00:00 2001 From: Matthew Jones Date: Thu, 21 Jul 2016 10:51:44 -0400 Subject: [PATCH 02/26] Bump version number for 3.0.1 release --- awx/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awx/__init__.py b/awx/__init__.py index c8081e8794..81659056aa 100644 --- a/awx/__init__.py +++ b/awx/__init__.py @@ -5,7 +5,7 @@ import os import sys import warnings -__version__ = '3.0.0' +__version__ = '3.0.1' __all__ = ['__version__'] From d886d07364e50b8ac232492a654ca95b798428f9 Mon Sep 17 00:00:00 2001 From: Chris Meyers Date: Thu, 21 Jul 2016 13:33:12 -0400 Subject: [PATCH 03/26] do not cache mongo read data on migration --- awx/main/migrations/_system_tracking.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awx/main/migrations/_system_tracking.py b/awx/main/migrations/_system_tracking.py index 9c77fb10f3..0465f969e2 100644 --- a/awx/main/migrations/_system_tracking.py +++ b/awx/main/migrations/_system_tracking.py @@ -52,7 +52,7 @@ def migrate_facts(apps, schema_editor): migrated_count = 0 not_migrated_count = 0 transform = KeyTransform([('.', '\uff0E'), ('$', '\uff04')]) - for factver in FactVersion.objects.all(): + for factver in FactVersion.objects.all().no_cache(): try: host = Host.objects.only('id').get(inventory__id=factver.host.inventory_id, name=factver.host.hostname) fact_obj = transform.replace_outgoing(factver.fact) From 4cf7344dc4d07dada242f5fb34bb6ec342003193 Mon Sep 17 00:00:00 2001 From: Chris Meyers Date: Thu, 21 Jul 2016 16:53:25 -0400 Subject: [PATCH 04/26] check license permission on host create --- awx/api/views.py | 1 + 1 file changed, 1 insertion(+) diff --git a/awx/api/views.py b/awx/api/views.py index 11ae8f6766..7d1a855ed7 100644 --- a/awx/api/views.py +++ b/awx/api/views.py @@ -1573,6 +1573,7 @@ class InventoryScanJobTemplateList(SubListAPIView): class HostList(ListCreateAPIView): + always_allow_superuser = False model = Host serializer_class = HostSerializer From 5bce9ee215cfa3555c115bad5499f9f6d94efe31 Mon Sep 17 00:00:00 2001 From: Akita Noek Date: Thu, 21 Jul 2016 17:22:11 -0400 Subject: [PATCH 05/26] If you have use_role on an inventory, make it so you can use it in scan jobs #3077 --- awx/main/access.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/awx/main/access.py b/awx/main/access.py index 5b2ee91851..19b4e34f70 100644 --- a/awx/main/access.py +++ b/awx/main/access.py @@ -906,8 +906,7 @@ class JobTemplateAccess(BaseAccess): project = get_value(Project, 'project') if 'job_type' in data and data['job_type'] == PERM_INVENTORY_SCAN: if inventory: - org = inventory.organization - accessible = self.user in org.admin_role + accessible = self.user in inventory.use_role else: accessible = False if not project and accessible: From fd098e64dcfc24c63b0614807a47f2ba753c1eba Mon Sep 17 00:00:00 2001 From: Chris Meyers Date: Fri, 22 Jul 2016 09:29:07 -0400 Subject: [PATCH 06/26] log migration to standard tower log dir --- awx/main/migrations/_rbac.py | 2 +- awx/main/migrations/_system_tracking.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/awx/main/migrations/_rbac.py b/awx/main/migrations/_rbac.py index 4fe3504e7a..8bebca3c6c 100644 --- a/awx/main/migrations/_rbac.py +++ b/awx/main/migrations/_rbac.py @@ -15,7 +15,7 @@ def log_migration(wrapped): as it runs, Django resets this, so we use a decorator to re-add the handler for each method. ''' - handler = logging.FileHandler("/tmp/tower_rbac_migrations.log", mode="a", encoding="UTF-8") + handler = logging.FileHandler("/var/log/tower/tower_rbac_migrations.log", mode="a", encoding="UTF-8") formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') handler.setLevel(logging.DEBUG) handler.setFormatter(formatter) diff --git a/awx/main/migrations/_system_tracking.py b/awx/main/migrations/_system_tracking.py index 0465f969e2..476bfc913a 100644 --- a/awx/main/migrations/_system_tracking.py +++ b/awx/main/migrations/_system_tracking.py @@ -16,7 +16,7 @@ def log_migration(wrapped): as it runs, Django resets this, so we use a decorator to re-add the handler for each method. ''' - handler = logging.FileHandler("/tmp/tower_system_tracking_migrations.log", mode="a", encoding="UTF-8") + handler = logging.FileHandler("/var/log/tower/tower_system_tracking_migrations.log", mode="a", encoding="UTF-8") formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') handler.setLevel(logging.DEBUG) handler.setFormatter(formatter) From 811d6dfe3d307387d68414dc2af329fc9ab2519e Mon Sep 17 00:00:00 2001 From: Chris Meyers Date: Fri, 22 Jul 2016 10:04:02 -0400 Subject: [PATCH 07/26] add missed log and fix dev env --- awx/main/migrations/_team_cleanup.py | 2 +- tools/docker-compose/start_development.sh | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/awx/main/migrations/_team_cleanup.py b/awx/main/migrations/_team_cleanup.py index 1a937d1f88..5af2b26492 100644 --- a/awx/main/migrations/_team_cleanup.py +++ b/awx/main/migrations/_team_cleanup.py @@ -9,7 +9,7 @@ def log_migration(wrapped): as it runs, Django resets this, so we use a decorator to re-add the handler for each method. ''' - handler = logging.FileHandler("/tmp/tower_rbac_migrations.log", mode="a", encoding="UTF-8") + handler = logging.FileHandler("/var/log/tower/tower_rbac_migrations.log", mode="a", encoding="UTF-8") formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') handler.setLevel(logging.DEBUG) handler.setFormatter(formatter) diff --git a/tools/docker-compose/start_development.sh b/tools/docker-compose/start_development.sh index 16d859a3ce..634dc02749 100755 --- a/tools/docker-compose/start_development.sh +++ b/tools/docker-compose/start_development.sh @@ -10,6 +10,9 @@ ansible -i "127.0.0.1," -c local -v -m wait_for -a "host=redis port=6379" all ansible -i "127.0.0.1," -c local -v -m postgresql_user -U postgres -a "name=awx-dev password=AWXsome1 login_user=postgres login_host=postgres" all ansible -i "127.0.0.1," -c local -v -m postgresql_db -U postgres -a "name=awx-dev owner=awx-dev login_user=postgres login_host=postgres" all +# For migration log +mkdir -p /var/log/tower/ + # Move to the source directory so we can bootstrap if [ -f "/tower_devel/manage.py" ]; then cd /tower_devel From eb89cf98cc7f16edadbefa053f33afc51b44375c Mon Sep 17 00:00:00 2001 From: Chris Meyers Date: Fri, 22 Jul 2016 10:05:00 -0400 Subject: [PATCH 08/26] Always create fact cleanup job --- awx/main/migrations/0010_v300_create_system_job_templates.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/awx/main/migrations/0010_v300_create_system_job_templates.py b/awx/main/migrations/0010_v300_create_system_job_templates.py index abf336efaa..e8b227a11d 100644 --- a/awx/main/migrations/0010_v300_create_system_job_templates.py +++ b/awx/main/migrations/0010_v300_create_system_job_templates.py @@ -4,9 +4,6 @@ from __future__ import unicode_literals from django.db import migrations, models from django.utils.timezone import now -from awx.api.license import feature_enabled - - def create_system_job_templates(apps, schema_editor): ''' Create default system job templates if not present. Create default schedules @@ -80,7 +77,7 @@ def create_system_job_templates(apps, schema_editor): polymorphic_ctype=sjt_ct, ), ) - if created and feature_enabled('system_tracking', bypass_database=True): + if created: sched = Schedule( name='Cleanup Fact Schedule', rrule='DTSTART:%s RRULE:FREQ=MONTHLY;INTERVAL=1;BYMONTHDAY=1' % now_str, From 64d653bc618fd53d038f3ffdc3b3707f3b94c961 Mon Sep 17 00:00:00 2001 From: Akita Noek Date: Fri, 22 Jul 2016 09:42:00 -0400 Subject: [PATCH 09/26] Equate '' to None for foreign key references when PUTing to a job template The UI will sometimes send an empty string instead of null, we already handle this in our serializer and treat it as null, so this change brings our access system in line with the expectation that those two things should be equal. Possibly related to #3077 --- awx/main/access.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/awx/main/access.py b/awx/main/access.py index 19b4e34f70..592359a031 100644 --- a/awx/main/access.py +++ b/awx/main/access.py @@ -978,7 +978,8 @@ class JobTemplateAccess(BaseAccess): for k, v in data.items(): if hasattr(obj, k) and getattr(obj, k) != v: - if k not in field_whitelist and v != getattr(obj, '%s_id' % k, None): + if k not in field_whitelist and v != getattr(obj, '%s_id' % k, None) \ + and not (hasattr(obj, '%s_id' % k) and getattr(obj, '%s_id' % k) is None and v == ''): # Equate '' to None in the case of foreign keys return False return True From c42af3c61a56432d0ced903c0938658b83f16567 Mon Sep 17 00:00:00 2001 From: Chris Meyers Date: Fri, 22 Jul 2016 10:33:30 -0400 Subject: [PATCH 10/26] forgot to remove label cleanup periodic task --- awx/main/tasks.py | 8 -------- awx/main/tests/unit/settings/test_defaults.py | 1 - awx/main/tests/unit/test_tasks.py | 11 ----------- awx/settings/defaults.py | 4 ---- 4 files changed, 24 deletions(-) diff --git a/awx/main/tasks.py b/awx/main/tasks.py index 2325702da6..7cd4c3c9c4 100644 --- a/awx/main/tasks.py +++ b/awx/main/tasks.py @@ -47,7 +47,6 @@ from django.contrib.auth.models import User from awx.main.constants import CLOUD_PROVIDERS from awx.main.models import * # noqa from awx.main.models import UnifiedJob -from awx.main.models.label import Label from awx.main.queue import FifoQueue from awx.main.conf import tower_settings from awx.main.task_engine import TaskSerializer, TASK_TIMEOUT_INTERVAL @@ -123,13 +122,6 @@ def run_administrative_checks(self): tower_admin_emails, fail_silently=True) -@task(bind=True) -def run_label_cleanup(self): - qs = Label.get_orphaned_labels() - labels_count = qs.count() - qs.delete() - return labels_count - @task(bind=True) def cleanup_authtokens(self): AuthToken.objects.filter(expires__lt=now()).delete() diff --git a/awx/main/tests/unit/settings/test_defaults.py b/awx/main/tests/unit/settings/test_defaults.py index f59ddfb60b..d1c80fce3e 100644 --- a/awx/main/tests/unit/settings/test_defaults.py +++ b/awx/main/tests/unit/settings/test_defaults.py @@ -4,7 +4,6 @@ from django.conf import settings from datetime import timedelta @pytest.mark.parametrize("job_name,function_path", [ - ('label_cleanup', 'awx.main.tasks.run_label_cleanup'), ('admin_checks', 'awx.main.tasks.run_administrative_checks'), ('tower_scheduler', 'awx.main.tasks.tower_periodic_scheduler'), ]) diff --git a/awx/main/tests/unit/test_tasks.py b/awx/main/tests/unit/test_tasks.py index ce88f019ce..fb491a015b 100644 --- a/awx/main/tests/unit/test_tasks.py +++ b/awx/main/tests/unit/test_tasks.py @@ -7,7 +7,6 @@ from awx.main.models import ( ) from awx.main.tasks import ( - run_label_cleanup, send_notifications, run_administrative_checks, ) @@ -21,16 +20,6 @@ def apply_patches(_patches): yield [p.stop() for p in _patches] -def test_run_label_cleanup(mocker): - qs = mocker.Mock(**{'count.return_value': 3, 'delete.return_value': None}) - mock_label = mocker.patch('awx.main.models.label.Label.get_orphaned_labels',return_value=qs) - - ret = run_label_cleanup() - - mock_label.assert_called_with() - qs.delete.assert_called_with() - assert 3 == ret - def test_send_notifications_not_list(): with pytest.raises(TypeError): send_notifications(None) diff --git a/awx/settings/defaults.py b/awx/settings/defaults.py index a4110e8286..701cd802e7 100644 --- a/awx/settings/defaults.py +++ b/awx/settings/defaults.py @@ -345,10 +345,6 @@ CELERYBEAT_SCHEDULE = { 'task': 'awx.main.tasks.run_administrative_checks', 'schedule': timedelta(days=30) }, - 'label_cleanup': { - 'task': 'awx.main.tasks.run_label_cleanup', - 'schedule': timedelta(days=7) - }, 'authtoken_cleanup': { 'task': 'awx.main.tasks.cleanup_authtokens', 'schedule': timedelta(days=30) From e5b031e533a718c9955e8f91fa19acf3f090f0a0 Mon Sep 17 00:00:00 2001 From: Chris Meyers Date: Fri, 22 Jul 2016 12:49:19 -0400 Subject: [PATCH 11/26] use existing logging infrastructure --- awx/main/migrations/_rbac.py | 27 +---------- awx/main/migrations/_system_tracking.py | 19 +------- awx/main/migrations/_team_cleanup.py | 19 +------- awx/settings/defaults.py | 37 ++++++++++++++- awx/settings/production.py | 57 +++-------------------- tools/docker-compose/start_development.sh | 3 -- 6 files changed, 46 insertions(+), 116 deletions(-) diff --git a/awx/main/migrations/_rbac.py b/awx/main/migrations/_rbac.py index 8bebca3c6c..e263271eab 100644 --- a/awx/main/migrations/_rbac.py +++ b/awx/main/migrations/_rbac.py @@ -8,25 +8,8 @@ from collections import defaultdict from awx.main.utils import getattrd from awx.main.models.rbac import Role, batch_role_ancestor_rebuilding -logger = logging.getLogger(__name__) +logger = logging.getLogger('rbac_migrations') -def log_migration(wrapped): - '''setup the logging mechanism for each migration method - as it runs, Django resets this, so we use a decorator - to re-add the handler for each method. - ''' - handler = logging.FileHandler("/var/log/tower/tower_rbac_migrations.log", mode="a", encoding="UTF-8") - formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') - handler.setLevel(logging.DEBUG) - handler.setFormatter(formatter) - - def wrapper(*args, **kwargs): - logger.handlers = [] - logger.addHandler(handler) - return wrapped(*args, **kwargs) - return wrapper - -@log_migration def create_roles(apps, schema_editor): ''' Implicit role creation happens in our post_save hook for all of our @@ -56,7 +39,6 @@ def create_roles(apps, schema_editor): obj.save() -@log_migration def migrate_users(apps, schema_editor): User = apps.get_model('auth', "User") Role = apps.get_model('main', "Role") @@ -89,7 +71,6 @@ def migrate_users(apps, schema_editor): sa_role.members.add(user) logger.warning(smart_text(u"added superuser: {}".format(user.username))) -@log_migration def migrate_organization(apps, schema_editor): Organization = apps.get_model('main', "Organization") for org in Organization.objects.iterator(): @@ -100,7 +81,6 @@ def migrate_organization(apps, schema_editor): org.member_role.members.add(user) logger.info(smart_text(u"added member: {}, {}".format(org.name, user.username))) -@log_migration def migrate_team(apps, schema_editor): Team = apps.get_model('main', 'Team') for t in Team.objects.iterator(): @@ -172,7 +152,6 @@ def _discover_credentials(instances, cred, orgfunc): _update_credential_parents(org, cred) -@log_migration def migrate_credential(apps, schema_editor): Credential = apps.get_model('main', "Credential") JobTemplate = apps.get_model('main', 'JobTemplate') @@ -210,7 +189,6 @@ def migrate_credential(apps, schema_editor): logger.warning(smart_text(u"orphaned credential found Credential(name={}, kind={}, host={}), superuser only".format(cred.name, cred.kind, cred.host, ))) -@log_migration def migrate_inventory(apps, schema_editor): Inventory = apps.get_model('main', 'Inventory') Permission = apps.get_model('main', 'Permission') @@ -254,7 +232,6 @@ def migrate_inventory(apps, schema_editor): execrole.members.add(perm.user) logger.info(smart_text(u'added User({}) access to Inventory({})'.format(perm.user.username, inventory.name))) -@log_migration def migrate_projects(apps, schema_editor): ''' I can see projects when: @@ -368,7 +345,6 @@ def migrate_projects(apps, schema_editor): -@log_migration def migrate_job_templates(apps, schema_editor): ''' NOTE: This must be run after orgs, inventory, projects, credential, and @@ -499,7 +475,6 @@ def migrate_job_templates(apps, schema_editor): -@log_migration def rebuild_role_hierarchy(apps, schema_editor): logger.info('Computing role roots..') start = time() diff --git a/awx/main/migrations/_system_tracking.py b/awx/main/migrations/_system_tracking.py index 476bfc913a..931c5c467c 100644 --- a/awx/main/migrations/_system_tracking.py +++ b/awx/main/migrations/_system_tracking.py @@ -9,25 +9,8 @@ from awx.fact.utils.dbtransform import KeyTransform from mongoengine.connection import ConnectionError from pymongo.errors import OperationFailure -logger = logging.getLogger(__name__) +logger = logging.getLogger('system_tracking_migrations') -def log_migration(wrapped): - '''setup the logging mechanism for each migration method - as it runs, Django resets this, so we use a decorator - to re-add the handler for each method. - ''' - handler = logging.FileHandler("/var/log/tower/tower_system_tracking_migrations.log", mode="a", encoding="UTF-8") - formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') - handler.setLevel(logging.DEBUG) - handler.setFormatter(formatter) - - def wrapper(*args, **kwargs): - logger.handlers = [] - logger.addHandler(handler) - return wrapped(*args, **kwargs) - return wrapper - -@log_migration def migrate_facts(apps, schema_editor): Fact = apps.get_model('main', "Fact") Host = apps.get_model('main', "Host") diff --git a/awx/main/migrations/_team_cleanup.py b/awx/main/migrations/_team_cleanup.py index 5af2b26492..7c0db85309 100644 --- a/awx/main/migrations/_team_cleanup.py +++ b/awx/main/migrations/_team_cleanup.py @@ -2,25 +2,8 @@ import logging from django.utils.encoding import smart_text -logger = logging.getLogger(__name__) +logger = logging.getLogger('rbac_migrations') -def log_migration(wrapped): - '''setup the logging mechanism for each migration method - as it runs, Django resets this, so we use a decorator - to re-add the handler for each method. - ''' - handler = logging.FileHandler("/var/log/tower/tower_rbac_migrations.log", mode="a", encoding="UTF-8") - formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') - handler.setLevel(logging.DEBUG) - handler.setFormatter(formatter) - - def wrapper(*args, **kwargs): - logger.handlers = [] - logger.addHandler(handler) - return wrapped(*args, **kwargs) - return wrapper - -@log_migration def migrate_team(apps, schema_editor): '''If an orphan team exists that is still active, delete it.''' Team = apps.get_model('main', 'Team') diff --git a/awx/settings/defaults.py b/awx/settings/defaults.py index a4110e8286..e152ad28e8 100644 --- a/awx/settings/defaults.py +++ b/awx/settings/defaults.py @@ -941,7 +941,34 @@ LOGGING = { 'maxBytes': 1024 * 1024 * 5, # 5 MB 'backupCount': 5, 'formatter':'simple', - } + }, + 'fact_receiver': { + 'level': 'WARNING', + 'class':'logging.handlers.RotatingFileHandler', + 'filters': ['require_debug_false'], + 'filename': os.path.join(LOG_ROOT, 'fact_receiver.log'), + 'maxBytes': 1024 * 1024 * 5, # 5 MB + 'backupCount': 5, + 'formatter':'simple', + }, + 'system_tracking_migrations': { + 'level': 'WARNING', + 'class':'logging.handlers.RotatingFileHandler', + 'filters': ['require_debug_false'], + 'filename': os.path.join(LOG_ROOT, 'tower_system_tracking_migrations.log'), + 'maxBytes': 1024 * 1024 * 5, # 5 MB + 'backupCount': 5, + 'formatter':'simple', + }, + 'rbac_migrations': { + 'level': 'WARNING', + 'class':'logging.handlers.RotatingFileHandler', + 'filters': ['require_debug_false'], + 'filename': os.path.join(LOG_ROOT, 'tower_rbac_migrations.log'), + 'maxBytes': 1024 * 1024 * 5, # 5 MB + 'backupCount': 5, + 'formatter':'simple', + }, }, 'loggers': { 'django': { @@ -1000,6 +1027,14 @@ LOGGING = { 'handlers': ['console', 'file', 'tower_warnings'], 'level': 'DEBUG', }, + 'system_tracking_migrations': { + 'handlers': ['console', 'file', 'tower_warnings'], + 'level': 'DEBUG', + }, + 'rbac_migrations': { + 'handlers': ['console', 'file', 'tower_warnings'], + 'level': 'DEBUG', + }, } } diff --git a/awx/settings/production.py b/awx/settings/production.py index 1f33f2bbbd..7b07b8d0a7 100644 --- a/awx/settings/production.py +++ b/awx/settings/production.py @@ -49,56 +49,13 @@ ANSIBLE_VENV_PATH = "/var/lib/awx/venv/ansible" TOWER_USE_VENV = True TOWER_VENV_PATH = "/var/lib/awx/venv/tower" -LOGGING['handlers']['tower_warnings'] = { - 'level': 'WARNING', - 'class':'logging.handlers.RotatingFileHandler', - 'filters': ['require_debug_false'], - 'filename': '/var/log/tower/tower.log', - 'maxBytes': 1024 * 1024 * 5, # 5 MB - 'backupCount': 5, - 'formatter':'simple', -} - - -LOGGING['handlers']['callback_receiver'] = { - 'level': 'WARNING', - 'class':'logging.handlers.RotatingFileHandler', - 'filters': ['require_debug_false'], - 'filename': '/var/log/tower/callback_receiver.log', - 'maxBytes': 1024 * 1024 * 5, # 5 MB - 'backupCount': 5, - 'formatter':'simple', -} - -LOGGING['handlers']['socketio_service'] = { - 'level': 'WARNING', - 'class':'logging.handlers.RotatingFileHandler', - 'filters': ['require_debug_false'], - 'filename': '/var/log/tower/socketio_service.log', - 'maxBytes': 1024 * 1024 * 5, # 5 MB - 'backupCount': 5, - 'formatter':'simple', -} - -LOGGING['handlers']['task_system'] = { - 'level': 'INFO', - 'class':'logging.handlers.RotatingFileHandler', - 'filters': ['require_debug_false'], - 'filename': '/var/log/tower/task_system.log', - 'maxBytes': 1024 * 1024 * 5, # 5 MB - 'backupCount': 5, - 'formatter':'simple', -} - -LOGGING['handlers']['fact_receiver'] = { - 'level': 'WARNING', - 'class':'logging.handlers.RotatingFileHandler', - 'filters': ['require_debug_false'], - 'filename': '/var/log/tower/fact_receiver.log', - 'maxBytes': 1024 * 1024 * 5, # 5 MB - 'backupCount': 5, - 'formatter':'simple', -} +LOGGING['handlers']['tower_warnings']['filename'] = '/var/log/tower/tower.log' +LOGGING['handlers']['callback_receiver']['filename'] = '/var/log/tower/callback_receiver.log' +LOGGING['handlers']['socketio_service']['filename'] = '/var/log/tower/socketio_service.log' +LOGGING['handlers']['task_system']['filename'] = '/var/log/tower/task_system.log' +LOGGING['handlers']['fact_receiver']['filename'] = '/var/log/tower/fact_receiver.log' +LOGGING['handlers']['system_tracking_migrations']['filename'] = '/var/log/tower/tower_system_tracking_migrations.log' +LOGGING['handlers']['rbac_migrations']['filename'] = '/var/log/tower/tower_rbac_migrations.log', # Load settings from any .py files in the global conf.d directory specified in # the environment, defaulting to /etc/tower/conf.d/. diff --git a/tools/docker-compose/start_development.sh b/tools/docker-compose/start_development.sh index 634dc02749..16d859a3ce 100755 --- a/tools/docker-compose/start_development.sh +++ b/tools/docker-compose/start_development.sh @@ -10,9 +10,6 @@ ansible -i "127.0.0.1," -c local -v -m wait_for -a "host=redis port=6379" all ansible -i "127.0.0.1," -c local -v -m postgresql_user -U postgres -a "name=awx-dev password=AWXsome1 login_user=postgres login_host=postgres" all ansible -i "127.0.0.1," -c local -v -m postgresql_db -U postgres -a "name=awx-dev owner=awx-dev login_user=postgres login_host=postgres" all -# For migration log -mkdir -p /var/log/tower/ - # Move to the source directory so we can bootstrap if [ -f "/tower_devel/manage.py" ]; then cd /tower_devel From 4bea2aa627fcf4e23b82e14b21d9cedb35744119 Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Thu, 21 Jul 2016 11:03:50 -0400 Subject: [PATCH 12/26] Show new jobs in UI Jobs tab --- awx/ui/client/src/controllers/Jobs.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awx/ui/client/src/controllers/Jobs.js b/awx/ui/client/src/controllers/Jobs.js index a7408259a2..eafbfbb981 100644 --- a/awx/ui/client/src/controllers/Jobs.js +++ b/awx/ui/client/src/controllers/Jobs.js @@ -67,7 +67,7 @@ export function JobsListController ($rootScope, $log, $scope, $compile, $statePa list: AllJobsList, id: 'active-jobs', pageSize: 20, - url: GetBasePath('unified_jobs') + '?status__in=pending,waiting,running,completed,failed,successful,error,canceled&order_by=-finished', + url: GetBasePath('unified_jobs') + '?status__in=pending,waiting,running,completed,failed,successful,error,canceled,new&order_by=-finished', searchParams: search_params, spinner: false }); From 94c8dfd506a5f27e8267320d08d59152c667eb4d Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Thu, 21 Jul 2016 09:05:32 -0400 Subject: [PATCH 13/26] Allow instant cancel for new jobs --- awx/main/models/unified_jobs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awx/main/models/unified_jobs.py b/awx/main/models/unified_jobs.py index 36b2dfe75e..f7106eb7ab 100644 --- a/awx/main/models/unified_jobs.py +++ b/awx/main/models/unified_jobs.py @@ -873,7 +873,7 @@ class UnifiedJob(PolymorphicModel, PasswordFieldsModel, CommonModelNameNotUnique if not self.cancel_flag: self.cancel_flag = True cancel_fields = ['cancel_flag'] - if self.status in ('pending', 'waiting'): + if self.status in ('pending', 'waiting', 'new'): self.status = 'canceled' cancel_fields.append('status') self.save(update_fields=cancel_fields) From 94052c2b2b1756c4c7e8d4753d45bbf5b1753b94 Mon Sep 17 00:00:00 2001 From: Akita Noek Date: Fri, 22 Jul 2016 16:44:00 -0400 Subject: [PATCH 14/26] Fixed credential migration issue involving null inventory fields in job templates Don't attempt to derive credential organization information from a job template when the inventory field is null for that job template #3107 --- awx/main/migrations/_rbac.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awx/main/migrations/_rbac.py b/awx/main/migrations/_rbac.py index e263271eab..b60ac65691 100644 --- a/awx/main/migrations/_rbac.py +++ b/awx/main/migrations/_rbac.py @@ -159,7 +159,7 @@ def migrate_credential(apps, schema_editor): InventorySource = apps.get_model('main', 'InventorySource') for cred in Credential.objects.iterator(): - results = [x for x in JobTemplate.objects.filter(Q(credential=cred) | Q(cloud_credential=cred)).all()] + \ + results = [x for x in JobTemplate.objects.filter(Q(credential=cred) | Q(cloud_credential=cred), inventory__isnull=False).all()] + \ [x for x in InventorySource.objects.filter(credential=cred).all()] if cred.deprecated_team is not None and results: if len(results) == 1: From 81c4a82db8dc12a0a7b4dd54229b6b98a9cb29de Mon Sep 17 00:00:00 2001 From: Wayne Witzel III Date: Mon, 25 Jul 2016 11:22:50 -0400 Subject: [PATCH 15/26] fix issue with rbac_migrations logger --- awx/settings/production.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awx/settings/production.py b/awx/settings/production.py index 7b07b8d0a7..6efe6c397d 100644 --- a/awx/settings/production.py +++ b/awx/settings/production.py @@ -55,7 +55,7 @@ LOGGING['handlers']['socketio_service']['filename'] = '/var/log/tower/socketio_s LOGGING['handlers']['task_system']['filename'] = '/var/log/tower/task_system.log' LOGGING['handlers']['fact_receiver']['filename'] = '/var/log/tower/fact_receiver.log' LOGGING['handlers']['system_tracking_migrations']['filename'] = '/var/log/tower/tower_system_tracking_migrations.log' -LOGGING['handlers']['rbac_migrations']['filename'] = '/var/log/tower/tower_rbac_migrations.log', +LOGGING['handlers']['rbac_migrations']['filename'] = '/var/log/tower/tower_rbac_migrations.log' # Load settings from any .py files in the global conf.d directory specified in # the environment, defaulting to /etc/tower/conf.d/. From 60e012f75b6e3efbb02ebddf0a6ac175131541c1 Mon Sep 17 00:00:00 2001 From: Matthew Jones Date: Mon, 25 Jul 2016 12:44:41 -0400 Subject: [PATCH 16/26] Force requests to emit application/json When sending webhook notifications --- awx/main/notifications/webhook_backend.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awx/main/notifications/webhook_backend.py b/awx/main/notifications/webhook_backend.py index fbefe78037..fe887efc68 100644 --- a/awx/main/notifications/webhook_backend.py +++ b/awx/main/notifications/webhook_backend.py @@ -32,7 +32,7 @@ class WebhookBackend(TowerBaseEmailBackend): self.headers['User-Agent'] = "Tower {}".format(get_awx_version()) for m in messages: r = requests.post("{}".format(m.recipients()[0]), - data=json.dumps(m.body), + json=m.body, headers=self.headers) if r.status_code >= 400: logger.error(smart_text("Error sending notification webhook: {}".format(r.text))) From 11d68d1ebe2f3f04d65fa340ec87470c2bd6762d Mon Sep 17 00:00:00 2001 From: James Laska Date: Mon, 25 Jul 2016 15:09:16 -0400 Subject: [PATCH 17/26] Complete Sat6 integration Various fixes to get sat-6 integration working. * inventory_import.py - the inventory script is called foreman.py * tasks.py - be sure to call `add_section()` before using it * defaults.py - Add various SATELLITE6_* enablement variables Relates #3119 --- awx/main/management/commands/inventory_import.py | 1 + awx/main/tasks.py | 2 ++ awx/plugins/inventory/cloudforms.py | 0 awx/plugins/inventory/foreman.py | 1 - awx/settings/defaults.py | 10 ++++++++++ 5 files changed, 13 insertions(+), 1 deletion(-) mode change 100644 => 100755 awx/plugins/inventory/cloudforms.py mode change 100644 => 100755 awx/plugins/inventory/foreman.py diff --git a/awx/main/management/commands/inventory_import.py b/awx/main/management/commands/inventory_import.py index 73bb0f99f4..21031f23cc 100644 --- a/awx/main/management/commands/inventory_import.py +++ b/awx/main/management/commands/inventory_import.py @@ -481,6 +481,7 @@ def load_inventory_source(source, all_group=None, group_filter_re=None, # Sanity check: We sanitize these module names for our API but Ansible proper doesn't follow # good naming conventions source = source.replace('azure.py', 'windows_azure.py') + source = source.replace('satellite6.py', 'foreman.py') logger.debug('Analyzing type of source: %s', source) original_all_group = all_group if not os.path.exists(source): diff --git a/awx/main/tasks.py b/awx/main/tasks.py index 7cd4c3c9c4..17268515b5 100644 --- a/awx/main/tasks.py +++ b/awx/main/tasks.py @@ -1299,9 +1299,11 @@ class RunInventoryUpdate(BaseTask): cp.set(section, 'password', decrypt_field(credential, 'password')) section = 'ansible' + cp.add_section(section) cp.set(section, 'group_patterns', '["{app}-{tier}-{color}", "{app}-{color}", "{app}", "{tier}"]') section = 'cache' + cp.add_section(section) cp.set(section, 'path', '/tmp') cp.set(section, 'max_age', '0') diff --git a/awx/plugins/inventory/cloudforms.py b/awx/plugins/inventory/cloudforms.py old mode 100644 new mode 100755 diff --git a/awx/plugins/inventory/foreman.py b/awx/plugins/inventory/foreman.py old mode 100644 new mode 100755 index 57367b1e77..7eff557cf1 --- a/awx/plugins/inventory/foreman.py +++ b/awx/plugins/inventory/foreman.py @@ -4,7 +4,6 @@ # # vim: set fileencoding=utf-8 : # -# Copyright (C) 2016 Guido Günther # # This script is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/awx/settings/defaults.py b/awx/settings/defaults.py index 9954433484..7fc78c90aa 100644 --- a/awx/settings/defaults.py +++ b/awx/settings/defaults.py @@ -672,6 +672,16 @@ OPENSTACK_HOST_FILTER = r'^.+$' OPENSTACK_EXCLUDE_EMPTY_GROUPS = True OPENSTACK_INSTANCE_ID_VAR = 'openstack.id' +# --------------------- +# ----- Foreman ----- +# --------------------- +SATELLITE6_ENABLED_VAR = 'foreman.enabled' +SATELLITE6_ENABLED_VALUE = 'true' +SATELLITE6_GROUP_FILTER = r'^.+$' +SATELLITE6_HOST_FILTER = r'^.+$' +SATELLITE6_EXCLUDE_EMPTY_GROUPS = True +SATELLITE6_INSTANCE_ID_VAR = 'foreman.uuid' + # --------------------- # -- Activity Stream -- # --------------------- From 13bd9211aec44b5b46ac3362a8d99f42bf0d8ec9 Mon Sep 17 00:00:00 2001 From: James Laska Date: Mon, 25 Jul 2016 15:18:10 -0400 Subject: [PATCH 18/26] Add copyright back with utf-8 encoding Fixes encoding exception --- awx/plugins/inventory/foreman.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/awx/plugins/inventory/foreman.py b/awx/plugins/inventory/foreman.py index 7eff557cf1..ce057690df 100755 --- a/awx/plugins/inventory/foreman.py +++ b/awx/plugins/inventory/foreman.py @@ -1,9 +1,9 @@ #!/usr/bin/python +# vim: set fileencoding=utf-8 : # # NOTE FOR TOWER: change foreman_ to sattelite_ for the group prefix # -# vim: set fileencoding=utf-8 : -# +# Copyright (C) 2016 Guido Günther # # This script is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by From 9bad20cee34f21a32937f82a56c20c584e9529b0 Mon Sep 17 00:00:00 2001 From: Wayne Witzel III Date: Tue, 26 Jul 2016 12:09:36 -0400 Subject: [PATCH 19/26] do not allow assignment of system roles or user.admin_role to teams --- awx/main/access.py | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/awx/main/access.py b/awx/main/access.py index 592359a031..2c01b62065 100644 --- a/awx/main/access.py +++ b/awx/main/access.py @@ -720,18 +720,25 @@ class TeamAccess(BaseAccess): def can_attach(self, obj, sub_obj, relationship, *args, **kwargs): """Reverse obj and sub_obj, defer to RoleAccess if this is an assignment of a resource role to the team.""" - if isinstance(sub_obj, Role) and isinstance(sub_obj.content_object, ResourceMixin): - role_access = RoleAccess(self.user) - return role_access.can_attach(sub_obj, obj, 'member_role.parents', - *args, **kwargs) + if isinstance(sub_obj, Role): + if sub_obj.content_object is None: + raise PermissionDenied("The {} role cannot be assigned to a team".format(sub_obj.name)) + elif isinstance(sub_obj.content_object, User): + raise PermissionDenied("The admin_role for a User cannot be assigned to a team") + + if isinstance(sub_obj.content_object, ResourceMixin): + role_access = RoleAccess(self.user) + return role_access.can_attach(sub_obj, obj, 'member_role.parents', + *args, **kwargs) return super(TeamAccess, self).can_attach(obj, sub_obj, relationship, *args, **kwargs) def can_unattach(self, obj, sub_obj, relationship, *args, **kwargs): - if isinstance(sub_obj, Role) and isinstance(sub_obj.content_object, ResourceMixin): - role_access = RoleAccess(self.user) - return role_access.can_unattach(sub_obj, obj, 'member_role.parents', - *args, **kwargs) + if isinstance(sub_obj, Role): + if isinstance(sub_obj.content_object, ResourceMixin): + role_access = RoleAccess(self.user) + return role_access.can_unattach(sub_obj, obj, 'member_role.parents', + *args, **kwargs) return super(TeamAccess, self).can_unattach(obj, sub_obj, relationship, *args, **kwargs) From 74b013aad0905120410317b4f084deadb578dc7a Mon Sep 17 00:00:00 2001 From: Wayne Witzel III Date: Tue, 26 Jul 2016 12:09:58 -0400 Subject: [PATCH 20/26] driveby cleanup --- awx/main/access.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/awx/main/access.py b/awx/main/access.py index 2c01b62065..c109e5d449 100644 --- a/awx/main/access.py +++ b/awx/main/access.py @@ -1688,8 +1688,7 @@ class RoleAccess(BaseAccess): if not check_user_access(self.user, sub_obj.__class__, 'read', sub_obj): return False - if obj.object_id and \ - isinstance(obj.content_object, ResourceMixin) and \ + if isinstance(obj.content_object, ResourceMixin) and \ self.user in obj.content_object.admin_role: return True return False From df1464d4bf47c4fbf93827c247db20b97a8d9a46 Mon Sep 17 00:00:00 2001 From: Wayne Witzel III Date: Tue, 26 Jul 2016 12:10:19 -0400 Subject: [PATCH 21/26] fixing tests for new team role assignment restrictions --- awx/main/tests/functional/test_rbac_api.py | 28 ++++++++++----------- awx/main/tests/functional/test_rbac_team.py | 12 ++++----- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/awx/main/tests/functional/test_rbac_api.py b/awx/main/tests/functional/test_rbac_api.py index d5ccbdf5d0..54dcc8deb5 100644 --- a/awx/main/tests/functional/test_rbac_api.py +++ b/awx/main/tests/functional/test_rbac_api.py @@ -12,7 +12,7 @@ def mock_feature_enabled(feature, bypass_database=None): @pytest.fixture def role(): - return Role.objects.create() + return Role.objects.create(role_field='admin_role') # @@ -210,33 +210,33 @@ def test_get_teams_roles_list(get, team, organization, admin): @pytest.mark.django_db -def test_add_role_to_teams(team, role, post, admin): - assert team.member_role.children.filter(id=role.id).count() == 0 +def test_add_role_to_teams(team, post, admin): + assert team.member_role.children.filter(id=team.member_role.id).count() == 0 url = reverse('api:team_roles_list', args=(team.id,)) - response = post(url, {'id': role.id}, admin) + response = post(url, {'id': team.member_role.id}, admin) assert response.status_code == 204 - assert team.member_role.children.filter(id=role.id).count() == 1 + assert team.member_role.children.filter(id=team.member_role.id).count() == 1 - response = post(url, {'id': role.id}, admin) + response = post(url, {'id': team.member_role.id}, admin) assert response.status_code == 204 - assert team.member_role.children.filter(id=role.id).count() == 1 + assert team.member_role.children.filter(id=team.member_role.id).count() == 1 response = post(url, {}, admin) assert response.status_code == 400 - assert team.member_role.children.filter(id=role.id).count() == 1 + assert team.member_role.children.filter(id=team.member_role.id).count() == 1 @pytest.mark.django_db -def test_remove_role_from_teams(team, role, post, admin): - assert team.member_role.children.filter(id=role.id).count() == 0 +def test_remove_role_from_teams(team, post, admin): + assert team.member_role.children.filter(id=team.member_role.id).count() == 0 url = reverse('api:team_roles_list', args=(team.id,)) - response = post(url, {'id': role.id}, admin) + response = post(url, {'id': team.member_role.id}, admin) assert response.status_code == 204 - assert team.member_role.children.filter(id=role.id).count() == 1 + assert team.member_role.children.filter(id=team.member_role.id).count() == 1 - response = post(url, {'disassociate': role.id, 'id': role.id}, admin) + response = post(url, {'disassociate': team.member_role.id, 'id': team.member_role.id}, admin) assert response.status_code == 204 - assert team.member_role.children.filter(id=role.id).count() == 0 + assert team.member_role.children.filter(id=team.member_role.id).count() == 0 diff --git a/awx/main/tests/functional/test_rbac_team.py b/awx/main/tests/functional/test_rbac_team.py index 0c16ba9f6f..6907589462 100644 --- a/awx/main/tests/functional/test_rbac_team.py +++ b/awx/main/tests/functional/test_rbac_team.py @@ -10,17 +10,17 @@ def test_team_attach_unattach(team, user): access = TeamAccess(u) team.member_role.members.add(u) - assert not access.can_attach(team, u.admin_role, 'member_role.children', None) - assert not access.can_unattach(team, u.admin_role, 'member_role.children') + assert not access.can_attach(team, team.member_role, 'member_role.children', None) + assert not access.can_unattach(team, team.member_role, 'member_role.children') team.admin_role.members.add(u) - assert access.can_attach(team, u.admin_role, 'member_role.children', None) - assert access.can_unattach(team, u.admin_role, 'member_role.children') + assert access.can_attach(team, team.member_role, 'member_role.children', None) + assert access.can_unattach(team, team.member_role, 'member_role.children') u2 = user('non-member', False) access = TeamAccess(u2) - assert not access.can_attach(team, u2.admin_role, 'member_role.children', None) - assert not access.can_unattach(team, u2.admin_role, 'member_role.chidlren') + assert not access.can_attach(team, team.member_role, 'member_role.children', None) + assert not access.can_unattach(team, team.member_role, 'member_role.chidlren') @pytest.mark.django_db def test_team_access_superuser(team, user): From a0c563e831f3a729513e87b1f479ec9dd804cd58 Mon Sep 17 00:00:00 2001 From: Wayne Witzel III Date: Tue, 26 Jul 2016 14:50:48 -0400 Subject: [PATCH 22/26] flake8 fixups --- awx/main/notifications/webhook_backend.py | 1 - tools/data_generators/rbac_dummy_data_generator.py | 10 +++------- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/awx/main/notifications/webhook_backend.py b/awx/main/notifications/webhook_backend.py index fe887efc68..e74f39f654 100644 --- a/awx/main/notifications/webhook_backend.py +++ b/awx/main/notifications/webhook_backend.py @@ -3,7 +3,6 @@ import logging import requests -import json from django.utils.encoding import smart_text diff --git a/tools/data_generators/rbac_dummy_data_generator.py b/tools/data_generators/rbac_dummy_data_generator.py index ad90dc74a9..f9e583c3a3 100755 --- a/tools/data_generators/rbac_dummy_data_generator.py +++ b/tools/data_generators/rbac_dummy_data_generator.py @@ -3,11 +3,6 @@ # All Rights Reserved import os import sys -os.environ.setdefault("DJANGO_SETTINGS_MODULE", "awx.settings.development") # noqa - -import django -django.setup() # noqa - # Python from collections import defaultdict @@ -15,7 +10,7 @@ from optparse import make_option, OptionParser # Django - +import django from django.utils.timezone import now from django.contrib.auth.models import User from django.db import transaction @@ -23,7 +18,8 @@ from django.db import transaction # awx from awx.main.models import * # noqa - +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "awx.settings.development") # noqa +django.setup() # noqa option_list = [ From f53a1b8f6c9f119c13e0c1eb54a3ce269ad0b7b2 Mon Sep 17 00:00:00 2001 From: James Laska Date: Wed, 27 Jul 2016 10:18:10 -0400 Subject: [PATCH 23/26] Use foreman.id instead foreman.uuid isn't always present --- awx/settings/defaults.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awx/settings/defaults.py b/awx/settings/defaults.py index 7fc78c90aa..aa8f69866b 100644 --- a/awx/settings/defaults.py +++ b/awx/settings/defaults.py @@ -680,7 +680,7 @@ SATELLITE6_ENABLED_VALUE = 'true' SATELLITE6_GROUP_FILTER = r'^.+$' SATELLITE6_HOST_FILTER = r'^.+$' SATELLITE6_EXCLUDE_EMPTY_GROUPS = True -SATELLITE6_INSTANCE_ID_VAR = 'foreman.uuid' +SATELLITE6_INSTANCE_ID_VAR = 'foreman.id' # --------------------- # -- Activity Stream -- From 45e506ec0f8f34a8aa55f63285b5ccf185905042 Mon Sep 17 00:00:00 2001 From: Jared Tabor Date: Wed, 27 Jul 2016 11:28:05 -0700 Subject: [PATCH 24/26] Make check for idle session more specific in order to avoid collision with a social auth session's startup. --- awx/ui/client/src/app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awx/ui/client/src/app.js b/awx/ui/client/src/app.js index e25d53ac51..6b90c16b5d 100644 --- a/awx/ui/client/src/app.js +++ b/awx/ui/client/src/app.js @@ -872,7 +872,7 @@ var tower = angular.module('Tower', [ } else { var lastUser = $cookieStore.get('current_user'), timestammp = Store('sessionTime'); - if(lastUser && lastUser.id && timestammp && timestammp[lastUser.id]){ + if(lastUser && lastUser.id && timestammp && timestammp[lastUser.id] && timestammp[lastUser.id].loggedIn){ var stime = timestammp[lastUser.id].time, now = new Date().getTime(); if ((stime - now) <= 0) { From 945635eb70d8cefadc97218981fb95d1465e08ab Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Wed, 27 Jul 2016 16:54:50 -0400 Subject: [PATCH 25/26] also limit creation of system auditors to superusers squash --- awx/main/access.py | 10 ++++++---- awx/main/tests/functional/test_rbac_user.py | 13 +++++++++++++ 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/awx/main/access.py b/awx/main/access.py index c109e5d449..d8fe5b245b 100644 --- a/awx/main/access.py +++ b/awx/main/access.py @@ -245,16 +245,18 @@ class UserAccess(BaseAccess): def can_add(self, data): - if data is not None and 'is_superuser' in data: - if to_python_boolean(data['is_superuser'], allow_none=True) and not self.user.is_superuser: + if data is not None and ('is_superuser' in data or 'is_system_auditor' in data): + if (to_python_boolean(data.get('is_superuser', 'false'), allow_none=True) or + to_python_boolean(data.get('is_system_auditor', 'false'), allow_none=True)) and not self.user.is_superuser: return False if self.user.is_superuser: return True return Organization.accessible_objects(self.user, 'admin_role').exists() def can_change(self, obj, data): - if data is not None and 'is_superuser' in data: - if to_python_boolean(data['is_superuser'], allow_none=True) and not self.user.is_superuser: + if data is not None and ('is_superuser' in data or 'is_system_auditor' in data): + if (to_python_boolean(data.get('is_superuser', 'false'), allow_none=True) or + to_python_boolean(data.get('is_system_auditor', 'false'), allow_none=True)) and not self.user.is_superuser: return False # A user can be changed if they are themselves, or by org admins or # superusers. Change permission implies changing only certain fields diff --git a/awx/main/tests/functional/test_rbac_user.py b/awx/main/tests/functional/test_rbac_user.py index c5959a2c32..de2b9aa8b1 100644 --- a/awx/main/tests/functional/test_rbac_user.py +++ b/awx/main/tests/functional/test_rbac_user.py @@ -75,3 +75,16 @@ def test_org_user_removed(user, organization): organization.member_role.members.remove(member) assert admin not in member.admin_role + +@pytest.mark.django_db +def test_org_admin_create_sys_auditor(org_admin): + access = UserAccess(org_admin) + assert not access.can_add(data=dict( + username='new_user', password="pa$$sowrd", email="asdf@redhat.com", + is_system_auditor='true')) + +@pytest.mark.django_db +def test_org_admin_edit_sys_auditor(org_admin, alice, organization): + organization.member_role.members.add(alice) + access = UserAccess(org_admin) + assert not access.can_change(obj=alice, data=dict(is_system_auditor='true')) From 951d8e1555abd0e936d2369120ad5e312328972b Mon Sep 17 00:00:00 2001 From: Graham Mainwaring Date: Thu, 28 Jul 2016 10:31:29 -0400 Subject: [PATCH 26/26] Check that venv is activated before loading wsgi app (#3148) Check that venv is activated before loading wsgi app --- config/wsgi.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/config/wsgi.py b/config/wsgi.py index 9999eb08c8..c053481836 100644 --- a/config/wsgi.py +++ b/config/wsgi.py @@ -1 +1,4 @@ +import sys +if sys.prefix != '/var/lib/awx/venv/tower': + raise RuntimeError('Tower virtualenv not activated. Check WSGIPythonHome in Apache configuration.') from awx.wsgi import application # NOQA