diff --git a/changelogs/fragments/win_acl_follow.yml b/changelogs/fragments/win_acl_follow.yml new file mode 100644 index 00000000..5df026e8 --- /dev/null +++ b/changelogs/fragments/win_acl_follow.yml @@ -0,0 +1,2 @@ +minor_changes: +- win_acl - Added the ``follow`` parameter with will follow the symlinks and junctions before applying ACLs to change the target instead of the link diff --git a/plugins/modules/win_acl.ps1 b/plugins/modules/win_acl.ps1 index 956c81a7..aa8b7401 100644 --- a/plugins/modules/win_acl.ps1 +++ b/plugins/modules/win_acl.ps1 @@ -8,6 +8,7 @@ #Requires -Module Ansible.ModuleUtils.Legacy #Requires -Module Ansible.ModuleUtils.PrivilegeUtil #Requires -Module Ansible.ModuleUtils.SID +#Requires -Module Ansible.ModuleUtils.LinkUtil $ErrorActionPreference = "Stop" @@ -89,6 +90,7 @@ $state = Get-AnsibleParam -obj $params -name "state" -type "str" -default "prese $inherit = Get-AnsibleParam -obj $params -name "inherit" -type "str" $propagation = Get-AnsibleParam -obj $params -name "propagation" -type "str" -default "None" -validateset "InheritOnly", "None", "NoPropagateInherit" +$follow = Get-AnsibleParam -obj $params -name "follow" -type "bool" -default "false" # We mount the HKCR, HKU, and HKCC registry hives so PS can access them. # Network paths have no qualifiers so we use -EA SilentlyContinue to ignore that @@ -103,6 +105,23 @@ if ($path_qualifier -eq "HKCC:" -and (-not (Test-Path -LiteralPath HKCC:\))) { New-PSDrive -Name HKCC -PSProvider Registry -Root HKEY_CURRENT_CONFIG > $null } +Load-LinkUtils +while ($follow) { + try { + $link_info = Get-Link $path + } + catch { + $link_info = $null + } + + if ($link_info -and $link_info.Type -in @("SymbolicLink", "JunctionPoint")) { + $path = $link_info.AbsolutePath + } + else { + break + } +} + If (-Not (Test-Path -LiteralPath $path)) { Fail-Json -obj $result -message "$path file or directory does not exist on the host" } @@ -191,10 +210,10 @@ Try { If ($state -eq "present" -And $match -eq $false) { Try { $objACL.AddAccessRule($objACE) - If ($path_item.PSProvider.Name -eq "Registry") { + Try { Set-ACL -LiteralPath $path -AclObject $objACL } - else { + Catch { (Get-Item -LiteralPath $path).SetAccessControl($objACL) } $result.changed = $true diff --git a/plugins/modules/win_acl.py b/plugins/modules/win_acl.py index 139bd490..219a365b 100644 --- a/plugins/modules/win_acl.py +++ b/plugins/modules/win_acl.py @@ -65,6 +65,12 @@ type: str choices: [ InheritOnly, None, NoPropagateInherit ] default: "None" + follow: + description: + - Follow the symlinks and junctions to apply the ACLs to the target instead of the link. + type: bool + default: false + version_added: 1.12.0 notes: - If adding ACL's for AppPool identities, the Windows Feature "Web-Scripting-Tools" must be enabled. seealso: diff --git a/tests/integration/targets/win_acl/tasks/main.yml b/tests/integration/targets/win_acl/tasks/main.yml index d322fb89..3872baf4 100644 --- a/tests/integration/targets/win_acl/tasks/main.yml +++ b/tests/integration/targets/win_acl/tasks/main.yml @@ -17,6 +17,23 @@ - present - block: + - name: create test dir for link target + win_file: + path: '{{ test_acl_path }}\target' + state: directory + + - name: create symlinks in test dir + win_powershell: + script: | + param ( + [string]$Path + ) + + cmd.exe /c mklink /J "$Path\junction" "$Path\target" + cmd.exe /c mklink /D "$Path\symlink" "$Path\junction" + parameters: + Path: '{{ test_acl_path }}' + - name: run tests include_tasks: tests.yml diff --git a/tests/integration/targets/win_acl/tasks/tests.yml b/tests/integration/targets/win_acl/tasks/tests.yml index 56f52733..a3af720d 100644 --- a/tests/integration/targets/win_acl/tasks/tests.yml +++ b/tests/integration/targets/win_acl/tasks/tests.yml @@ -213,6 +213,76 @@ - remove_right is changed - remove_right_actual.stdout_lines == ["[", "", "]"] +- name: add right to symlink without follow + win_acl: + path: '{{ test_acl_path }}\symlink' + type: allow + user: Guests + rights: Write + register: allow_not_follow + +- name: get result of add right to symlink without follow on symlink + win_shell: '$path = ''{{ test_acl_path }}\symlink''; {{ test_ace_cmd }}' + register: allow_not_follow_symlink + +- name: get result of add right to symlink without follow on symlink + win_shell: '$path = ''{{ test_acl_path }}\target''; {{ test_ace_cmd }}' + register: allow_not_follow_target + +- name: assert add write rights to Guest - network + assert: + that: + - allow_not_follow is changed + - (allow_not_follow_symlink.stdout|from_json)|count == 1 + - (allow_not_follow_symlink.stdout|from_json)[0].identity == 'BUILTIN\Guests' + - (allow_not_follow_symlink.stdout|from_json)[0].inheritance_flags == 'ContainerInherit, ObjectInherit' + - (allow_not_follow_symlink.stdout|from_json)[0].propagation_flags == 'None' + - (allow_not_follow_symlink.stdout|from_json)[0].rights == 'Write, Synchronize' + - (allow_not_follow_symlink.stdout|from_json)[0].type == 'Allow' + - (allow_not_follow_target.stdout|from_json)|count == 0 + +- name: remove write rights on symlink + win_acl: + path: '{{ test_acl_path }}\symlink' + type: allow + user: Guests + rights: Write + state: absent + +- name: add right to symlink with follow + win_acl: + path: '{{ test_acl_path }}\symlink' + type: allow + user: Guests + rights: Write + follow: true + register: allow_follow + +- name: get result of add right to symlink without follow on symlink + win_shell: '$path = ''{{ test_acl_path }}\symlink''; {{ test_ace_cmd }}' + register: allow_follow_symlink + +- name: get result of add right to junction without follow on symlink + win_shell: '$path = ''{{ test_acl_path }}\junction''; {{ test_ace_cmd }}' + register: allow_follow_junction + +- name: get result of add right to symlink without follow on symlink + win_shell: '$path = ''{{ test_acl_path }}\target''; {{ test_ace_cmd }}' + register: allow_follow_target + +- name: assert add write rights to Guest - network + assert: + that: + - allow_not_follow is changed + - (allow_follow_symlink.stdout|from_json)|count == 0 + - (allow_follow_junction.stdout|from_json)|count == 0 + - (allow_follow_target.stdout|from_json)|count == 1 + - (allow_follow_target.stdout|from_json)[0].identity == 'BUILTIN\Guests' + - (allow_follow_target.stdout|from_json)[0].inheritance_flags == 'ContainerInherit, ObjectInherit' + - (allow_follow_target.stdout|from_json)[0].propagation_flags == 'None' + - (allow_follow_target.stdout|from_json)[0].rights == 'Write, Synchronize' + - (allow_follow_target.stdout|from_json)[0].type == 'Allow' + - name: add write rights to Guest - registry win_acl: path: '{{ test_acl_reg_path }}'