Skip to content

Conversation

MyPyDavid
Copy link
Member

@MyPyDavid MyPyDavid commented Sep 22, 2025

Description

Related issue: #1425

@MyPyDavid MyPyDavid added this to the RDMO 2.3.3 milestone Sep 22, 2025
@MyPyDavid MyPyDavid self-assigned this Sep 22, 2025


# Add rules
rules.add_rule('management.can_view_management',
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this will break the backwards compatibility on the base_navigation template, should I keep the the add_rule in there just to not break the possible deployed custom templates?
Otherwise instance admins would need to update the theme for that. Think that it is better to use add_perm than add_rule..

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, but can we have both and remove one in RDMO 3?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, I think add_rule fits better since this is just a predicate "exposed" to the user instance in views etc. I think we should use add_rule for all things can_... and add_perm for model/object permissions. management.can_view_management has no input model or instance. Same for management.can_upload_files and management.can_import_elements.

ChatGPT explained it nicely:


🔹 rules.add_rule(name, function)

  • What it does: Registers a predicate function under a name.
  • A predicate is just a Python function that takes (user, obj) and returns True or False.
  • Predicates are reusable building blocks.
  • Example:
import rules

# Define a predicate
@rules.predicate
def is_staff(user):
    return user.is_staff

# Register it under a name
rules.add_rule('is_staff', is_staff)

Now you can reference "is_staff" as a condition in permissions or combine it with others.


🔹 rules.add_perm(name, rule)

  • What it does: Registers a permission under a name and attaches it to one or more rules (predicates).
  • A permission is basically a "named check" that can be used in your app (e.g. in user.has_perm() or decorators).
  • Example:
# Define a permission based on our predicate
rules.add_perm('app.view_secret', is_staff)

Now, anywhere in Django, you can do:

user.has_perm('app.view_secret')

and it will use the is_staff predicate internally.


✅ Summary

  • add_rule → defines a predicate (logic building block).
  • add_perm → defines a permission that Django can check, usually built from rules/predicates.

Think of it like this:

  • Rule = ingredient
  • Permission = finished dish 🍲

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok now I see that its actually used as a permission in ManagementView so I guess has_perm is the better idea. Maybe we should remove the can_ from the perms then:

management.view_management, management.upload_files, management.import_elements

maybe even management.upload_elements?

Then the naming is consistent and management and elements are kind of "fake" models, if you know what I mean.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same for projects.can_view_all_projects to projects.view_projects.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, Ive renamed the permissions like that. The has_perm is better for when it will be used on a user object anyway..

return super().has_permission(request, view)


class HasRulesPermission(BasePermission):
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is for the Upload and Import viewsets

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! I checked the drf and rules code, because I was wondering if there is something like this already. This just introduces the permission_required class attribute in the same way as the regular django views, right? I would rename the class to HasPermission, since also all the other Permissions somehow depend on rules.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, HasPermission it is..


permission_classes = (IsAuthenticated, )
permission_classes = [HasRulesPermission]
permission_required = 'management.can_view_management'
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is the meta used anywhere outside of the management as well??

Copy link
Member

@jochenklar jochenklar Sep 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, I don't thin so.

Copy link
Member

@jochenklar jochenklar Sep 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Meta should stay IsAuthenticated since it is just the static information about fields and help texts etc.

Copy link
Member

@jochenklar jochenklar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very nice, thanks! I have the usual naming requests and the lengthy discussion with myself (and ChatGPT) which I want to share with you 😃



# Add rules
rules.add_rule('management.can_view_management',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, but can we have both and remove one in RDMO 3?



# Add rules
rules.add_rule('management.can_view_management',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, I think add_rule fits better since this is just a predicate "exposed" to the user instance in views etc. I think we should use add_rule for all things can_... and add_perm for model/object permissions. management.can_view_management has no input model or instance. Same for management.can_upload_files and management.can_import_elements.

ChatGPT explained it nicely:


🔹 rules.add_rule(name, function)

  • What it does: Registers a predicate function under a name.
  • A predicate is just a Python function that takes (user, obj) and returns True or False.
  • Predicates are reusable building blocks.
  • Example:
import rules

# Define a predicate
@rules.predicate
def is_staff(user):
    return user.is_staff

# Register it under a name
rules.add_rule('is_staff', is_staff)

Now you can reference "is_staff" as a condition in permissions or combine it with others.


🔹 rules.add_perm(name, rule)

  • What it does: Registers a permission under a name and attaches it to one or more rules (predicates).
  • A permission is basically a "named check" that can be used in your app (e.g. in user.has_perm() or decorators).
  • Example:
# Define a permission based on our predicate
rules.add_perm('app.view_secret', is_staff)

Now, anywhere in Django, you can do:

user.has_perm('app.view_secret')

and it will use the is_staff predicate internally.


✅ Summary

  • add_rule → defines a predicate (logic building block).
  • add_perm → defines a permission that Django can check, usually built from rules/predicates.

Think of it like this:

  • Rule = ingredient
  • Permission = finished dish 🍲


permission_classes = (IsAuthenticated, )
permission_classes = [HasRulesPermission]
permission_required = 'management.can_view_management'
Copy link
Member

@jochenklar jochenklar Sep 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, I don't thin so.


permission_classes = (IsAuthenticated, )
permission_classes = [HasRulesPermission]
permission_required = 'management.can_view_management'
Copy link
Member

@jochenklar jochenklar Sep 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Meta should stay IsAuthenticated since it is just the static information about fields and help texts etc.



# Add rules
rules.add_rule('management.can_view_management',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok now I see that its actually used as a permission in ManagementView so I guess has_perm is the better idea. Maybe we should remove the can_ from the perms then:

management.view_management, management.upload_files, management.import_elements

maybe even management.upload_elements?

Then the naming is consistent and management and elements are kind of "fake" models, if you know what I mean.



# Add rules
rules.add_rule('management.can_view_management',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same for projects.can_view_all_projects to projects.view_projects.

return False

# prefer a view-level override when present
perm = getattr(view, 'permission_required', None)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The None is not needed and I would turn it around, check the perm if perm and raise the error at the bottom.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I now simply return:

        # the viewset needs to set permission_required
        return request.user.has_perm(view.permission_required)

I think it should only be used like that anyway and break when we would forget to set a permission_required on the viewset.

return super().has_permission(request, view)


class HasRulesPermission(BasePermission):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! I checked the drf and rules code, because I was wondering if there is something like this already. This just introduces the permission_required class attribute in the same way as the regular django views, right? I would rename the class to HasPermission, since also all the other Permissions somehow depend on rules.

Copy link
Member

@jochenklar jochenklar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work! I think we are done here.

{% back_to_project_link %}

{% test_rule 'management.can_view_management' request.user request.site as can_view_management %}
{% comment %}the test_rule will be removed in RDMO 3.0{% endcomment %}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think here we can just use {% has_perm 'management.view_management' request.user as can_view_management %} only test_rule should still exist for the legacy templates.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah yes, that would work without breaking it! 😅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants