Skip to content

Commit 9ad27a5

Browse files
committed
Merge branch 'dev' of https://github.com/KelvinTegelaar/CIPP-API into dev
2 parents 9512822 + 479bdae commit 9ad27a5

File tree

3 files changed

+188
-10
lines changed

3 files changed

+188
-10
lines changed

.github/workflows/dev_api.yml

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,67 @@ jobs:
2525
uses: actions/checkout@v4
2626
with:
2727
persist-credentials: false
28+
29+
- name: Setup PowerShell module cache
30+
id: cacher
31+
uses: actions/cache@v3
32+
with:
33+
path: "~/.local/share/powershell/Modules"
34+
key: ${{ runner.os }}-ModuleBuilder
35+
36+
- name: Install ModuleBuilder
37+
if: steps.cacher.outputs.cache-hit != 'true'
38+
shell: pwsh
39+
run: |
40+
Set-PSRepository PSGallery -InstallationPolicy Trusted
41+
Install-Module ModuleBuilder -AllowClobber -Force
42+
43+
- name: Build CIPPCore Module
44+
shell: pwsh
45+
run: |
46+
$ModulePath = Join-Path $env:GITHUB_WORKSPACE "Modules/CIPPCore"
47+
$OutputPath = Join-Path $env:GITHUB_WORKSPACE "Output"
48+
49+
Write-Host "Building module from: $ModulePath"
50+
Write-Host "Output directory: $OutputPath"
51+
52+
# Generate function permissions before replacing the source module
53+
$ToolsPath = Join-Path $env:GITHUB_WORKSPACE "Tools"
54+
$ScriptPath = Join-Path $ToolsPath "Build-FunctionPermissions.ps1"
55+
pwsh -File $ScriptPath -ModulePath $ModulePath
56+
57+
# Build the module using ModuleBuilder
58+
Build-Module -SourcePath $ModulePath -OutputDirectory $OutputPath -Verbose
59+
60+
# Replace the source module with the built module
61+
Remove-Item -Path $ModulePath -Recurse -Force
62+
Copy-Item -Path (Join-Path $OutputPath "CIPPCore") -Destination $ModulePath -Recurse -Force
63+
64+
Write-Host "Module built and replaced successfully"
65+
66+
# Clean up output directory
67+
Remove-Item -Path $OutputPath -Recurse -Force
68+
69+
- name: Build CippExtensions Module
70+
shell: pwsh
71+
run: |
72+
$ModulePath = Join-Path $env:GITHUB_WORKSPACE "Modules/CippExtensions"
73+
$OutputPath = Join-Path $env:GITHUB_WORKSPACE "Output"
74+
75+
Write-Host "Building module from: $ModulePath"
76+
Write-Host "Output directory: $OutputPath"
77+
78+
# Build the module using ModuleBuilder
79+
Build-Module -SourcePath $ModulePath -OutputDirectory $OutputPath -Verbose
80+
81+
# Replace the source module with the built module
82+
Remove-Item -Path $ModulePath -Recurse -Force
83+
Copy-Item -Path (Join-Path $OutputPath "CippExtensions") -Destination $ModulePath -Recurse -Force
84+
85+
Write-Host "Module built and replaced successfully"
86+
87+
# Clean up output directory
88+
Remove-Item -Path $OutputPath -Recurse -Force
2889
2990
- name: Login to Azure
3091
uses: azure/login@v2

Modules/CIPPCore/Public/Authentication/Test-CIPPAccess.ps1

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,50 @@ function Test-CIPPAccess {
1212
# Get function help
1313
$FunctionName = 'Invoke-{0}' -f $Request.Params.CIPPEndpoint
1414

15+
$SwPermissions = [System.Diagnostics.Stopwatch]::StartNew()
16+
if (-not $global:CIPPFunctionPermissions) {
17+
$CIPPCoreModule = Get-Module -Name CIPPCore
18+
if ($CIPPCoreModule) {
19+
$PermissionsFileJson = Join-Path $CIPPCoreModule.ModuleBase 'lib' 'data' 'function-permissions.json'
20+
21+
if (Test-Path $PermissionsFileJson) {
22+
try {
23+
$jsonData = Get-Content -Path $PermissionsFileJson -Raw | ConvertFrom-Json -AsHashtable
24+
$global:CIPPFunctionPermissions = [System.Collections.Hashtable]::new([StringComparer]::OrdinalIgnoreCase)
25+
foreach ($key in $jsonData.Keys) {
26+
$global:CIPPFunctionPermissions[$key] = $jsonData[$key]
27+
}
28+
Write-Information "Loaded $($global:CIPPFunctionPermissions.Count) function permissions from JSON cache"
29+
} catch {
30+
Write-Warning "Failed to load function permissions from JSON: $($_.Exception.Message)"
31+
}
32+
}
33+
}
34+
}
35+
$SwPermissions.Stop()
36+
$AccessTimings['FunctionPermissions'] = $SwPermissions.Elapsed.TotalMilliseconds
37+
1538
if ($FunctionName -ne 'Invoke-me') {
1639
$swHelp = [System.Diagnostics.Stopwatch]::StartNew()
17-
try {
18-
$Help = Get-Help $FunctionName -ErrorAction Stop
19-
} catch {
20-
Write-Warning "Function '$FunctionName' not found"
40+
if ($global:CIPPFunctionPermissions -and $global:CIPPFunctionPermissions.ContainsKey($FunctionName)) {
41+
$PermissionData = $global:CIPPFunctionPermissions[$FunctionName]
42+
$APIRole = $PermissionData['Role']
43+
$Functionality = $PermissionData['Functionality']
44+
Write-Information "Loaded function permission data from cache for '$FunctionName': Role='$APIRole', Functionality='$Functionality'"
45+
} else {
46+
try {
47+
$Help = Get-Help $FunctionName -ErrorAction Stop
48+
$APIRole = $Help.Role
49+
$Functionality = $Help.Functionality
50+
Write-Information "Loaded function permission data via Get-Help for '$FunctionName': Role='$APIRole', Functionality='$Functionality'"
51+
} catch {
52+
Write-Warning "Function '$FunctionName' not found"
53+
}
2154
}
2255
$swHelp.Stop()
2356
$AccessTimings['GetHelp'] = $swHelp.Elapsed.TotalMilliseconds
2457
}
2558

26-
# Check help for role
27-
$APIRole = $Help.Role
28-
2959
# Get default roles from config
3060
$swRolesLoad = [System.Diagnostics.Stopwatch]::StartNew()
3161
$CIPPCoreModuleRoot = Get-Module -Name CIPPCore | Select-Object -ExpandProperty ModuleBase
@@ -367,7 +397,7 @@ function Test-CIPPAccess {
367397
if (!$APIAllowed) {
368398
throw "Access to this CIPP API endpoint is not allowed, you do not have the required permission: $APIRole"
369399
}
370-
if (!$TenantAllowed -and $Help.Functionality -notmatch 'AnyTenant') {
400+
if (!$TenantAllowed -and $Functionality -notmatch 'AnyTenant') {
371401
throw 'Access to this tenant is not allowed'
372402
} else {
373403
return $true
@@ -405,12 +435,12 @@ function Test-CIPPAccess {
405435
}
406436
}
407437

408-
if (!$TenantAllowed -and $Help.Functionality -notmatch 'AnyTenant') {
438+
if (!$TenantAllowed -and $Functionality -notmatch 'AnyTenant') {
409439

410440
if (!$APIAllowed) {
411441
throw "Access to this CIPP API endpoint is not allowed, you do not have the required permission: $APIRole"
412442
}
413-
if (!$TenantAllowed -and $Help.Functionality -notmatch 'AnyTenant') {
443+
if (!$TenantAllowed -and $Functionality -notmatch 'AnyTenant') {
414444
Write-Information "Tenant not allowed: $TenantFilter"
415445

416446
throw 'Access to this tenant is not allowed'
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
param(
2+
[string]$ModulePath = (Join-Path $PSScriptRoot '..' 'Modules' 'CIPPCore'),
3+
[string]$OutputPath,
4+
[string]$ModuleName
5+
)
6+
7+
$ErrorActionPreference = 'Stop'
8+
9+
function Resolve-ModuleImportPath {
10+
param(
11+
[Parameter(Mandatory = $true)][string]$Root,
12+
[Parameter(Mandatory = $true)][string]$Name
13+
)
14+
15+
$psd1 = Join-Path $Root "$Name.psd1"
16+
if (Test-Path $psd1) { return $psd1 }
17+
18+
$psm1 = Join-Path $Root "$Name.psm1"
19+
if (Test-Path $psm1) { return $psm1 }
20+
21+
throw "Module files not found for '$Name' in '$Root'. Expected $Name.psd1 or $Name.psm1."
22+
}
23+
24+
function Get-HelpProperty {
25+
param(
26+
[Parameter(Mandatory = $true)]$HelpObject,
27+
[Parameter(Mandatory = $true)][string]$PropertyName
28+
)
29+
30+
$property = $HelpObject.PSObject.Properties[$PropertyName]
31+
if ($property) { return $property.Value }
32+
return ''
33+
}
34+
35+
# Resolve defaults
36+
if (-not (Test-Path -Path $ModulePath)) {
37+
throw "ModulePath '$ModulePath' not found. Provide -ModulePath to the module root."
38+
}
39+
$ModulePath = (Resolve-Path -Path $ModulePath).ProviderPath
40+
if (-not $ModuleName) { $ModuleName = (Split-Path -Path $ModulePath -Leaf) }
41+
if (-not $OutputPath) {
42+
$defaultLibData = Join-Path $ModulePath 'lib' 'data' 'function-permissions.json'
43+
$OutputPath = if (Test-Path (Split-Path -Parent $defaultLibData)) { $defaultLibData } else { Join-Path $ModulePath 'function-permissions.json' }
44+
}
45+
46+
# Ensure destination directory exists
47+
$null = New-Item -ItemType Directory -Path (Split-Path -Parent $OutputPath) -Force
48+
49+
# Import target module so Get-Help can read Role/Functionality metadata
50+
$ModuleImportPath = Resolve-ModuleImportPath -Root $ModulePath -Name $ModuleName
51+
$normalizedImportPath = [System.IO.Path]::GetFullPath($ModuleImportPath)
52+
$loaded = Get-Module -Name $ModuleName | Where-Object { [System.IO.Path]::GetFullPath($_.Path) -eq $normalizedImportPath }
53+
if (-not $loaded) {
54+
Write-Host "Importing module '$ModuleName' from '$ModuleImportPath'"
55+
Import-Module -Name $ModuleImportPath -Force -ErrorAction Stop
56+
} else {
57+
Write-Host "Module '$ModuleName' already loaded from '$ModuleImportPath'; reusing existing session copy."
58+
}
59+
60+
$commands = Get-Command -Module $ModuleName -CommandType Function
61+
$permissions = [ordered]@{}
62+
63+
foreach ($command in $commands | Sort-Object -Property Name | Select-Object -Unique) {
64+
$help = Get-Help -Name $command.Name -ErrorAction SilentlyContinue
65+
if ($help) {
66+
$role = Get-HelpProperty -HelpObject $help -PropertyName 'Role'
67+
$functionality = Get-HelpProperty -HelpObject $help -PropertyName 'Functionality'
68+
} else {
69+
$role = ''
70+
$functionality = ''
71+
}
72+
73+
if ($role -or $functionality) {
74+
$permissions[$command.Name] = @{
75+
Role = $role
76+
Functionality = $functionality
77+
}
78+
} else {
79+
Write-Host "Skipping $($command.Name): no Role or Functionality metadata found."
80+
}
81+
}
82+
83+
# Depth 3 is sufficient for the flat hashtable of functions -> (Role, Functionality)
84+
$json = $permissions | ConvertTo-Json -Depth 3
85+
Set-Content -Path $OutputPath -Value $json -Encoding UTF8
86+
87+
Write-Host "Wrote permissions for $($permissions.Count) functions to $OutputPath"

0 commit comments

Comments
 (0)