Skip to content

Commit aeebcdf

Browse files
authored
Merge pull request #231947 from HeidiSteen/heidist-refresh
[azure search] Security trimming doc updates
2 parents 71c2f37 + ea55399 commit aeebcdf

File tree

3 files changed

+78
-43
lines changed

3 files changed

+78
-43
lines changed

articles/search/TOC.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@
238238
items:
239239
- name: Security filters
240240
href: search-security-trimming-for-azure-search.md
241-
- name: Filter on user identities
241+
- name: Security filters with Azure AD
242242
href: search-security-trimming-for-azure-search-with-aad.md
243243
- name: Development
244244
items:

articles/search/search-security-trimming-for-azure-search-with-aad.md

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,16 @@ manager: nitinme
77
author: HeidiSteen
88
ms.author: heidist
99
ms.service: cognitive-search
10-
ms.topic: conceptual
11-
ms.date: 01/30/2023
10+
ms.topic: how-to
11+
ms.date: 03/24/2023
1212
ms.custom: devx-track-csharp
1313
---
1414
# Security filters for trimming Azure Cognitive Search results using Active Directory identities
1515

1616
This article demonstrates how to use Azure Active Directory (AD) security identities together with filters in Azure Cognitive Search to trim search results based on user group membership.
1717

1818
This article covers the following tasks:
19+
1920
> [!div class="checklist"]
2021
> - Create Azure AD groups and users
2122
> - Associate the user with the group you have created
@@ -30,23 +31,23 @@ This article covers the following tasks:
3031

3132
Your index in Azure Cognitive Search must have a [security field](search-security-trimming-for-azure-search.md) to store the list of group identities having read access to the document. This use case assumes a one-to-one correspondence between a securable item (such as an individual's college application) and a security field specifying who has access to that item (admissions personnel).
3233

33-
You must have Azure AD administrator permissions (Owner or administrator), required in this walkthrough for creating users, groups, and associations.
34+
You must have Azure AD administrator permissions (Owner or administrator) to create users, groups, and associations.
3435

3536
Your application must also be registered with Azure AD as a multi-tenant app, as described in the following procedure.
3637

3738
### Register your application with Azure Active Directory
3839

3940
This step integrates your application with Azure AD for the purpose of accepting sign-ins of user and group accounts. If you aren't a tenant admin in your organization, you might need to [create a new tenant](../active-directory/develop/quickstart-create-new-tenant.md) to perform the following steps.
4041

41-
1. In [Azure portal](https://portal.azure.com), find the Azure Active Directory resource for your subscription.
42+
1. In [Azure portal](https://portal.azure.com), find the Azure Active Directory tenant.
4243

4344
1. On the left, under **Manage**, select **App registrations**, and then select **New registration**.
4445

45-
1. Give the registration a name, perhaps a name that is similar to the search application name. Select **Register**.
46+
1. Give the registration a name, perhaps a name that's similar to the search application name. Select **Register**.
4647

47-
1. Once the app registration is created, copy the Application ID. You'll need to provide this string to your application.
48+
1. Once the app registration is created, copy the Application (client) ID. You'll need to provide this string to your application.
4849

49-
If you're stepping through the [DotNetHowToSecurityTrimming](https://github.com/Azure-Samples/search-dotnet-getting-started/tree/master/DotNetHowToEncryptionUsingCMK), paste this value into the **app.config** file.
50+
If you're stepping through the [DotNetHowToSecurityTrimming](https://github.com/Azure-Samples/search-dotnet-getting-started/tree/master/DotNetHowToSecurityTrimming), paste this value into the **app.config** file.
5051

5152
Repeat for the Tenant ID.
5253

@@ -62,7 +63,9 @@ This step integrates your application with Azure AD for the purpose of accepting
6263
- **Group.ReadWrite.All**
6364
- **User.ReadWrite.All**
6465

65-
Microsoft Graph provides an API that allows programmatic access to Azure AD through a REST API. The code sample for this walkthrough uses the permissions to call the Microsoft Graph API for creating groups, users, and associations. The APIs are also used to cache group identifiers for faster filtering.
66+
Microsoft Graph provides an API that allows programmatic access to Azure AD through a REST API. The code sample for this walkthrough uses the permissions to call the Microsoft Graph API for creating groups, users, and associations. The APIs are also used to cache group identifiers for faster filtering.
67+
68+
1. Select **Grant admin consent for tenant** to complete the consent process.
6669

6770
## Create users and groups
6871

@@ -143,7 +146,7 @@ IndexDocumentsResult result = searchClient.IndexDocuments(batch);
143146

144147
## Issue a search request
145148

146-
For security trimming purposes, the values in your security field in the index are static values used for including or excluding documents in search results. For example, if the group identifier for Admissions is "A11B22C33D44-E55F66G77-H88I99JKK", any documents in an Azure Cognitive Search index having that identifier in the security filed are included (or excluded) in the search results sent back to the requestor.
149+
For security trimming purposes, the values in your security field in the index are static values used for including or excluding documents in search results. For example, if the group identifier for Admissions is "A11B22C33D44-E55F66G77-H88I99JKK", any documents in an Azure Cognitive Search index having that identifier in the security field are included (or excluded) in the search results sent back to the caller.
147150

148151
To filter documents returned in search results based on groups of the user issuing the request, review the following steps.
149152

articles/search/search-security-trimming-for-azure-search.md

Lines changed: 65 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -7,61 +7,89 @@ manager: nitinme
77
author: HeidiSteen
88
ms.author: heidist
99
ms.service: cognitive-search
10-
ms.topic: conceptual
11-
ms.date: 01/30/2023
10+
ms.topic: how-to
11+
ms.date: 03/24/2023
1212
---
1313

1414
# Security filters for trimming results in Azure Cognitive Search
1515

16-
You can apply security filters to trim search results in Azure Cognitive Search based on user identity. This search experience generally requires comparing the identity of whoever requests the search against a field containing the principals who have permissions to the document. When a match is found, the user or principal (such as a group or role) has access to that document.
16+
Cognitive Search doesn't provide document-level permissions and can't vary search results from within the same index by user permissions. As a workaround, you can create a filter that trims search results based on a string containing a group or user identity.
1717

18-
One way to achieve security filtering is through a complicated disjunction of equality expressions: for example, `Id eq 'id1' or Id eq 'id2'`, and so forth. This approach is error-prone, difficult to maintain, and in cases where the list contains hundreds or thousands of values, slows down query response time by many seconds.
18+
This article describes a pattern for security filtering that includes following steps:
1919

20-
A simpler and faster approach is through the `search.in` function. If you use `search.in(Id, 'id1, id2, ...')` instead of an equality expression, you can expect sub-second response times.
21-
22-
This article shows you how to accomplish security filtering using the following steps:
2320
> [!div class="checklist"]
24-
> * Create a field that contains the principal identifiers
25-
> * Push or update existing documents with the relevant principal identifiers
26-
> * Issue a search request with `search.in` `filter`
21+
> * Assemble source documents with the required content
22+
> * Create a field for the principal identifiers
23+
> * Push the documents to the search index for indexing
24+
> * Query the index with the `search.in` filter function
25+
26+
## About the security filter pattern
27+
28+
Although Cognitive Search doesn't integrate with security subsystems for access to content within an index, many customers who have document-level security requirements have found that filters can meet their needs.
29+
30+
In Cognitive Search, a security filter is a regular OData filter that includes or excludes a search result based on a matching value, except that in a security filter, the criteria is a string consisting of a security principal. There's no authentication or authorization through the security principal. The principal is just a string, used in a filter expression, to include or exclude a document from the search results.
2731

28-
>[!NOTE]
29-
> The process of retrieving the principal identifiers is not covered in this document. You should get it from your identity service provider.
32+
There are several ways to achieve security filtering. One way is through a complicated disjunction of equality expressions: for example, `Id eq 'id1' or Id eq 'id2'`, and so forth. This approach is error-prone, difficult to maintain, and in cases where the list contains hundreds or thousands of values, slows down query response time by many seconds.
33+
34+
A better solution is using the `search.in` function for security filters, as described in this article. If you use `search.in(Id, 'id1, id2, ...')` instead of an equality expression, you can expect subsecond response times.
3035

3136
## Prerequisites
3237

33-
This article assumes you have an [Azure subscription](https://azure.microsoft.com/pricing/free-trial/?WT.mc_id=A261C142F), an[Azure Cognitive Search service](search-create-service-portal.md), and an [index](search-what-is-an-index.md).
38+
* The field containing group or user identity must be a string with the "filterable" attribute. It should be a collection. It shouldn't allow nulls.
39+
40+
* Other fields in the same document should provide the content that's accessible to that group or user. In the following JSON documents, the "security_id" fields contain identities used in a security filter, and the name, salary, and marital status will be included if the identity of the caller matches the "security_id" of the document.
41+
42+
```json
43+
{
44+
"Employee-1": {
45+
"id": "100-1000-10-1-10000-1",
46+
"name": "Abram",
47+
"salary": 75000,
48+
"married": true,
49+
"security_id": "10011"
50+
},
51+
"Employee-2": {
52+
"id": "200-2000-20-2-20000-2",
53+
"name": "Adams",
54+
"salary": 75000,
55+
"married": true,
56+
"security_id": "20022"
57+
}
58+
}
59+
```
60+
61+
>[!NOTE]
62+
> The process of retrieving the principal identifiers and injecting those strings into source documents that can be indexed by Cognitive Search isn't covered in this article. Refer to the documentation of your identity service provider for help with obtaining identifiers.
3463

3564
## Create security field
3665

37-
Your documents must include a field specifying which groups have access. This information becomes the filter criteria against which documents are selected or rejected from the result set returned to the issuer.
38-
Let's assume that we have an index of secured files, and each file is accessible by a different set of users.
66+
In the search index, within the field collection, you need one field that contains the group or user identity, similar to the fictitious "security_id" field in the previous example.
67+
68+
1. Add a security field as a `Collection(Edm.String)`. Make sure it has a `filterable` attribute set to `true` so that search results are filtered based on the access the user has. For example, if you set the `group_ids` field to `["group_id1, group_id2"]` for the document with `file_name` "secured_file_b", only users that belong to group IDs "group_id1" or "group_id2" have read access to the file.
3969

40-
1. Add field `group_ids` (you can choose any name here) as a `Collection(Edm.String)`. Make sure the field has a `filterable` attribute set to `true` so that search results are filtered based on the access the user has. For example, if you set the `group_ids` field to `["group_id1, group_id2"]` for the document with `file_name` "secured_file_b", only users that belong to group IDs "group_id1" or "group_id2" have read access to the file.
41-
42-
Make sure the field's `retrievable` attribute is set to `false` so that it isn't returned as part of the search request.
70+
Set the field's `retrievable` attribute to `false` so that it isn't returned as part of the search request.
4371

44-
2. Also add `file_id` and `file_name` fields for the sake of this example.
72+
1. Indexes require a document key. The "file_id" field satisfies that requirement. Indexes should also contain searchable content. The "file_name" and "file_description" fields represent that in this example.
4573

46-
```JSON
47-
{
74+
```https
75+
POST https://[search service].search.windows.net/indexes/securedfiles/docs/index?api-version=2020-06-30
76+
{
4877
"name": "securedfiles",
4978
"fields": [
50-
{"name": "file_id", "type": "Edm.String", "key": true, "searchable": false, "sortable": false, "facetable": false},
51-
{"name": "file_name", "type": "Edm.String"},
52-
{"name": "group_ids", "type": "Collection(Edm.String)", "filterable": true, "retrievable": false}
79+
{"name": "file_id", "type": "Edm.String", "key": true, "searchable": false },
80+
{"name": "file_name", "type": "Edm.String", "searchable": true },
81+
{"name": "file_description", "type": "Edm.String", "searchable": true },
82+
{"name": "group_ids", "type": "Collection(Edm.String)", "filterable": true, "retrievable": false }
5383
]
5484
}
55-
```
85+
```
5686

57-
## Pushing data into your index using the REST API
87+
## Push data into your index using the REST API
5888

59-
Issue an HTTP POST request to your index's URL endpoint. The body of the HTTP request is a JSON object containing the documents to be added:
89+
Issue an HTTP POST request to your index's URL endpoint. The body of the HTTP request is a JSON object containing the documents to be indexed:
6090

6191
```http
6292
POST https://[search service].search.windows.net/indexes/securedfiles/docs/index?api-version=2020-06-30
63-
Content-Type: application/json
64-
api-key: [admin key]
6593
```
6694

6795
In the request body, specify the content of your documents:
@@ -73,18 +101,21 @@ In the request body, specify the content of your documents:
73101
"@search.action": "upload",
74102
"file_id": "1",
75103
"file_name": "secured_file_a",
104+
"file_description": "File access is restricted to the Human Resources.",
76105
"group_ids": ["group_id1"]
77106
},
78107
{
79108
"@search.action": "upload",
80109
"file_id": "2",
81110
"file_name": "secured_file_b",
111+
"file_description": "File access is restricted to Human Resources and Recruiting.",
82112
"group_ids": ["group_id1", "group_id2"]
83113
},
84114
{
85115
"@search.action": "upload",
86116
"file_id": "3",
87117
"file_name": "secured_file_c",
118+
"file_description": "File access is restricted to Operations and Logistics.",
88119
"group_ids": ["group_id5", "group_id6"]
89120
}
90121
]
@@ -105,15 +136,16 @@ If you need to update an existing document with the list of groups, you can use
105136
}
106137
```
107138

108-
For full details on adding or updating documents, you can read [Edit documents](/rest/api/searchservice/addupdate-or-delete-documents).
139+
For more information on uploading documents, see [Add, Update, or Delete Documents (REST)](/rest/api/searchservice/addupdate-or-delete-documents).
109140

110-
## Apply the security filter
141+
## Apply the security filter in the query
111142

112143
In order to trim documents based on `group_ids` access, you should issue a search query with a `group_ids/any(g:search.in(g, 'group_id1, group_id2,...'))` filter, where 'group_id1, group_id2,...' are the groups to which the search request issuer belongs.
113144

114145
This filter matches all documents for which the `group_ids` field contains one of the given identifiers.
115146
For full details on searching documents using Azure Cognitive Search, you can read [Search Documents](/rest/api/searchservice/search-documents).
116-
Note that this sample shows how to search documents using a POST request.
147+
148+
This sample shows how to set up query using a POST request.
117149

118150
Issue the HTTP POST request:
119151

@@ -154,7 +186,7 @@ You should get the documents back where `group_ids` contains either "group_id1"
154186

155187
This article described a pattern for filtering results based on user identity and the `search.in()` function. You can use this function to pass in principal identifiers for the requesting user to match against principal identifiers associated with each target document. When a search request is handled, the `search.in` function filters out search results for which none of the user's principals have read access. The principal identifiers can represent things like security groups, roles, or even the user's own identity.
156188

157-
For an alternative pattern based on Active Directory, or to revisit other security features, see the following links.
189+
For an alternative pattern based on Azure Active Directory, or to revisit other security features, see the following links.
158190

159191
* [Security filters for trimming results using Active Directory identities](search-security-trimming-for-azure-search-with-aad.md)
160192
* [Security in Azure Cognitive Search](search-security-overview.md)

0 commit comments

Comments
 (0)