diff --git a/ansible_base/authentication/migrations/0001_initial.py b/ansible_base/authentication/migrations/0001_initial.py index a81feaffa..6257d55ca 100644 --- a/ansible_base/authentication/migrations/0001_initial.py +++ b/ansible_base/authentication/migrations/0001_initial.py @@ -26,7 +26,7 @@ class Migration(migrations.Migration): ('create_objects', models.BooleanField(default=True, help_text='Allow authenticator to create objects (users, teams, organizations)')), ('users_unique', models.BooleanField(default=False, help_text='Are users from this source the same as users from another source with the same id')), ('remove_users', models.BooleanField(default=True, help_text='When a user authenticates from this source should they be removed from any other groups they were previously added to')), - ('configuration', models.JSONField(default=dict, help_text='The required configuration for this source')), + ('configuration', models.JSONField(default=dict, blank=True, help_text='The required configuration for this source')), ('type', models.CharField(editable=False, help_text='The type of authentication service this is', max_length=256)), ('order', models.IntegerField(default=1, help_text='The order in which an authenticator will be tried. This only pertains to username/password authenticators')), ('slug', models.SlugField(default=None, editable=False, help_text='An immutable identifier for the authenticator', max_length=1024, unique=True)), @@ -48,9 +48,9 @@ class Migration(migrations.Migration): ('name', models.CharField(help_text='The name of this resource', max_length=512)), ('revoke', models.BooleanField(default=False, help_text='If a user does not meet this rule should we revoke the permission')), ('map_type', models.CharField(choices=[('team', 'team'), ('is_superuser', 'is_superuser'), ('is_system_auditor', 'is_system_auditor'), ('allow', 'allow'), ('organization', 'organization')], default='team', help_text='What does the map work on, a team, a user flag or is this an allow rule', max_length=17)), - ('team', models.CharField(default=None, help_text='A team name this rule works on', max_length=512, null=True)), - ('organization', models.CharField(default=None, help_text='An organization name this rule works on', max_length=512, null=True)), - ('triggers', models.JSONField(default=dict, help_text='Trigger information for this rule')), + ('team', models.CharField(default=None, blank=True, help_text='A team name this rule works on', max_length=512, null=True)), + ('organization', models.CharField(default=None, blank=True, help_text='An organization name this rule works on', max_length=512, null=True)), + ('triggers', models.JSONField(default=dict, blank=True, help_text='Trigger information for this rule')), ('order', models.PositiveIntegerField(default=0, help_text='The order in which this rule should be processed, smaller numbers are of higher precedence. Items with the same order will be executed in random order')), ('authenticator', models.ForeignKey(help_text='The authenticator this mapping belongs to', on_delete=django.db.models.deletion.CASCADE, to='authentication.authenticator')), ('created_by', models.ForeignKey(default=None, editable=False, help_text='The user who created this resource', null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='%(app_label)s_%(class)s_created+', to=settings.AUTH_USER_MODEL)), @@ -65,8 +65,8 @@ class Migration(migrations.Migration): ('extra_data', models.JSONField(default=dict)), ('created', models.DateTimeField(auto_now_add=True)), ('modified', models.DateTimeField(auto_now=True)), - ('claims', models.JSONField(default=dict)), - ('last_login_map_results', models.JSONField(default=list)), + ('claims', models.JSONField(default=dict, blank=True)), + ('last_login_map_results', models.JSONField(default=list, blank=True)), ('access_allowed', models.BooleanField(default=None, null=True)), ('provider', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='authenticator_user', to='authentication.authenticator', to_field='slug')), ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='authenticator_user', to=settings.AUTH_USER_MODEL)), diff --git a/ansible_base/authentication/models/authenticator.py b/ansible_base/authentication/models/authenticator.py index 060db242b..b5199ad71 100644 --- a/ansible_base/authentication/models/authenticator.py +++ b/ansible_base/authentication/models/authenticator.py @@ -14,7 +14,7 @@ class Authenticator(UniqueNamedCommonModel): remove_users = fields.BooleanField( default=True, help_text="When a user authenticates from this source should they be removed from any other groups they were previously added to" ) - configuration = prevent_search(JSONField(default=dict, help_text="The required configuration for this source")) + configuration = prevent_search(JSONField(default=dict, help_text="The required configuration for this source", blank=True)) type = fields.CharField( editable=False, max_length=256, diff --git a/ansible_base/authentication/models/authenticator_map.py b/ansible_base/authentication/models/authenticator_map.py index 7173979a3..084665702 100644 --- a/ansible_base/authentication/models/authenticator_map.py +++ b/ansible_base/authentication/models/authenticator_map.py @@ -48,17 +48,20 @@ class Meta: max_length=512, null=True, default=None, + blank=True, help_text='A team name this rule works on', ) organization = models.CharField( max_length=512, null=True, default=None, + blank=True, help_text='An organization name this rule works on', ) triggers = models.JSONField( null=False, default=dict, + blank=True, help_text="Trigger information for this rule", ) order = models.PositiveIntegerField( diff --git a/ansible_base/authentication/models/social_auth.py b/ansible_base/authentication/models/social_auth.py index 166525f27..d94cbaaa5 100644 --- a/ansible_base/authentication/models/social_auth.py +++ b/ansible_base/authentication/models/social_auth.py @@ -15,8 +15,8 @@ class AuthenticatorUser(AbstractUserSocialAuth): user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name="authenticator_user", on_delete=models.CASCADE) # TODO: set self.authenticated based on the provider that is passed to this method. # the provider should be the name of the Authenticator model instance - claims = models.JSONField(default=dict, null=False) - last_login_map_results = models.JSONField(default=list, null=False) + claims = models.JSONField(default=dict, null=False, blank=True) + last_login_map_results = models.JSONField(default=list, null=False, blank=True) # This field tracks if a user passed or failed an allow map access_allowed = models.BooleanField(default=None, null=True) diff --git a/ansible_base/authentication/serializers/authenticator_map.py b/ansible_base/authentication/serializers/authenticator_map.py index 847fc982e..f8a422e86 100644 --- a/ansible_base/authentication/serializers/authenticator_map.py +++ b/ansible_base/authentication/serializers/authenticator_map.py @@ -22,11 +22,11 @@ def validate(self, data) -> dict: map_type = data.get('map_type', None) team = data.get('team', None) org = data.get('organization', None) - if map_type == 'team' and not team: + if map_type == 'team' and (not team or team == ''): errors["team"] = "You must specify a team with the selected map type" - if map_type == 'team' and not org: + if map_type == 'team' and (not org or org == ''): errors["organization"] = "You must specify an organization with the selected map type" - if map_type == 'organization' and not org: + if map_type == 'organization' and (not org or org == ''): errors["organization"] = "You must specify an organization with the selected map type" if not data.get('order', None): diff --git a/ansible_base/lib/abstract_models/organization.py b/ansible_base/lib/abstract_models/organization.py index 2d746dbb4..d128f5441 100644 --- a/ansible_base/lib/abstract_models/organization.py +++ b/ansible_base/lib/abstract_models/organization.py @@ -15,11 +15,13 @@ class Meta: description = models.TextField( null=False, default="", + blank=True, help_text=_("The organization description."), ) users = models.ManyToManyField( settings.AUTH_USER_MODEL, related_name="organizations", + blank=True, help_text=_("The list of users in this organization."), ) diff --git a/test_app/migrations/0002_team_organization.py b/test_app/migrations/0002_team_organization.py index 168da7b85..b8c33a7b1 100644 --- a/test_app/migrations/0002_team_organization.py +++ b/test_app/migrations/0002_team_organization.py @@ -27,11 +27,11 @@ class Migration(migrations.Migration): ('created_on', models.DateTimeField(default=None, editable=False, help_text='The date/time this resource was created')), ('modified_on', models.DateTimeField(default=None, editable=False, help_text='The date/time this resource was created')), ('name', models.CharField(help_text='The name of this resource', max_length=512, unique=True)), - ('description', models.TextField(default='', help_text='The organization description.')), + ('description', models.TextField(default='', blank=True, help_text='The organization description.')), ('created_by', models.ForeignKey(default=None, editable=False, help_text='The user who created this resource', null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='%(app_label)s_%(class)s_created+', to=settings.AUTH_USER_MODEL)), ('modified_by', models.ForeignKey(default=None, editable=False, help_text='The user who last modified this resource', null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='%(app_label)s_%(class)s_modified+', to=settings.AUTH_USER_MODEL)), - ('teams', models.ManyToManyField(help_text='The list of teams in this organization.', related_name='organizations', to='test_app.team')), - ('users', models.ManyToManyField(help_text='The list of users in this organization.', related_name='organizations', to=settings.AUTH_USER_MODEL)), + ('teams', models.ManyToManyField(help_text='The list of teams in this organization.', blank=True, related_name='organizations', to='test_app.team')), + ('users', models.ManyToManyField(help_text='The list of users in this organization.', blank=True, related_name='organizations', to=settings.AUTH_USER_MODEL)), ], options={ 'abstract': False, diff --git a/test_app/tests/authentication/views/test_authenticator_map.py b/test_app/tests/authentication/views/test_authenticator_map.py index c3b2b1de4..1007830df 100644 --- a/test_app/tests/authentication/views/test_authenticator_map.py +++ b/test_app/tests/authentication/views/test_authenticator_map.py @@ -105,7 +105,7 @@ def test_authenticator_map_invalid_map_type(admin_api_client, local_authenticato 'is_superuser', {'name': 'Rule 1', 'map_type': 'team', 'triggers': {'always': {}}, 'order': 1, 'organization': 'foobar-org', 'team': ''}, 'team', - "This field may not be blank.", + "You must specify a team with the selected map type", id="map_type=team, team param is empty string", ), pytest.param( @@ -126,7 +126,7 @@ def test_authenticator_map_invalid_map_type(admin_api_client, local_authenticato 'is_superuser', {'name': 'Rule 1', 'map_type': 'team', 'triggers': {'always': {}}, 'order': 1, 'team': 'foobar-team', 'organization': ''}, 'organization', - "This field may not be blank.", + "You must specify an organization with the selected map type", id="map_type=team, organization param is empty string", ), pytest.param(