Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SqlAGDatabase: Fix impersonate permission test #1295

Merged
merged 32 commits into from
Mar 5, 2019
Merged

SqlAGDatabase: Fix impersonate permission test #1295

merged 32 commits into from
Mar 5, 2019

Conversation

codykonior
Copy link
Contributor

@codykonior codykonior commented Feb 27, 2019

Pull Request (PR) description

When adding a database to an AG using SqlAGDatabase and the MatchDatabaseOwner switch, it checks for an IMPERSONATE ANY LOGIN permission.

That permission doesn't exist until SQL 2014. There are two other alternatives that exist on SQL Server 2012 (and also exist in higher versions):

  • CONTROL SERVER (also granted as part of the sysadmin server role) permission
  • IMPERSONATE permission

This PR adds checks for the two additional permissions if the IMPERSONATE ANY LOGIN permission doesn't exist. It also fixes code coverage for New-TerminatingError as I noticed that wasn't right.

I raised this previously but the PR became too out of date to maintain. To address some of the comments of the time:

Re: SqlServerDscHelper.psm1 about $testLoginEffectPermissionsParams

Could we actually call the existing helper function Get-SqlInstanceMajorVersion to know what the major version is and only call this if the version is 2014 or newer?"

The query that tests the permissions works fine even on lower versions so no change is necessary.

We should not be using $ServerObject.InstanceName?

It's copy-paste of what was there previously.

ServiceName is not the service name. For default instances it's MSSQLSERVER where InstanceName is blank. For non-default instances both are the same.

Invoke-Query depends on InstanceName being MSSQLSERVER instead of blank so it has to stay as-is.

Curious. Did the MatchDatabaseOwner parameter even work before since it never referenced the actual owner of the databases?

It would work in limited circumstances such as SQL 2014 and above, with IMPERSONATE ANY LOGIN permission (and possibly even more).

The reason I say "and possibly more" is that the RESTORE FULL is done under the impersonated account but then RESTORE LOG wasn't. This causes the process to fail even with the IMPERSONATE ANY LOGIN unless it has yet another another high level permission. This has all been fixed.

Re: Tests/Unit/SqlServerDSCHelper.Tests.ps1 about mocking

If we instead mock the call to Test-LoginEffectivePermissions then we should be able to use a ParameterFilter on two different Assert-MockCalled to filter out that we actually call 'Impersonate..' and 'COntrol...' once each.
I think mocking Test-LoginEffectivePermissions would also simplify this test, and give better control testing this helper function?

Test-LoginEffectivePermissions only gets called once, it then executes Invoke-Query one or more times to determine if the permissions are present; which is why that is mocked instead.

If I didn't do this and just mocked Test-LoginEffectivePermissions then nothing would change and my code wouldn't be covered by the test, and so PR would have reduced code coverage.

Re: Other comments about style and updating documentation.

I believe these are all done.

Further testing

I built a separate test rig to confirm this all works correctly on an actual instance. I ran it on my 5-node lab which can be built with my OftenOn PowerShell module and the script executed in a PowerShell admin console on CHWK01 to reproduce.

This separate test is pretty huge and in this gist so as not to clutter up the PR but the final bit which is captured in the gif is easy enough to understand..

  • Confirms SqlAGDatabase works on 2012
  • Confirms SqlAGDatabase fails on 2012 when MatchDatabaseOwner is used
  • Confirms SqlAGDatabase gets restored on 2012 with the correct owner when MatchDatabaseOwner is used

It's checking the return bool from Invoke-DscResource and also who the owner is on the databases.

evidence

This Pull Request (PR) fixes the following issues

Task list

  • Added an entry under the Unreleased section of the change log in the CHANGELOG.md.
    Entry should say what was changed, and how that affects users (if applicable).
  • Resource documentation added/updated in README.md.
  • Resource parameter descriptions added/updated in README.md, schema.mof
    and comment-based help.
  • Comment-based help added/updated.
  • Localization strings added/updated in all localization files as appropriate.
  • Examples appropriately added/updated.
  • Unit tests added/updated. See DSC Resource Testing Guidelines.
  • Integration tests added/updated (where possible). See DSC Resource Testing Guidelines.
  • New/changed code adheres to DSC Resource Style Guidelines and Best Practices.

This change is Reviewable

@codecov-io
Copy link

codecov-io commented Feb 27, 2019

Codecov Report

Merging #1295 into dev will increase coverage by <1%.
The diff coverage is 98%.

Impacted file tree graph

@@          Coverage Diff          @@
##            dev   #1295    +/-   ##
=====================================
+ Coverage    97%     97%   +<1%     
=====================================
  Files        35      35            
  Lines      4376    4432    +56     
=====================================
+ Hits       4271    4327    +56     
  Misses      105     105

@johlju
Copy link
Member

johlju commented Feb 28, 2019

@johlju johlju added the waiting for code fix A review left open comments, and the pull request is waiting for changes to be pushed by the author. label Feb 28, 2019
@codykonior codykonior closed this Feb 28, 2019
@codykonior codykonior reopened this Feb 28, 2019
@codykonior
Copy link
Contributor Author

Any idea how I can investigate this further? I can't reproduce the failure locally.

@johlju
Copy link
Member

johlju commented Feb 28, 2019

You can debug in the appveyor build worker if you can’t reproduce it locally. See the AppVeyor documentation
https://www.appveyor.com/docs/how-to/rdp-to-build-worker/

@codykonior
Copy link
Contributor Author

codykonior commented Mar 2, 2019

I found the cause of the test failures. The Pester mock parameter filters I modified are multi-line and still matched on Windows but would fail on the Linux build machine because of line endings. It was never an issue before because each line of the existing code ended in a wildcard which swallowed the carriage returns. By me adding a line without a wildcard the mock would be skipped and try to connect to an actual server and cause spurious errors.

Anyway this is ready for review once the latest build passes.

@johlju johlju added needs review The pull request needs a code review. and removed waiting for code fix A review left open comments, and the pull request is waiting for changes to be pushed by the author. labels Mar 3, 2019
Copy link
Member

@johlju johlju left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is awesome work, especially the work you put in to manually test this change!

Just a few comments.

Reviewed 5 of 9 files at r1, 4 of 4 files at r3, 1 of 1 files at r4.
Reviewable status: all files reviewed, 6 unresolved discussions (waiting on @codykonior)


README.md, line 290 at r4 (raw file):

PSDscRunAsCredential

This should actually be written with 'Ps'. https://docs.microsoft.com/en-us/powershell/dsc/configurations/runasuser

We could do a search/replace if there are other places where this is written wrongly.


SqlServerDscHelper.psm1, line 1062 at r4 (raw file):

function Test-LoginEffectivePermissions

Non-blocking: Would be really nice if we could add comment-based help to this function. But since there were none to begin this, this can be added in another PR in a larger cleanup job.


SqlServerDscHelper.psm1, line 1084 at r4 (raw file):

# Other types are not used here and so not implemented:

Can we change this to a comment-block? Although this would be more suitable in a comment-based help (see previous comment)

<#
    Other types are not used here and so not implemented:
    ...
#>

SqlServerDscHelper.psm1, line 1279 at r4 (raw file):

$impersonatePermissionsPresent = $false

I thin k this row is unnecessary since it is set two calls down to either false or true?


SqlServerDscHelper.psm1, line 1292 at r4 (raw file):

return $impersonatePermissionsPresent

Maybe we should write a Verbose message saying what type of impersonate permission was returned for informational (debug) purposes? Before each return statement below.


Tests/Unit/SqlServerDSCHelper.Tests.ps1, line 1277 at r4 (raw file):

Assert-MockCalled -CommandName Invoke-Query -Exactly -Times 1 -Scope It

I think we instead should mock Test-LoginEffectivePermissions in the tests for Test-ImpersonatePermissions, and check with a ParameterFilter on the Assert-MockCalled that the correct properties of Test-LoginEffectivePermissions was called.
It would be easier than rely on a call in another helper function (the Invoke-Query).

The separate unit tests for Test-LoginEffectivePermissions should assert that Invoke-Query was called correctly.

We should preferably not test two helper functions in the same unit test, especially now when one become more complex.

@johlju johlju added waiting for code fix A review left open comments, and the pull request is waiting for changes to be pushed by the author. and removed needs review The pull request needs a code review. labels Mar 3, 2019
Copy link
Contributor Author

@codykonior codykonior left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewable status: 4 of 10 files reviewed, 5 unresolved discussions (waiting on @johlju)


README.md, line 290 at r4 (raw file):

Previously, johlju (Johan Ljunggren) wrote…
PSDscRunAsCredential

This should actually be written with 'Ps'. https://docs.microsoft.com/en-us/powershell/dsc/configurations/runasuser

We could do a search/replace if there are other places where this is written wrongly.

Done.


SqlServerDscHelper.psm1, line 1084 at r4 (raw file):

Previously, johlju (Johan Ljunggren) wrote…
# Other types are not used here and so not implemented:

Can we change this to a comment-block? Although this would be more suitable in a comment-based help (see previous comment)

<#
    Other types are not used here and so not implemented:
    ...
#>

Done.


SqlServerDscHelper.psm1, line 1279 at r4 (raw file):

Previously, johlju (Johan Ljunggren) wrote…
$impersonatePermissionsPresent = $false

I thin k this row is unnecessary since it is set two calls down to either false or true?

Done.


SqlServerDscHelper.psm1, line 1292 at r4 (raw file):

Previously, johlju (Johan Ljunggren) wrote…
return $impersonatePermissionsPresent

Maybe we should write a Verbose message saying what type of impersonate permission was returned for informational (debug) purposes? Before each return statement below.

Done.


Tests/Unit/SqlServerDSCHelper.Tests.ps1, line 1277 at r4 (raw file):

Previously, johlju (Johan Ljunggren) wrote…
Assert-MockCalled -CommandName Invoke-Query -Exactly -Times 1 -Scope It

I think we instead should mock Test-LoginEffectivePermissions in the tests for Test-ImpersonatePermissions, and check with a ParameterFilter on the Assert-MockCalled that the correct properties of Test-LoginEffectivePermissions was called.
It would be easier than rely on a call in another helper function (the Invoke-Query).

The separate unit tests for Test-LoginEffectivePermissions should assert that Invoke-Query was called correctly.

We should preferably not test two helper functions in the same unit test, especially now when one become more complex.

I've reverted this test to as it was before but I'm unsure where you'd like the replacement test put.

Copy link
Member

@johlju johlju left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed 5 of 6 files at r5.
Reviewable status: 3 of 10 files reviewed, 1 unresolved discussion (waiting on @codykonior and @johlju)


Tests/Unit/SqlServerDSCHelper.Tests.ps1, line 1277 at r4 (raw file):

Previously, codykonior (Cody Konior) wrote…

I've reverted this test to as it was before but I'm unsure where you'd like the replacement test put.

I think the changes to Test-ImpersonatePermissions should be tested here, but mock and assert the call to Test-LoginEffectivePermissions ( and assert using ParameterFilter to make sure it is called with the expected parameters).

I think the changes to Test-LoginEffectivePermissions should go in the tests in the following link, and will mock and assert Invoke-Query.
https://github.com/PowerShell/SqlServerDsc/blob/6a87852967c15f22f8b1c3809c2d531682a88873/Tests/Unit/SqlServerDSCHelper.Tests.ps1#L682

Would that work?

Copy link
Member

@johlju johlju left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed 7 of 7 files at r6.
Reviewable status: all files reviewed, 1 unresolved discussion (waiting on @codykonior)

These cover the alternate code path inside where a different
Invoke-Query is issued for a LOGIN object (the new tests) as
opposed to a SERVER object (the old tests).
Copy link
Contributor Author

@codykonior codykonior left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewable status: 9 of 10 files reviewed, 1 unresolved discussion (waiting on @johlju)


Tests/Unit/SqlServerDSCHelper.Tests.ps1, line 1277 at r4 (raw file):

Previously, johlju (Johan Ljunggren) wrote…

I think the changes to Test-ImpersonatePermissions should be tested here, but mock and assert the call to Test-LoginEffectivePermissions ( and assert using ParameterFilter to make sure it is called with the expected parameters).

I think the changes to Test-LoginEffectivePermissions should go in the tests in the following link, and will mock and assert Invoke-Query.
https://github.com/PowerShell/SqlServerDsc/blob/6a87852967c15f22f8b1c3809c2d531682a88873/Tests/Unit/SqlServerDSCHelper.Tests.ps1#L682

Would that work?

Done.

Copy link
Member

@johlju johlju left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:lgtm:

Reviewed 1 of 1 files at r8.
Reviewable status: all files reviewed, 2 unresolved discussions (waiting on @codykonior)


Tests/Unit/SqlServerDSCHelper.Tests.ps1, line 766 at r8 (raw file):

It 'Should return $false when the specified login has no permissions assigned'

Is this test the same as the one two tests down?


Tests/Unit/SqlServerDSCHelper.Tests.ps1, line 775 at r8 (raw file):

It 'Should return $false when the desired login permissions are not present' {

Is this test the same as the previous comment?

@johlju
Copy link
Member

johlju commented Mar 4, 2019

@codykonior Can you double check the tests I commented on? It looks like the are the same? Otherwise it is all good now! 😄

Copy link
Contributor Author

@codykonior codykonior left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewable status: 9 of 10 files reviewed, 2 unresolved discussions (waiting on @johlju)


Tests/Unit/SqlServerDSCHelper.Tests.ps1, line 766 at r8 (raw file):

Previously, johlju (Johan Ljunggren) wrote…
It 'Should return $false when the specified login has no permissions assigned'

Is this test the same as the one two tests down?

Done.


Tests/Unit/SqlServerDSCHelper.Tests.ps1, line 775 at r8 (raw file):

Previously, johlju (Johan Ljunggren) wrote…
It 'Should return $false when the desired login permissions are not present' {

Is this test the same as the previous comment?

It was but it wasn't meant to be. I've updated the test.

Copy link
Member

@johlju johlju left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:lgtm:

Reviewed 3 of 3 files at r9.
Reviewable status: :shipit: complete! all files reviewed, all discussions resolved

@johlju johlju merged commit 9801548 into dsccommunity:dev Mar 5, 2019
@johlju
Copy link
Member

johlju commented Mar 5, 2019

@codykonior Awesome work on this! This one is merged now. 😄

@johlju johlju removed the waiting for code fix A review left open comments, and the pull request is waiting for changes to be pushed by the author. label Apr 10, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

SqlAGDatabase: Impersonate permission incorrectly checked on SQL 2012
3 participants