---
title: "Setting up access controls for properties"
slug: "setting-up-access-controls-for-properties"
updated: 2026-02-03T17:47:05Z
published: 2026-02-03T17:47:05Z
canonical: "support.lusid.com/setting-up-access-controls-for-properties"
---

> ## Documentation Index
> Fetch the complete documentation index at: https://support.lusid.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Setting up access controls for properties

In this tutorial, we'll see how to use data policies and roles to set up scope-specific access controls for properties in LUSID.

Property access controls work by checking the property key (`&lt;domain&gt;/&lt;scope&gt;/&lt;code&gt;`) whenever a property is retrieved, edited, or deleted. Even if a user has access to an entity, they may not have access to all properties on that entity unless explicitly granted.

Let's imagine we have a [person entity](/v1/docs/representing-people-in-lusid-using-person-entities) that is configured with the properties `Person/Details/Country` and `Person/SensitiveDetails/Address`. We want to set up access controls that:

- Allow most users to view `Person/Details/Country` and other properties within the `Person/Details/*` scope.
- Allow a small number of privileged users to view `Person/SensitiveDetails/Address` and other properties within the `Person/SensitiveDetails/*` scope in addition to properties in the `Person/Details/*` scope.

To do this, we need to create and assign two different roles to our users, one role containing a feature and data policy that allows users to make requests to the `GetPersonPropertyTimeSeries` API and read `Person/Details/*` property definitions and property values data, and the other role containing a data policy that allows read-only access to `Person/SensitiveDetails/*` property definitions and property values data.

> **Note**: To complete this tutorial, you must have suitable access control permissions. This can most easily be achieved by assigning your LUSID user the built-in `lusid-administrator` role, which should already be the case if you are the domain owner. If you are informed you do not have a license to perform a certain operation, [contact support](https://www.finbourne.com/contact).

## Understanding property permissions

You can control two types of property resources:

- **Property Definitions:** Control who can `Add`, `Read`, `List`, `Update`, or `Delete` property type definitions
- **Property Values:** Control who can `Read`, `Update`, or `Delete` the actual values attached to entities

For most operations, users need `Read` access to both the [property definition](/v1/docs/how-do-i-create-a-property-type) AND the [property value](/v1/docs/how-do-i-add-a-property-to-an-entity). For example, to update a property value, a user needs *property definition* `Read` access plus *property value* `Update` and `Read` access.

## Prerequisite: Setting up property definitions and person entities

### Creating property definitions

For our scenario, we first need ensure we have the following property definitions:

- `Person/Details/Country`
- `Person/SensitiveDetails/BankAccount`

To create `Person/Details/Country` , we can call the [CreatePropertyDefinition](https://www.lusid.com/docs/api#operation/CreatePropertyDefinition) API, passing in the following:

```json
curl -X POST "https://<your-domain>.lusid.com/api/api/propertydefinitions" 
  -H "Authorization: Bearer <your-API-access-token>" 
  -H "Content-Type: application/json-patch+json" 
  -d "{
    "domain": "Person",
    "scope": "Details",
    "code": "Country",
    "displayName": "Country trader operates in",
    "dataTypeId": {"scope": "system", "code": "string"},
    "lifeTime": "TimeVariant",
    "constraintStyle": "Property",
    "propertyDescription": "Country trader operates in"
}"
```

To create `Person/SensitiveDetails/Address` , we can call the [CreatePropertyDefinition](https://www.lusid.com/docs/api#operation/CreatePropertyDefinition) API again, passing in the following:

```json
curl -X POST "https://<your-domain>.lusid.com/api/api/propertydefinitions"
-H "Authorization: Bearer <your-API-access-token>"
-H "Content-Type: application/json"
-d "{
  "domain": "Person",
  "scope": "SensitiveDetails",
  "code": "Address",
  "displayName": "Trader home address",
  "dataTypeId": {"scope": "system", "code": "string"},
  "lifeTime": "TimeVariant",
  "constraintStyle": "Property",
  "propertyDescription": "Home address of trader"
}"
```

[Read more on creating a property type.](/v1/docs/how-do-i-create-a-property-type)

### **Creating a person entity**

We also need to ensure we have a person entity that has been decorated with the above properties. For this tutorial, let's create a *trader* person entity to decorate with some properties. To do this:

1. Create a property definition for a person entity identifier. The following call creates a property definition with a 3-stage property key of `Person/Traders/TraderId`:

```json
curl -X POST "https://<your-domain>.lusid.com/api/api/propertydefinitions"
-H "Content-Type: application/json-patch+json"
-H "Authorization: Bearer <your-API-access-token>"
-d "{
    "domain": "Person",
    "scope": "Traders",
    "code": "TraderId",
    "valueRequired": true,
    "displayName": "Trader identifier for Person entities",
    "dataTypeId": { "scope": "system", "code": "string" },
    "lifeTime": "Perpetual",
    "constraintStyle": "Identifier",
}"
```
2. Call the [UpsertPerson](https://www.lusid.com/docs/api#operation/UpsertPerson) API to create a person entity. Note the `labelValue` you give the trader identifier must be unique among all traders represented in LUSID, in this case `Trader5`.

```json
curl -X POST "https://<your-domain>.lusid.com/api/api/persons"
-H "Content-Type: application/json-patch+json"
-H "Authorization: Bearer <your-API-access-token>"
-d "{
    "identifiers": {
        "Person/Traders/TraderId": {
            "key": "Person/Traders/TraderId",
            "value": {
                "labelValue": "Trader5"
            }
        }
    },
    "displayName": "John Doe",
    "description": "A portfolio manager and trader in US investments",
    "properties": {
        "Person/Details/Country": {
            "key": "Person/Details/Country",
            "value": {
                "labelValue": "USA"
            }
        },
        "Person/SensitiveDetails/Address": {
            "key": "Person/SensitiveDetails/Address",
            "value": {
                "labelValue": "123 New Lane, New York"
            },
            "effectiveFrom": "2016-07-01T00:00:00.0000000+00:00"
        }
} }"
```

[Read more on creating person entities.](/v1/docs/representing-people-in-lusid-using-person-entities)

## Step 1: Creating a data and feature policy for unprivileged and privileged users

You can create policies to control access to the following actions for properties:

- Read
- Update
- Delete
- Any (all of the above)

For this tutorial, we will focus on controlling *read* access to various datapoints, but the same steps can be followed to control access for all of the actions above.

### Creating a policy for a less privileged user

We first need to create a policy that allows a user to do all of the following, but *not* read `Person/SensitiveDetails/*`:

- Grants access to the following LUSID features:
  - `GetPerson` API
  - `GetPersonPropertyTimeSeries` API
- Grants read-only access to the following data points:
  - All person entities.
  - Property definitions and property values under the scope `Person/Traders/*`.
  - Property definitions and property values under the scope `Person/Details/*`.

To create a policy for all of the above:

1. Sign into the [LUSID web app](https://www.lusid.com/app/home) and use the left-hand menu to navigate to **Identity and Access > Policies**: ![](https://cdn.document360.io/d575ad81-c0ed-4980-bbd1-d59ac5c3de82/Images/Documentation/image(272).png)
2. Click the **Create policy** button. ![](https://cdn.document360.io/d575ad81-c0ed-4980-bbd1-d59ac5c3de82/Images/Documentation/image(273).png)
3. Specify the following:

![](https://cdn.document360.io/d575ad81-c0ed-4980-bbd1-d59ac5c3de82/Images/Documentation/image(274).png)
  - A unique **Policy code**; we'll call our policy `allow-read-non-sensitive-person-properties`.
  - A friendly **Description** for the policy.
  - Whether this policy will **Allow** or **Deny** access to the specified features and data.
4. In the **Features** tab, select the checkbox of each feature (corresponding to API endpoints) to add to the policy: ![](https://cdn.document360.io/d575ad81-c0ed-4980-bbd1-d59ac5c3de82/Images/Documentation/image(275).png)

Selecting which API endpoints is subjective, but to grant read-only access to person entities, you might choose: `GetPerson`, `ListPersons`, `GetPersonPropertyTimeSeries`.

API endpoints are identified by their operation ID. Examine the [API reference](https://www.lusid.com/docs/api/lusid/endpoints/persons/GetPerson) for more information on each endpoint; the operation ID is printed under the URL: ![](https://cdn.document360.io/d575ad81-c0ed-4980-bbd1-d59ac5c3de82/Images/Documentation/image(276).png)
5. Select **Next** to reach the **Data Resources** tab. For our required data policies, specify the following:
  - To grant access to read all person entity data, locate **Person** from the list and add the **Read** action. Choose **Identifier** as **Selector** and specify `*` to apply the policy to every person scope and code. ![](https://cdn.document360.io/d575ad81-c0ed-4980-bbd1-d59ac5c3de82/Images/Documentation/image(277).png)
  - To grant access to the `Person/Traders/*` and `Person/Details/*` property definitions and values, locate both **Property Definition** and **Property Value** from the list and add the appropriate **Actions**. For both resources, we need to supply two selectors:
    - Choose **Identifier** as **Selector** and specify `Person` as **Domain**, `Traders` as **Scope**, and `*` as **Code**.
    - Choose **Identifier** as **Selector** and specify `Person` as **Domain**, `Details` as **Scope**, and `*` as **Code**. ![](https://cdn.document360.io/d575ad81-c0ed-4980-bbd1-d59ac5c3de82/Images/Documentation/image(278).png)
6. Select **Next** and **Done** to review the policy, and **Save** to create your new policy. ![](https://cdn.document360.io/d575ad81-c0ed-4980-bbd1-d59ac5c3de82/Images/Documentation/image(280).png)

### Creating a policy for a privileged user

We need to create a separate data policy that allows a user to read property definitions and values under the scope `Person/SensitiveDetails/*`.

To create this policy, we can follow the previous steps to create a policy via the LUSID web app, this time selecting only **Data Resources**, choosing **Identifier** as **Selector**, and specifying `Person` as **Domain**, `SensitiveDetails` as **Scope**, and `*` as **Code**.

![](https://cdn.document360.io/d575ad81-c0ed-4980-bbd1-d59ac5c3de82/Images/Documentation/image(281).png)

## Step 2: Creating roles for unprivileged and privileged users

Now that we have created the required policies, we need to assign the policies to different roles which we can then assign to each user. For our tutorial, we need the following roles:

- `unprivileged-user` assigned the policy `allow-read-non-sensitive-person-properties`.
- `privileged-user` assigned the policies `allow-read-non-sensitive-person-properties` and `allow-read-sensitive-details-person-properties`.

The simplest way to create a role is via the LUSID web app; to do this:

1. Sign into the [LUSID web app](https://www.lusid.com/app/home) and use the left-hand menu to navigate to **Identity and Access > Roles**: ![](https://cdn.document360.io/d575ad81-c0ed-4980-bbd1-d59ac5c3de82/Images/Documentation/image(282).png)
2. Let's first create our unprivileged role:
  1. Select **Create role** and specify the following:
    - A **Code** to uniquely identify the role; we can use `unprivileged-user` for our first role.
    - A friendly **Description** for the role.
    - An **Activation date** and, optionally, **Deactivation date** for the role. For our tutorial, we want the role to be active immediately, so we can specify the current date.
  2. Use the **Policies > Choose** dropdown to assign policies to the role. For our unprivileged role, we need to add the `allow-read-non-sensitive-person-properties` policy. ![](https://cdn.document360.io/d575ad81-c0ed-4980-bbd1-d59ac5c3de82/Images/Documentation/image(283).png)
  3. Select **Create** to create the role.
3. Now let's create our more privileged role:
  1. Select **Create role** and specify the following:
    - A **Code** to uniquely identify the role; we can use `privileged-user` for this role.
    - A friendly **Description** for the role.
    - An **Activation date** and, optionally, **Deactivation date** for the role. For our tutorial, we want the role to be active immediately, so we can specify the current date.
  2. Use the **Policies > Choose** dropdown to assign policies to the role. For our privileged role, we need to add both the `allow-read-non-sensitive-person-properties` and `allow-read-sensitive-details-person-properties` policies. ![](https://cdn.document360.io/d575ad81-c0ed-4980-bbd1-d59ac5c3de82/Images/Documentation/image(284).png)
  3. Select **Create** to create the role.

> **Note:** Depending on other existing roles and policies in your domain, you may need to set a precedence for each role to determine which policies take effect in the event of a conflict. [Read more on setting precedence for a role here.](/v1/docs/how-do-i-create-a-role#setting-precedence-for-a-role)

## Step 3: Assigning roles to users and seeing the access controls in action

Once we have created the roles, we can assign them to our desired users via the LUSID web app to enforce who can view properties containing sensitive data. To do this:

1. Sign into the [LUSID web app](https://www.lusid.com/app/home) use the left-hand menu to navigate to **Identity and Access > Users**.
2. Locate a user to assign a role to and select **Edit**. ![](https://cdn.document360.io/d575ad81-c0ed-4980-bbd1-d59ac5c3de82/Images/Documentation/image(285).png)
3. Click **Add roles** and select the `unprivileged-user` role we made earlier. ![](https://cdn.document360.io/d575ad81-c0ed-4980-bbd1-d59ac5c3de82/Images/Documentation/image(286).png)
4. Click **Save** to add the role to the user.
5. To create a privileged user, repeat steps 2-4, additionally assigning the `privileged-user` role to the user. ![](https://cdn.document360.io/d575ad81-c0ed-4980-bbd1-d59ac5c3de82/Images/Documentation/image(287).png)

Once a user is assigned the `unprivileged-user` role, we can see the policy in action by having the user call the [GetPersonPropertyTimeSeries](https://www.lusid.com/docs/api#operation/GetPersonPropertyTimeSeries) API, passing in the following:

```json
curl -X GET
  "https://<your-domain>.lusid.com/api/api/persons/Traders/TraderId/Trader5/properties/time-series?propertyKey=Person%2FDetails%2FCountry" 
  -H "Authorization: Bearer <your-API-access-token>"
```

Part of the response is as follows; the unprivileged user can access the non-sensitive data:

```json
{
  "values": [
    {
      "value": {
        "labelValue": "USA"
      },
      ...
```

However, if the unprivileged user attempts to view properties under the `Person/SensitiveDetails/*` scope by passing in the following:

```json
curl -X GET
  "https://<your-domain>.lusid.com/api/api/persons/Traders/TraderId/Trader5/properties/time-series?propertyKey=Person%2FSensitiveDetails%2FAddress" 
  -H "Authorization: Bearer <your-API-access-token>"
```

The request fails with part of the response as follows:

```json
{
  "name": "AccessDenied",
  "title": "Access cannot be granted",
  "status": 403,
  "detail": "Access to perform the requested action cannot be granted due to insufficient privileges. Please contact your organisation's administrator.",
  "instance": "https://<your-domain>.lusid.com/app/insights/logs/0HN1UO1IR6S4U:00000131",
  ...
}
```

We can follow the `instance` URL and look under the **Access logs** tab to understand which policy is required to gain access to such data:

![](https://cdn.document360.io/d575ad81-c0ed-4980-bbd1-d59ac5c3de82/Images/Documentation/7759f4de-6de3-499d-915d-67ce581bac10.png)

If our privileged user attempts the same API call, the user *can* read the sensitive data; part of the response is as follows:

```json
{
  "values": [
    {
      "value": {
        "labelValue": "123 New Lane, New York"
      },
     ...
```
