-
Notifications
You must be signed in to change notification settings - Fork 3.5k
Prevent modifying shared resources when using platform ingress #15234
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
Prevent modifying shared resources when using platform ingress #15234
Conversation
3ee4dd0
to
08ddaac
Compare
49b01c4
to
46e2958
Compare
awx/sso/conf.py
Outdated
category=_('Authentication'), | ||
category_slug='authentication', | ||
) | ||
if settings.DIRECT_SHARED_RESOURCE_MANAGEMENT_ENABLED: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
did we check if this breaks the UI?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the external auth settings links are still present, and lead to 404s
What about adding/removing users from organization/team members/admins? |
The setting in the eda-server patch is We will have a need to use either the same setting in DAB, or a different setting that does the same thing DAB-specific. I don't care what things are called as long as they are plausibly consistent in all the systems. Given the current state of things, the only way this makes sense is to rename the setting here to |
went ahead and renamed the setting to |
def update_wrapper(*args, **kwargs): | ||
if not settings.AWX_DIRECT_SHARED_RESOURCE_MANAGEMENT_ENABLED: | ||
view, serializer = args | ||
instance = view.get_object() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fairly strong preference to replace this line with instance = serializer.instance
If you read the origination of serializer
here, you can see instance
is already passed into it. The get_object
method is very non-trivial and expensive and we should avoid re-calling.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
okay I tried this and it gets tricky
instance = serializer.instance
this returns an instance populated with new data, not the original data. So I can't compare attrs[field] and instance.field
I can access _prior_values_store
, but those dictionary keys don't match one to one with the shared_fields
for example, for a Team
'_prior_values_store': {'description': 'sbf',
'name': 'teamA',
'organization_id': 1},
but the shared_field is called organization
, not organization_id
I could hard-code organization
to organization_id
..but then this code path becomes less generalized.
do you have a suggestion for this? I do agree that re-running a get_object() is expensive
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
instance = serializer.instance
this returns an instance populated with new data
In what scenario does this happen? Digging back into DRF, I believe update
is called for both PATCH and PUT, with the partial
being True/False depending on which. That partial
kwarg gets passed to the serializer, but in either case the serializer itself is passed instance
for the existing instance.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sadly we are doing this..
Line 645 in 389a729
setattr(obj, k, v) |
so in is_valid() we set these attributes
here is the drf update for our views
def update(self, request, *args, **kwargs):
partial = kwargs.pop('partial', False)
instance = self.get_object()
serializer = self.get_serializer(instance, data=request.data, partial=partial)
serializer.is_valid(raise_exception=True)
self.perform_update(serializer)
so by the time we get to perform_update
our serializer.instance already has new values
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
kind of gross but we could considering something like
diff --git a/awx/api/views/__init__.py b/awx/api/views/__init__.py
index 4ed8f8b2f6..17829b3698 100644
--- a/awx/api/views/__init__.py
+++ b/awx/api/views/__init__.py
@@ -748,6 +748,15 @@ def immutablesharedfields(cls):
cls.delete = delete_wrapper
+ cls.original_get_object = cls.get_object
+ def get_object_wrapper(*args, **kwargs):
+ view = args[0]
+ obj = cls.original_get_object(*args, **kwargs)
+ import copy
+ view._original_obj = copy.deepcopy(obj)
+ return obj
+ cls.get_object = get_object_wrapper
+
if hasattr(cls, 'perform_update'):
cls.original_perform_update = cls.perform_update
@@ -755,7 +764,7 @@ def immutablesharedfields(cls):
def update_wrapper(*args, **kwargs):
if not settings.AWX_DIRECT_SHARED_RESOURCE_MANAGEMENT_ENABLED:
view, serializer = args
- instance = view.get_object()
+ instance = view._original_obj
if instance:
if isinstance(instance, models.Organization):
shared_fields = OrganizationType._declared_fields.keys()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right, that's grosser than just calling .get_object
, it's just hamstrung by our legacy serializer. Normal DRF doesn't have this problem of getting a diff, and it's just AWX that's unorthodox.
Adds a class decorator to prevent modifying shared resources when gateway is being used. DIRECT_SHARED_RESOURCE_MANAGEMENT_ENABLED is the setting to enable/disable this feature. Works by overriding these view methods: - create - delete - perform_update create and delete are overridden to raise a PermissionDenied exception. perform_update is overridden to check if any shared fields are being modified, and raise a PermissionDenied exception if so. Signed-off-by: Seth Foster <[email protected]>
Signed-off-by: Seth Foster <[email protected]>
Signed-off-by: Seth Foster <[email protected]>
Signed-off-by: Seth Foster <[email protected]>
Prevent sso conf from registering external authentication related settings if DIRECT_SHARED_RESOURCE_MANAGEMENT_ENABLED is False Signed-off-by: Seth Foster <[email protected]>
Do not check for setting at import time, which causes the override settings in pytest to be flakey. Instead we can check for settings.DIRECT_SHARED_RESOURCE_MANAGEMENT_ENABLED in the method call. Signed-off-by: Seth Foster <[email protected]>
Signed-off-by: Seth Foster <[email protected]>
Co-authored-by: Hao Liu <[email protected]>
Signed-off-by: Seth Foster <[email protected]>
Signed-off-by: Seth Foster <[email protected]>
153252f
to
6159ce3
Compare
Signed-off-by: Seth Foster <[email protected]>
Signed-off-by: Seth Foster <[email protected]>
Trying to wrap up this review, what's the status of adding validation for |
Oh I see the |
…le#15234) * Prevent modifying shared resources Adds a class decorator to prevent modifying shared resources when gateway is being used. AWX_DIRECT_SHARED_RESOURCE_MANAGEMENT_ENABLED is the setting to enable/disable this feature. Works by overriding these view methods: - create - delete - perform_update create and delete are overridden to raise a PermissionDenied exception. perform_update is overridden to check if any shared fields are being modified, and raise a PermissionDenied exception if so. Additional changes: Prevent sso conf from registering external authentication related settings if AWX_DIRECT_SHARED_RESOURCE_MANAGEMENT_ENABLED is False Signed-off-by: Seth Foster <[email protected]> Co-authored-by: Hao Liu <[email protected]>
…le#15234) * Prevent modifying shared resources Adds a class decorator to prevent modifying shared resources when gateway is being used. AWX_DIRECT_SHARED_RESOURCE_MANAGEMENT_ENABLED is the setting to enable/disable this feature. Works by overriding these view methods: - create - delete - perform_update create and delete are overridden to raise a PermissionDenied exception. perform_update is overridden to check if any shared fields are being modified, and raise a PermissionDenied exception if so. Additional changes: Prevent sso conf from registering external authentication related settings if AWX_DIRECT_SHARED_RESOURCE_MANAGEMENT_ENABLED is False Signed-off-by: Seth Foster <[email protected]> Co-authored-by: Hao Liu <[email protected]>
SUMMARY
List of endpoints affected
Preventing CRUD operations on organizations, teams, and users
Adds a class decorator to prevent modifying shared resources when the platform ingress is being used.
AWX_DIRECT_SHARED_RESOURCE_MANAGEMENT_ENABLED
is the setting to enable/disable this feature.Works by overriding these view methods:
create and delete are overridden to raise a PermissionDenied exception.
perform_update is overridden to check if any shared fields are being modified, and raise a PermissionDenied exception if so.
Preventing modifying member_role and admin_role of organizations and teams
For this, we prevent any POST to the
/api/v2/users/4/roles
endpoint ifmember_role
oradmin_role
Preventing external auth
Don't register any of the external auth settings if
AWX_DIRECT_SHARED_RESOURCE_MANAGEMENT_ENABLED
is False.Right now this leads to 404 in the UI if you hit these links

Todo
ISSUE TYPE
COMPONENT NAME
AWX VERSION