Skip to content

Commit 0dcb509

Browse files
authored
Merge pull request #421 from KelvinTegelaar/dev
[pull] dev from KelvinTegelaar:dev
2 parents 69f7380 + c7d837c commit 0dcb509

File tree

12 files changed

+1748
-199
lines changed

12 files changed

+1748
-199
lines changed

Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Administration/Invoke-ExecModifyCalPerms.ps1

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
using namespace System.Net
22

3-
Function Invoke-ExecModifyCalPerms {
3+
function Invoke-ExecModifyCalPerms {
44
<#
55
.FUNCTIONALITY
66
Entrypoint
@@ -64,6 +64,7 @@ Function Invoke-ExecModifyCalPerms {
6464
$Modification = $Permission.Modification
6565
$CanViewPrivateItems = $Permission.CanViewPrivateItems ?? $false
6666
$FolderName = $Permission.FolderName ?? 'Calendar'
67+
$SendNotificationToUser = $Permission.SendNotificationToUser ?? $false
6768

6869
Write-LogMessage -headers $Headers -API $APIName -message "Permission Level: $PermissionLevel, Modification: $Modification, CanViewPrivateItems: $CanViewPrivateItems, FolderName: $FolderName" -Sev 'Debug'
6970

@@ -76,16 +77,17 @@ Function Invoke-ExecModifyCalPerms {
7677
try {
7778
Write-LogMessage -headers $Headers -API $APIName -message "Processing target user: $TargetUser" -Sev 'Debug'
7879
$Params = @{
79-
APIName = $APIName
80-
Headers = $Headers
81-
RemoveAccess = if ($Modification -eq 'Remove') { $TargetUser } else { $null }
82-
TenantFilter = $TenantFilter
83-
UserID = $UserId
84-
folderName = $FolderName
85-
UserToGetPermissions = $TargetUser
86-
LoggingName = $TargetUser
87-
Permissions = $PermissionLevel
88-
CanViewPrivateItems = $CanViewPrivateItems
80+
APIName = $APIName
81+
Headers = $Headers
82+
RemoveAccess = if ($Modification -eq 'Remove') { $TargetUser } else { $null }
83+
TenantFilter = $TenantFilter
84+
UserID = $UserId
85+
folderName = $FolderName
86+
UserToGetPermissions = $TargetUser
87+
LoggingName = $TargetUser
88+
Permissions = $PermissionLevel
89+
CanViewPrivateItems = $CanViewPrivateItems
90+
SendNotificationToUser = $SendNotificationToUser
8991
}
9092

9193
# Write-Host "Request params: $($Params | ConvertTo-Json)"

Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Administration/Invoke-ListMailboxes.ps1

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ Function Invoke-ListMailboxes {
1717
# Interact with query parameters or the body of the request.
1818
$TenantFilter = $Request.Query.tenantFilter
1919
try {
20-
$Select = 'id,ExchangeGuid,ArchiveGuid,UserPrincipalName,DisplayName,PrimarySMTPAddress,RecipientType,RecipientTypeDetails,EmailAddresses,WhenSoftDeleted,IsInactiveMailbox,ForwardingSmtpAddress,DeliverToMailboxAndForward,ForwardingAddress,HiddenFromAddressListsEnabled,ExternalDirectoryObjectId,MessageCopyForSendOnBehalfEnabled,MessageCopyForSentAsEnabled,PersistedCapabilities,LitigationHoldEnabled,LitigationHoldDate,LitigationHoldDuration,ComplianceTagHoldApplied,RetentionHoldEnabled,InPlaceHolds'
20+
$Select = 'id,ExchangeGuid,ArchiveGuid,UserPrincipalName,DisplayName,PrimarySMTPAddress,RecipientType,RecipientTypeDetails,EmailAddresses,WhenSoftDeleted,IsInactiveMailbox,ForwardingSmtpAddress,DeliverToMailboxAndForward,ForwardingAddress,HiddenFromAddressListsEnabled,ExternalDirectoryObjectId,MessageCopyForSendOnBehalfEnabled,MessageCopyForSentAsEnabled,PersistedCapabilities,LitigationHoldEnabled,LitigationHoldDate,LitigationHoldDuration,ComplianceTagHoldApplied,RetentionHoldEnabled,InPlaceHolds,RetentionPolicy'
2121
$ExoRequest = @{
2222
tenantid = $TenantFilter
2323
cmdlet = 'Get-Mailbox'
@@ -76,7 +76,8 @@ Function Invoke-ListMailboxes {
7676
@{ Name = 'LicensedForLitigationHold'; Expression = { ($_.PersistedCapabilities -contains 'BPOS_S_DlpAddOn' -or $_.PersistedCapabilities -contains 'BPOS_S_Enterprise') } },
7777
ComplianceTagHoldApplied,
7878
RetentionHoldEnabled,
79-
InPlaceHolds
79+
InPlaceHolds,
80+
RetentionPolicy
8081
# This select also exists in ListUserMailboxDetails and should be updated if this is changed here
8182

8283

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
using namespace System.Net
2+
3+
Function Invoke-ExecManageRetentionPolicies {
4+
<#
5+
.FUNCTIONALITY
6+
Entrypoint
7+
.ROLE
8+
Exchange.RetentionPolicies.ReadWrite
9+
#>
10+
[CmdletBinding()]
11+
param($Request, $TriggerMetadata)
12+
13+
$APIName = $Request.Params.CIPPEndpoint
14+
$Headers = $Request.Headers
15+
Write-LogMessage -headers $Headers -API $APINAME -message 'Accessed this API' -Sev 'Debug'
16+
17+
$Results = [System.Collections.Generic.List[string]]::new()
18+
$TenantFilter = $Request.Query.tenantFilter ?? $Request.body.tenantFilter
19+
$CmdletArray = [System.Collections.ArrayList]::new()
20+
$CmdletMetadataArray = [System.Collections.ArrayList]::new()
21+
$GuidToMetadataMap = @{}
22+
23+
if ([string]::IsNullOrEmpty($TenantFilter)) {
24+
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
25+
StatusCode = [HttpStatusCode]::BadRequest
26+
Body = "Tenant filter is required"
27+
})
28+
return
29+
}
30+
31+
try {
32+
# Helper function to add cmdlet to bulk array
33+
function Add-BulkCmdlet {
34+
param($CmdletName, $Parameters, $ExpectedResult, $Operation, $Identity = "")
35+
36+
$OperationGuid = [Guid]::NewGuid().ToString()
37+
38+
$CmdletObj = @{
39+
CmdletInput = @{
40+
CmdletName = $CmdletName
41+
Parameters = $Parameters
42+
}
43+
OperationGuid = $OperationGuid
44+
}
45+
46+
$CmdletMetadata = [PSCustomObject]@{
47+
ExpectedResult = $ExpectedResult
48+
Operation = $Operation
49+
Identity = $Identity
50+
OperationGuid = $OperationGuid
51+
}
52+
53+
$null = $CmdletArray.Add($CmdletObj)
54+
$null = $CmdletMetadataArray.Add($CmdletMetadata)
55+
$GuidToMetadataMap[$OperationGuid] = $CmdletMetadata
56+
}
57+
58+
# Create Retention Policies
59+
$CreatePolicies = $Request.body.CreatePolicies
60+
if ($CreatePolicies) {
61+
foreach ($Policy in $CreatePolicies) {
62+
if ([string]::IsNullOrEmpty($Policy.Name)) {
63+
$Results.Add("Failed to create policy - Name is required")
64+
continue
65+
}
66+
67+
$cmdParams = @{
68+
Name = $Policy.Name
69+
}
70+
71+
if ($Policy.RetentionPolicyTagLinks) {
72+
$cmdParams.RetentionPolicyTagLinks = $Policy.RetentionPolicyTagLinks
73+
}
74+
75+
Add-BulkCmdlet -CmdletName 'New-RetentionPolicy' -Parameters $cmdParams -ExpectedResult "Successfully created retention policy: $($Policy.Name)" -Operation 'Create' -Identity $Policy.Name
76+
}
77+
}
78+
79+
# Modify Retention Policies
80+
$ModifyPolicies = $Request.body.ModifyPolicies
81+
if ($ModifyPolicies) {
82+
foreach ($Policy in $ModifyPolicies) {
83+
if ([string]::IsNullOrEmpty($Policy.Identity)) {
84+
$Results.Add("Failed to modify policy - Identity is required")
85+
continue
86+
}
87+
88+
$cmdParams = @{
89+
Identity = $Policy.Identity
90+
}
91+
92+
if ($Policy.Name) {
93+
$cmdParams.Name = $Policy.Name
94+
}
95+
96+
# Handle tag modifications - need to get current policy first for add/remove operations
97+
if ($Policy.AddTags -or $Policy.RemoveTags) {
98+
try {
99+
$currentPolicy = New-ExoRequest -tenantid $TenantFilter -cmdlet 'Get-RetentionPolicy' -cmdParams @{Identity = $Policy.Identity}
100+
$currentTags = $currentPolicy.RetentionPolicyTagLinks
101+
} catch {
102+
$Results.Add("Failed to modify policy $($Policy.Identity) - Could not retrieve current policy")
103+
continue
104+
}
105+
106+
if ($Policy.AddTags) {
107+
$newTagsList = [System.Collections.ArrayList]::new()
108+
if ($currentTags) {
109+
foreach ($tag in $currentTags) { $null = $newTagsList.Add($tag) }
110+
}
111+
foreach ($tag in $Policy.AddTags) {
112+
if ($tag -notin $newTagsList) { $null = $newTagsList.Add($tag) }
113+
}
114+
$cmdParams.RetentionPolicyTagLinks = @($newTagsList)
115+
}
116+
117+
if ($Policy.RemoveTags) {
118+
$newTagsList = [System.Collections.ArrayList]::new()
119+
if ($currentTags) {
120+
foreach ($tag in $currentTags) {
121+
if ($tag -notin $Policy.RemoveTags) { $null = $newTagsList.Add($tag) }
122+
}
123+
}
124+
$cmdParams.RetentionPolicyTagLinks = @($newTagsList)
125+
}
126+
} elseif ($Policy.RetentionPolicyTagLinks) {
127+
$cmdParams.RetentionPolicyTagLinks = $Policy.RetentionPolicyTagLinks
128+
}
129+
130+
Add-BulkCmdlet -CmdletName 'Set-RetentionPolicy' -Parameters $cmdParams -ExpectedResult "Successfully modified retention policy: $($Policy.Identity)" -Operation 'Modify' -Identity $Policy.Identity
131+
}
132+
}
133+
134+
# Delete Retention Policies
135+
$DeletePolicies = $Request.body.DeletePolicies
136+
if ($DeletePolicies) {
137+
foreach ($PolicyIdentity in $DeletePolicies) {
138+
if ([string]::IsNullOrEmpty($PolicyIdentity)) {
139+
$Results.Add("Failed to delete policy - Identity is required")
140+
continue
141+
}
142+
143+
# Check if policy is assigned to mailboxes (do this before bulk processing)
144+
$assignedMailboxes = New-ExoRequest -tenantid $TenantFilter -cmdlet 'Get-Mailbox' -cmdParams @{
145+
Filter = "RetentionPolicy -eq '$PolicyIdentity'"
146+
ResultSize = 1
147+
} -ErrorAction SilentlyContinue
148+
149+
if ($assignedMailboxes) {
150+
$Results.Add("Cannot delete retention policy $PolicyIdentity - still assigned to mailboxes")
151+
continue
152+
}
153+
154+
Add-BulkCmdlet -CmdletName 'Remove-RetentionPolicy' -Parameters @{Identity = $PolicyIdentity; Confirm = $false} -ExpectedResult "Successfully deleted retention policy: $PolicyIdentity" -Operation 'Delete' -Identity $PolicyIdentity
155+
}
156+
}
157+
158+
# Execute bulk operations
159+
if ($CmdletArray.Count -gt 0) {
160+
Write-LogMessage -headers $Request.Headers -API $APINAME -message "Executing $($CmdletArray.Count) retention policy operations" -Sev 'Info' -tenant $TenantFilter
161+
162+
if ($CmdletArray.Count -gt 1) {
163+
# Use bulk processing
164+
$BulkResults = New-ExoBulkRequest -tenantid $TenantFilter -cmdletArray @($CmdletArray) -ReturnWithCommand $true
165+
166+
# Process bulk results using GUID mapping
167+
if ($BulkResults -is [hashtable] -and $BulkResults.Keys.Count -gt 0) {
168+
foreach ($cmdletName in $BulkResults.Keys) {
169+
foreach ($result in $BulkResults[$cmdletName]) {
170+
$operationGuid = $result.OperationGuid
171+
172+
if ($operationGuid -and $GuidToMetadataMap.ContainsKey($operationGuid)) {
173+
$metadata = $GuidToMetadataMap[$operationGuid]
174+
175+
if ($result.error) {
176+
$ErrorMessage = try { (Get-CippException -Exception $result.error).NormalizedError } catch { $result.error }
177+
$Message = "Failed to $($metadata.Operation.ToLower()) retention policy $($metadata.Identity): $ErrorMessage"
178+
Write-LogMessage -headers $Request.Headers -API $APINAME -message $Message -Sev 'Error' -tenant $TenantFilter
179+
$Results.Add($Message)
180+
} else {
181+
Write-LogMessage -headers $Request.Headers -API $APINAME -message $metadata.ExpectedResult -Sev 'Info' -tenant $TenantFilter
182+
$Results.Add($metadata.ExpectedResult)
183+
}
184+
}
185+
}
186+
}
187+
}
188+
} else {
189+
# Single operation
190+
$CmdletObj = $CmdletArray[0]
191+
$CmdletMetadata = $CmdletMetadataArray[0]
192+
193+
try {
194+
$null = New-ExoRequest -tenantid $TenantFilter -cmdlet $CmdletObj.CmdletInput.CmdletName -cmdParams $CmdletObj.CmdletInput.Parameters
195+
Write-LogMessage -headers $Request.Headers -API $APINAME -message $CmdletMetadata.ExpectedResult -Sev 'Info' -tenant $TenantFilter
196+
$Results.Add($CmdletMetadata.ExpectedResult)
197+
} catch {
198+
$ErrorMessage = Get-NormalizedError -Message $_.Exception.Message
199+
$Message = "Failed to $($CmdletMetadata.Operation.ToLower()) retention policy $($CmdletMetadata.Identity): $ErrorMessage"
200+
Write-LogMessage -headers $Request.Headers -API $APINAME -message $Message -Sev 'Error' -tenant $TenantFilter
201+
$Results.Add($Message)
202+
}
203+
}
204+
}
205+
206+
$StatusCode = [HttpStatusCode]::OK
207+
208+
# Simple response logic
209+
if ($CreatePolicies -or $ModifyPolicies -or $DeletePolicies) {
210+
# For any operations, return the results messages
211+
$GraphRequest = @($Results)
212+
} else {
213+
# For listing, return all policies or specific policy - wrapped in try-catch
214+
try {
215+
$SpecificName = $Request.Query.name
216+
if ($SpecificName) {
217+
# Get specific policy by name
218+
$GraphRequest = New-ExoRequest -tenantid $TenantFilter -cmdlet 'Get-RetentionPolicy' -cmdParams @{Identity = $SpecificName}
219+
} else {
220+
# Get all policies
221+
$GraphRequest = New-ExoRequest -tenantid $TenantFilter -cmdlet 'Get-RetentionPolicy'
222+
}
223+
} catch {
224+
$ErrorMessage = Get-NormalizedError -Message $_.Exception.Message
225+
$Message = if ($Request.Query.name) {
226+
"Failed to retrieve retention policy '$($Request.Query.name)': $ErrorMessage"
227+
} else {
228+
"Failed to retrieve retention policies: $ErrorMessage"
229+
}
230+
Write-LogMessage -headers $Request.Headers -API $APINAME -message $Message -Sev 'Error' -tenant $TenantFilter
231+
$Results.Add($Message)
232+
$StatusCode = [HttpStatusCode]::InternalServerError
233+
$GraphRequest = @($Results)
234+
}
235+
}
236+
237+
# If no results are found, we will return an empty message to prevent null reference errors in the frontend
238+
$GraphRequest = $GraphRequest ?? @()
239+
240+
} catch {
241+
$ErrorMessage = Get-NormalizedError -Message $_.Exception.Message
242+
$Message = "Failed to manage retention policies: $ErrorMessage"
243+
Write-LogMessage -headers $Request.Headers -API $APINAME -message $Message -Sev 'Error' -tenant $TenantFilter
244+
$Results.Add($Message)
245+
$StatusCode = [HttpStatusCode]::Forbidden
246+
$GraphRequest = @($Results)
247+
}
248+
249+
# If no results are found, we will return an empty message to prevent null reference errors in the frontend
250+
$GraphRequest = $GraphRequest ?? @()
251+
252+
# Associate values to output bindings by calling 'Push-OutputBinding'.
253+
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
254+
StatusCode = $StatusCode
255+
Body = $GraphRequest
256+
})
257+
}

0 commit comments

Comments
 (0)