# Access control

> Use a delegate class to decide whether an extension is visible to the viewer

---

Site [llms.txt](/llms.txt)

---

Pass a delegate class to the extension's decorator using the `delegate` argument. Its `should_display` method runs each time the page is built, returning `True` to show the extension and `False` to hide it. An extension with no delegate is displayed.

## Extension types that accept a delegate

Subclass the delegate class that matches the extension type, and import it from `extensions.views`.

| Extension type | Decorator | Delegate class to subclass |
| --- | --- | --- |
| Report | `report_extension` | `ReportExtensionDelegate` |
| Detail view tab | `tab_extension` | `TabExtensionDelegate` |
| Custom order form | `custom_form_extension` | `CustomFormExtensionDelegate` |

`dashboard_extension` also accepts a `delegate` argument, but Cloud Management Platform does not check it, so a dashboard card is shown to every user. Admin pages and the microfrontend types do not accept a delegate. An admin page needs none, since only CloudBolt admins and global viewers can view one.

## Write a delegate

This tab appears on a resource handler's detail view only when the handler is an AWS handler.

```python
from django.shortcuts import render

from extensions.views import tab_extension, TabExtensionDelegate
from resourcehandlers.aws.models import AWSHandler
from resourcehandlers.models import ResourceHandler


class AWSOnlyDelegate(TabExtensionDelegate):
    def should_display(self):
        return isinstance(self.instance.cast(), AWSHandler)


@tab_extension(
    model=ResourceHandler,
    title="Spend",
    description="Cost breakdown for this resource handler",
    delegate=AWSOnlyDelegate,
)
def spend_tab(request, obj_id):
    resource_handler = ResourceHandler.objects.get(id=obj_id)
    return render(
        request,
        "resource_handler_spend/templates/spend_tab.html",
        {"resource_handler": resource_handler},
    )
```

Inside `should_display`, three attributes are available:

- `self.viewer`: the `UserProfile` of the viewer.
- `self.instance`: the object whose detail view is being extended, on tab and custom form delegates.
- `self.extension`: the extension, including the decorator's title and description.

## Conditions you can test

Each can be a complete `should_display` body. Combine with `and` to narrow further.

| To show the extension only when | Return |
| --- | --- |
| The viewer is a CloudBolt admin or a super admin | `self.viewer.is_cbadmin` |
| The viewer belongs to a particular group. Super admins, devops admins, and global viewers pass this test regardless of membership. | `self.viewer.get_groups().filter(name="Sales Team").exists()` |
| The viewer holds a particular permission | `"orders.add_orderitem" in self.viewer.user.get_all_permissions()` |
| The object being viewed is a particular technology | `isinstance(self.instance.cast(), AWSHandler)` |

## Hiding is not protecting

<div class="alert alert-warning" role="alert">


A delegate decides what appears on screen, not who may reach it. Your dashboard card, report, or tab has its own URL, and a signed-in user who requests that URL directly reaches the view even while the delegate is hiding the extension. Check the viewer's permissions within the view function before displaying sensitive data.
</div>


## Related

- [UI elements that support extensions](/docs/cmp/developer-resources/ui-extensions/supported-elements/): The decorator and arguments for each extension type.
- [Override or hide built-in tabs](/docs/cmp/developer-resources/ui-extensions/override-tabs/): How a hidden tab interacts with `replaces` and `hide_ootb_tab` arguments.
- [Permissions](/docs/cmp/configure/access-and-security/groups-roles-permissions/permissions/): The permission names a delegate can test for.
