Skip to content

Commit a1bb11d

Browse files
authored
Merge pull request #720 from alexandair/alex-35002
35002 - Add test for Cross-Tenant Access Policy (XTAP) RMS settings
2 parents 3fe6862 + ddba135 commit a1bb11d

File tree

3 files changed

+565
-0
lines changed

3 files changed

+565
-0
lines changed
Lines changed: 337 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,337 @@
1+
Describe "Test-Assessment-35002" {
2+
BeforeAll {
3+
$here = $PSScriptRoot
4+
$srcRoot = Join-Path $here "../../src/powershell"
5+
<#
6+
# Import required module functions
7+
@(
8+
"private/core/Add-ZtTestResultDetail.ps1"
9+
"public/Invoke-ZtGraphRequest.ps1"
10+
"private/core/Write-ZtProgress.ps1"
11+
"private/core/Get-ZtTestStatus.ps1"
12+
"private/core/Get-SafeMarkdown.ps1"
13+
) | ForEach-Object { . (Join-Path $srcRoot $_) }
14+
#>
15+
# Mock external module dependencies
16+
if (-not (Get-Command Write-PSFMessage -ErrorAction SilentlyContinue)) {
17+
function Write-PSFMessage {}
18+
}
19+
20+
# Load the class
21+
$classPath = Join-Path $srcRoot "classes/ZtTest.ps1"
22+
if (-not ("ZtTest" -as [type])) {
23+
. $classPath
24+
}
25+
26+
# Load the SUT
27+
$sut = Join-Path $srcRoot "tests/Test-Assessment.35002.ps1"
28+
. $sut
29+
30+
# Setup output file
31+
$script:outputFile = Join-Path $here "../TestResults/Report-Test-Assessment.35002.md"
32+
$outputDir = Split-Path $script:outputFile
33+
if (-not (Test-Path $outputDir)) { New-Item -ItemType Directory -Path $outputDir | Out-Null }
34+
"# Test Results for 35002`n" | Set-Content $script:outputFile
35+
}
36+
37+
BeforeEach {
38+
Mock Write-PSFMessage {}
39+
Mock Write-ZtProgress {}
40+
Mock Get-SafeMarkdown { param($Text) return $Text }
41+
$script:defaultPolicyResponse = $null
42+
$script:partnersResponse = @()
43+
}
44+
45+
Context "When Default Policy allows RMS" {
46+
It "Should pass when Inbound and Outbound allow RMS explicitly" {
47+
$script:defaultPolicyResponse = [PSCustomObject]@{
48+
b2bCollaborationInbound = [PSCustomObject]@{
49+
applications = [PSCustomObject]@{
50+
accessType = "allowed"
51+
targets = @([PSCustomObject]@{ target = "00000012-0000-0000-c000-000000000000" })
52+
}
53+
}
54+
b2bCollaborationOutbound = [PSCustomObject]@{
55+
applications = [PSCustomObject]@{
56+
accessType = "allowed"
57+
targets = @([PSCustomObject]@{ target = "00000012-0000-0000-c000-000000000000" })
58+
}
59+
}
60+
}
61+
$script:partnersResponse = @()
62+
63+
Mock Invoke-ZtGraphRequest {
64+
if ($RelativeUri -match "default") { return $script:defaultPolicyResponse }
65+
if ($RelativeUri -match "partners") { return $script:partnersResponse }
66+
}
67+
68+
Mock Add-ZtTestResultDetail {
69+
param($TestId, $Title, $Status, $Result)
70+
"## Scenario: Default Allowed Explicitly`n`n$Result`n" | Add-Content $script:outputFile
71+
}
72+
73+
Test-Assessment-35002
74+
75+
Should -Invoke Add-ZtTestResultDetail -ParameterFilter {
76+
$Status -eq $true -and $Result -match "RMS application is allowed"
77+
}
78+
}
79+
80+
It "Should pass when Inbound and Outbound allow All Apps" {
81+
$script:defaultPolicyResponse = [PSCustomObject]@{
82+
b2bCollaborationInbound = [PSCustomObject]@{
83+
applications = [PSCustomObject]@{
84+
accessType = "allowed"
85+
targets = @([PSCustomObject]@{ target = "AllApplications" })
86+
}
87+
}
88+
b2bCollaborationOutbound = [PSCustomObject]@{
89+
applications = [PSCustomObject]@{
90+
accessType = "allowed"
91+
targets = @([PSCustomObject]@{ target = "AllApplications" })
92+
}
93+
}
94+
}
95+
$script:partnersResponse = @()
96+
97+
Mock Invoke-ZtGraphRequest {
98+
if ($RelativeUri -match "default") { return $script:defaultPolicyResponse }
99+
if ($RelativeUri -match "partners") { return $script:partnersResponse }
100+
}
101+
102+
Mock Add-ZtTestResultDetail {
103+
param($TestId, $Title, $Status, $Result)
104+
"## Scenario: Default Allowed All Apps`n`n$Result`n" | Add-Content $script:outputFile
105+
}
106+
107+
Test-Assessment-35002
108+
109+
Should -Invoke Add-ZtTestResultDetail -ParameterFilter {
110+
$Status -eq $true
111+
}
112+
}
113+
114+
It "Should pass when Inbound and Outbound Block specific apps but NOT RMS (Implicit Allow)" {
115+
$script:defaultPolicyResponse = [PSCustomObject]@{
116+
b2bCollaborationInbound = [PSCustomObject]@{
117+
applications = [PSCustomObject]@{
118+
accessType = "blocked"
119+
targets = @([PSCustomObject]@{ target = "some-other-app-id" })
120+
}
121+
}
122+
b2bCollaborationOutbound = [PSCustomObject]@{
123+
applications = [PSCustomObject]@{
124+
accessType = "blocked"
125+
targets = @([PSCustomObject]@{ target = "some-other-app-id" })
126+
}
127+
}
128+
}
129+
$script:partnersResponse = @()
130+
131+
Mock Invoke-ZtGraphRequest {
132+
if ($RelativeUri -match "default") { return $script:defaultPolicyResponse }
133+
if ($RelativeUri -match "partners") { return $script:partnersResponse }
134+
}
135+
136+
Mock Add-ZtTestResultDetail {
137+
param($TestId, $Title, $Status, $Result)
138+
"## Scenario: Default Implicit Allow`n`n$Result`n" | Add-Content $script:outputFile
139+
}
140+
141+
Test-Assessment-35002
142+
143+
Should -Invoke Add-ZtTestResultDetail -ParameterFilter {
144+
$Status -eq $true
145+
}
146+
}
147+
}
148+
149+
Context "When Default Policy blocks RMS" {
150+
It "Should fail when Inbound explicitly blocks RMS" {
151+
$script:defaultPolicyResponse = [PSCustomObject]@{
152+
b2bCollaborationInbound = [PSCustomObject]@{
153+
applications = [PSCustomObject]@{
154+
accessType = "blocked"
155+
targets = @([PSCustomObject]@{ target = "00000012-0000-0000-c000-000000000000" })
156+
}
157+
}
158+
b2bCollaborationOutbound = [PSCustomObject]@{
159+
applications = [PSCustomObject]@{
160+
accessType = "allowed"
161+
targets = @([PSCustomObject]@{ target = "AllApplications" })
162+
}
163+
}
164+
}
165+
$script:partnersResponse = @()
166+
167+
Mock Invoke-ZtGraphRequest {
168+
if ($RelativeUri -match "default") { return $script:defaultPolicyResponse }
169+
if ($RelativeUri -match "partners") { return $script:partnersResponse }
170+
}
171+
172+
$script:capturedResult = $null
173+
Mock Add-ZtTestResultDetail {
174+
param($TestId, $Title, $Status, $Result)
175+
$script:capturedResult = $Result
176+
"## Scenario: Default Inbound Blocked Explicitly`n`n$Result`n" | Add-Content $script:outputFile
177+
}
178+
179+
Test-Assessment-35002
180+
181+
Should -Invoke Add-ZtTestResultDetail -ParameterFilter {
182+
$Status -eq $false
183+
}
184+
$script:capturedResult | Should -Match "Blocked \(Explicit\)"
185+
}
186+
187+
It "Should fail when Inbound allows specific apps but NOT RMS (Implicit Block)" {
188+
$script:defaultPolicyResponse = [PSCustomObject]@{
189+
b2bCollaborationInbound = [PSCustomObject]@{
190+
applications = [PSCustomObject]@{
191+
accessType = "allowed"
192+
targets = @([PSCustomObject]@{ target = "some-other-app-id" })
193+
}
194+
}
195+
b2bCollaborationOutbound = [PSCustomObject]@{
196+
applications = [PSCustomObject]@{
197+
accessType = "allowed"
198+
targets = @([PSCustomObject]@{ target = "AllApplications" })
199+
}
200+
}
201+
}
202+
$script:partnersResponse = @()
203+
204+
Mock Invoke-ZtGraphRequest {
205+
if ($RelativeUri -match "default") { return $script:defaultPolicyResponse }
206+
if ($RelativeUri -match "partners") { return $script:partnersResponse }
207+
}
208+
209+
$script:capturedResult = $null
210+
Mock Add-ZtTestResultDetail {
211+
param($TestId, $Title, $Status, $Result)
212+
$script:capturedResult = $Result
213+
"## Scenario: Default Inbound Blocked Implicitly`n`n$Result`n" | Add-Content $script:outputFile
214+
}
215+
216+
Test-Assessment-35002
217+
218+
Should -Invoke Add-ZtTestResultDetail -ParameterFilter {
219+
$Status -eq $false
220+
}
221+
$script:capturedResult | Should -Match "Blocked \(Implicit\)"
222+
}
223+
}
224+
225+
Context "When Partner Policies exist" {
226+
It "Should fail if a Partner Policy blocks RMS" {
227+
$script:defaultPolicyResponse = [PSCustomObject]@{
228+
b2bCollaborationInbound = [PSCustomObject]@{
229+
applications = [PSCustomObject]@{
230+
accessType = "allowed"
231+
targets = @([PSCustomObject]@{ target = "AllApplications" })
232+
}
233+
}
234+
b2bCollaborationOutbound = [PSCustomObject]@{
235+
applications = [PSCustomObject]@{
236+
accessType = "allowed"
237+
targets = @([PSCustomObject]@{ target = "AllApplications" })
238+
}
239+
}
240+
}
241+
$script:partnersResponse = @(
242+
[PSCustomObject]@{
243+
tenantId = "partner-tenant-id"
244+
b2bCollaborationInbound = [PSCustomObject]@{
245+
applications = [PSCustomObject]@{
246+
accessType = "blocked"
247+
targets = @([PSCustomObject]@{ target = "00000012-0000-0000-c000-000000000000" })
248+
}
249+
}
250+
}
251+
)
252+
253+
Mock Invoke-ZtGraphRequest {
254+
if ($RelativeUri -match "default") { return $script:defaultPolicyResponse }
255+
if ($RelativeUri -match "partners") { return $script:partnersResponse }
256+
}
257+
258+
$script:capturedResult = $null
259+
Mock Add-ZtTestResultDetail {
260+
param($TestId, $Title, $Status, $Result)
261+
$script:capturedResult = $Result
262+
"## Scenario: Partner Blocked`n`n$Result`n" | Add-Content $script:outputFile
263+
}
264+
265+
Test-Assessment-35002
266+
267+
Should -Invoke Add-ZtTestResultDetail -ParameterFilter {
268+
$Status -eq $false
269+
}
270+
$script:capturedResult | Should -Match "Partner \(partner-tenant-id\)"
271+
$script:capturedResult | Should -Match "Blocked \(Explicit\)"
272+
}
273+
274+
It "Should ignore inherited partner settings" {
275+
$script:defaultPolicyResponse = [PSCustomObject]@{
276+
b2bCollaborationInbound = [PSCustomObject]@{
277+
applications = [PSCustomObject]@{
278+
accessType = "allowed"
279+
targets = @([PSCustomObject]@{ target = "AllApplications" })
280+
}
281+
}
282+
b2bCollaborationOutbound = [PSCustomObject]@{
283+
applications = [PSCustomObject]@{
284+
accessType = "allowed"
285+
targets = @([PSCustomObject]@{ target = "AllApplications" })
286+
}
287+
}
288+
}
289+
# Partner with null/empty settings implies inheritance
290+
$script:partnersResponse = @(
291+
[PSCustomObject]@{
292+
tenantId = "partner-tenant-id"
293+
b2bCollaborationInbound = $null
294+
b2bCollaborationOutbound = [PSCustomObject]@{
295+
applications = $null
296+
}
297+
}
298+
)
299+
300+
Mock Invoke-ZtGraphRequest {
301+
if ($RelativeUri -match "default") { return $script:defaultPolicyResponse }
302+
if ($RelativeUri -match "partners") { return $script:partnersResponse }
303+
}
304+
305+
Mock Add-ZtTestResultDetail {
306+
param($TestId, $Title, $Status, $Result)
307+
"## Scenario: Partner Inherited`n`n$Result`n" | Add-Content $script:outputFile
308+
}
309+
310+
Test-Assessment-35002
311+
312+
Should -Invoke Add-ZtTestResultDetail -ParameterFilter {
313+
$Status -eq $true
314+
}
315+
}
316+
}
317+
318+
Context "Error Handling" {
319+
It "Should handle Graph API errors" {
320+
Mock Invoke-ZtGraphRequest { throw "Graph API Error" }
321+
322+
$script:capturedResult = $null
323+
Mock Add-ZtTestResultDetail {
324+
param($TestId, $Title, $Status, $Result)
325+
$script:capturedResult = $Result
326+
"## Scenario: Error Handling`n`n$Result`n" | Add-Content $script:outputFile
327+
}
328+
329+
Test-Assessment-35002
330+
331+
Should -Invoke Add-ZtTestResultDetail -ParameterFilter {
332+
$Status -eq $false
333+
}
334+
$script:capturedResult | Should -Match "Cross-tenant access policy settings cannot be determined"
335+
}
336+
}
337+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
Cross-tenant access policies (XTAP) in Microsoft Entra ID control how users in your organization collaborate with external organizations. When users share encrypted content across organizational boundaries or receive encrypted documents from external partners, the Microsoft Rights Management Service (RMS) must authenticate users from both organizations to enforce encryption permissions. If cross-tenant access settings block or restrict the RMS application (App ID: `00000012-0000-0000-c000-000000000000`), users will encounter "Access is blocked by your organization" or "Access is blocked by the organization" error messages when attempting to open encrypted emails or documents from external organizations. This prevents legitimate cross-organizational collaboration on protected content. Organizations should configure both inbound and outbound cross-tenant access settings to explicitly allow the RMS application, ensuring that external users can open encrypted content shared by your organization (inbound) and your users can open encrypted content received from external partners (outbound). Without proper XTAP configuration, encrypted content sharing fails even when users have appropriate permissions assigned through sensitivity label encryption settings.
2+
3+
**Remediation action**
4+
5+
To configure cross-tenant access settings to allow RMS:
6+
1. Navigate to [Microsoft Entra admin center > External Identities > Cross-tenant access settings](https://entra.microsoft.com/#view/Microsoft_AAD_IAM/CompanyRelationshipsMenuBlade/~/CrossTenantAccessSettings)
7+
2. Select "Default settings" or a specific organizational setting
8+
3. Under "Inbound access", select "B2B collaboration"
9+
4. Select "Applications" tab
10+
5. Choose "Allow access" and add "Microsoft Rights Management Services" (App ID: `00000012-0000-0000-c000-000000000000`)
11+
6. Repeat for "Outbound access" settings
12+
7. Save changes
13+
14+
- [Cross-tenant access settings and encrypted content](https://learn.microsoft.com/purview/encryption-azure-ad-configuration#cross-tenant-access-settings-and-encrypted-content)
15+
- [Configure cross-tenant access settings for B2B collaboration](https://learn.microsoft.com/entra/external-id/cross-tenant-access-settings-b2b-collaboration)
16+
17+
<!--- Results --->
18+
%TestResult%

0 commit comments

Comments
 (0)