Skip to content

Commit a4d9386

Browse files
Allow public documents when authentication is enabled (#1576)
Currently, when authentication is enforced there's no way to select "public" documents. Public documents are documents with neither oid or group ids associated with them. Add AZURE_ENABLE_GLOBAL_DOCUMENT_ACCESS env var which allows searching on these documents even if authentication is enabled. Add AZURE_ENABLE_UNAUTHENTICATED_ACCESS env var which allows searching on public documents even when a user is not logged in, and AZURE_ENFORCE_ACCESS_CONTROL is enabled.
1 parent f23a9b1 commit a4d9386

File tree

19 files changed

+549
-36
lines changed

19 files changed

+549
-36
lines changed

.azdo/pipelines/azure-dev.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ steps:
8989
AZURE_KEY_VAULT_NAME: $(AZURE_KEY_VAULT_NAME)
9090
AZURE_USE_AUTHENTICATION: $(AZURE_USE_AUTHENTICATION)
9191
AZURE_ENFORCE_ACCESS_CONTROL: $(AZURE_ENFORCE_ACCESS_CONTROL)
92+
AZURE_ENABLE_GLOBAL_DOCUMENT_ACCESS: $(AZURE_ENABLE_GLOBAL_DOCUMENT_ACCESS)
93+
AZURE_ENABLE_UNAUTHENTICATED_ACCESS: $(AZURE_ENABLE_UNAUTHENTICATED_ACCESS)
9294
AZURE_TENANT_ID: $(AZURE_TENANT_ID)
9395
AZURE_AUTH_TENANT_ID: $(AZURE_AUTH_TENANT_ID)
9496
AZURE_SERVER_APP_ID: $(AZURE_SERVER_APP_ID)

.github/workflows/azure-dev.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ jobs:
7676
AZURE_KEY_VAULT_NAME: ${{ vars.AZURE_KEY_VAULT_NAME }}
7777
AZURE_USE_AUTHENTICATION: ${{ vars.AZURE_USE_AUTHENTICATION }}
7878
AZURE_ENFORCE_ACCESS_CONTROL: ${{ vars.AZURE_ENFORCE_ACCESS_CONTROL }}
79+
AZURE_ENABLE_GLOBAL_DOCUMENT_ACCESS: ${{ vars.AZURE_ENABLE_GLOBAL_DOCUMENT_ACCESS }}
80+
AZURE_ENABLE_UNAUTHENTICATED_ACCESS: ${{ vars.AZURE_ENABLE_UNAUTHENTICATED_ACCESS }}
7981
AZURE_AUTH_TENANT_ID: ${{ vars.AZURE_AUTH_TENANT_ID }}
8082
AZURE_SERVER_APP_ID: ${{ vars.AZURE_SERVER_APP_ID }}
8183
AZURE_CLIENT_APP_ID: ${{ vars.AZURE_CLIENT_APP_ID }}

app/backend/app.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,8 @@ async def setup_clients():
328328
AZURE_TENANT_ID = os.getenv("AZURE_TENANT_ID")
329329
AZURE_USE_AUTHENTICATION = os.getenv("AZURE_USE_AUTHENTICATION", "").lower() == "true"
330330
AZURE_ENFORCE_ACCESS_CONTROL = os.getenv("AZURE_ENFORCE_ACCESS_CONTROL", "").lower() == "true"
331+
AZURE_ENABLE_GLOBAL_DOCUMENT_ACCESS = os.getenv("AZURE_ENABLE_GLOBAL_DOCUMENT_ACCESS", "").lower() == "true"
332+
AZURE_ENABLE_UNAUTHENTICATED_ACCESS = os.getenv("AZURE_ENABLE_UNAUTHENTICATED_ACCESS", "").lower() == "true"
331333
AZURE_SERVER_APP_ID = os.getenv("AZURE_SERVER_APP_ID")
332334
AZURE_SERVER_APP_SECRET = os.getenv("AZURE_SERVER_APP_SECRET")
333335
AZURE_CLIENT_APP_ID = os.getenv("AZURE_CLIENT_APP_ID")
@@ -390,6 +392,8 @@ async def setup_clients():
390392
client_app_id=AZURE_CLIENT_APP_ID,
391393
tenant_id=AZURE_AUTH_TENANT_ID,
392394
require_access_control=AZURE_ENFORCE_ACCESS_CONTROL,
395+
enable_global_documents=AZURE_ENABLE_GLOBAL_DOCUMENT_ACCESS,
396+
enable_unauthenticated_access=AZURE_ENABLE_UNAUTHENTICATED_ACCESS,
393397
)
394398

395399
if USE_USER_UPLOAD:

app/backend/core/authentication.py

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ def __init__(
4141
client_app_id: Optional[str],
4242
tenant_id: Optional[str],
4343
require_access_control: bool = False,
44+
enable_global_documents: bool = False,
45+
enable_unauthenticated_access: bool = False,
4446
):
4547
self.use_authentication = use_authentication
4648
self.server_app_id = server_app_id
@@ -62,18 +64,23 @@ def __init__(
6264
field_names = [field.name for field in search_index.fields] if search_index else []
6365
self.has_auth_fields = "oids" in field_names and "groups" in field_names
6466
self.require_access_control = require_access_control
67+
self.enable_global_documents = enable_global_documents
68+
self.enable_unauthenticated_access = enable_unauthenticated_access
6569
self.confidential_client = ConfidentialClientApplication(
6670
server_app_id, authority=self.authority, client_credential=server_app_secret, token_cache=TokenCache()
6771
)
6872
else:
6973
self.has_auth_fields = False
7074
self.require_access_control = False
75+
self.enable_global_documents = True
76+
self.enable_unauthenticated_access = True
7177

7278
def get_auth_setup_for_client(self) -> dict[str, Any]:
7379
# returns MSAL.js settings used by the client app
7480
return {
7581
"useLogin": self.use_authentication, # Whether or not login elements are enabled on the UI
76-
"requireAccessControl": self.require_access_control, # Whether or not access control is required to use the application
82+
"requireAccessControl": self.require_access_control, # Whether or not access control is required to access documents with access control lists
83+
"enableUnauthenticatedAccess": self.enable_unauthenticated_access, # Whether or not the user can access the app without login
7784
"msalConfig": {
7885
"auth": {
7986
"clientId": self.client_app_id, # Client app id used for login
@@ -150,17 +157,24 @@ def build_security_filters(self, overrides: dict[str, Any], auth_claims: dict[st
150157
else None
151158
)
152159

153-
# If only one security filter is specified, return that filter
160+
# If only one security filter is specified, use that filter
154161
# If both security filters are specified, combine them with "or" so only 1 security filter needs to pass
155162
# If no security filters are specified, don't return any filter
163+
security_filter = None
156164
if oid_security_filter and not groups_security_filter:
157-
return oid_security_filter
165+
security_filter = f"{oid_security_filter}"
158166
elif groups_security_filter and not oid_security_filter:
159-
return groups_security_filter
167+
security_filter = f"{groups_security_filter}"
160168
elif oid_security_filter and groups_security_filter:
161-
return f"({oid_security_filter} or {groups_security_filter})"
162-
else:
163-
return None
169+
security_filter = f"({oid_security_filter} or {groups_security_filter})"
170+
171+
# If global documents are allowed, append the public global filter
172+
if self.enable_global_documents:
173+
global_documents_filter = "(not oids/any() and not groups/any())"
174+
if security_filter:
175+
security_filter = f"({security_filter} or {global_documents_filter})"
176+
177+
return security_filter
164178

165179
@staticmethod
166180
async def list_groups(graph_resource_access_token: dict) -> list[str]:
@@ -230,12 +244,12 @@ async def get_auth_claims_if_enabled(self, headers: dict) -> dict[str, Any]:
230244
return auth_claims
231245
except AuthError as e:
232246
logging.exception("Exception getting authorization information - " + json.dumps(e.error))
233-
if self.require_access_control:
247+
if self.require_access_control and not self.enable_unauthenticated_access:
234248
raise
235249
return {}
236250
except Exception:
237251
logging.exception("Exception getting authorization information")
238-
if self.require_access_control:
252+
if self.require_access_control and not self.enable_unauthenticated_access:
239253
raise
240254
return {}
241255

app/frontend/src/authConfig.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ interface AuthSetup {
1717
useLogin: boolean;
1818
// Set to true if access control is enforced by the application
1919
requireAccessControl: boolean;
20+
// Set to true if the application allows unauthenticated access (only applies for documents without access control)
21+
enableUnauthenticatedAccess: boolean;
2022
/**
2123
* Configuration object to be passed to MSAL instance on creation.
2224
* For a full list of MSAL.js configuration parameters, visit:
@@ -64,6 +66,10 @@ export const useLogin = authSetup.useLogin;
6466

6567
export const requireAccessControl = authSetup.requireAccessControl;
6668

69+
export const enableUnauthenticatedAccess = authSetup.enableUnauthenticatedAccess;
70+
71+
export const requireLogin = requireAccessControl && !enableUnauthenticatedAccess;
72+
6773
/**
6874
* Configuration object to be passed to MSAL instance on creation.
6975
* For a full list of MSAL.js configuration parameters, visit:

app/frontend/src/components/QuestionInput/QuestionInput.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { useMsal } from "@azure/msal-react";
33
import { Stack, TextField } from "@fluentui/react";
44
import { Button, Tooltip, Field, Textarea } from "@fluentui/react-components";
55
import { Send28Filled } from "@fluentui/react-icons";
6-
import { isLoggedIn, requireAccessControl } from "../../authConfig";
6+
import { isLoggedIn, requireLogin } from "../../authConfig";
77

88
import styles from "./QuestionInput.module.css";
99

@@ -50,8 +50,8 @@ export const QuestionInput = ({ onSend, disabled, placeholder, clearOnSend, init
5050
};
5151

5252
const { instance } = useMsal();
53-
const disableRequiredAccessControl = requireAccessControl && !isLoggedIn(instance);
54-
const sendQuestionDisabled = disabled || !question.trim() || disableRequiredAccessControl;
53+
const disableRequiredAccessControl = requireLogin && !isLoggedIn(instance);
54+
const sendQuestionDisabled = disabled || !question.trim() || requireLogin;
5555

5656
if (disableRequiredAccessControl) {
5757
placeholder = "Please login to continue...";

azure.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ pipeline:
7070
- AZURE_KEY_VAULT_NAME
7171
- AZURE_USE_AUTHENTICATION
7272
- AZURE_ENFORCE_ACCESS_CONTROL
73+
- AZURE_ENABLE_GLOBAL_DOCUMENT_ACCESS
7374
- AZURE_AUTH_TENANT_ID
7475
- AZURE_SERVER_APP_ID
7576
- AZURE_CLIENT_APP_ID

0 commit comments

Comments
 (0)