Skip to content

Commit

Permalink
Merge pull request #1 from webmd-health-services/feature/test-cprincipal
Browse files Browse the repository at this point in the history
Initial version, migrated from Carbon
  • Loading branch information
splatteredbits authored Mar 26, 2024
2 parents 2211f88 + bb22e94 commit 8bb9853
Show file tree
Hide file tree
Showing 21 changed files with 793 additions and 121 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/.output
/PSModules
/Carbon.Accounts/Modules
/.vscode
/Carbon.Accounts/CHANGELOG.md
/Carbon.Accounts/LICENSE.txt
Expand Down
21 changes: 21 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@

# Carbon.Accounts PowerShell Module Changelog

## 1.0.0

### Upgrade Instructions

If switching to Carbon.Accounts from Carbon, do the following:

* Remove usages of the `ConnectedServer` property on `System.DirectoryServices.AccountManagement.Principal` objects.
That was an extended type property added by Carbon and it no longer exists.
* Remove usages of the `Carbon.Identity` and `Carbon.IdentityType` types. `Carbon.Accounts` now uses and returns native
PowerShell classes and enums instead. The new native/classes enums are identical to the old compiled types, so no need
to update object usages.

### Added

* `ConvertTo-CSecurityIdentifier` (from Carbon).
* `Resolve-CIdentity` (from Carbon).
* `Test-CIdentity` (from Carbon).
* `Resolve-CIdentityName` (from Carbon).
8 changes: 6 additions & 2 deletions Carbon.Accounts/Carbon.Accounts.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
RootModule = 'Carbon.Accounts.psm1'

# Version number of this module.
ModuleVersion = '0.0.0'
ModuleVersion = '1.0.0'

# ID used to uniquely identify this module
GUID = '4e82802a-d791-475b-9fa4-c41888276b23'
Expand All @@ -36,7 +36,7 @@
Copyright = '(c) WebMD Health Services.'

# Description of the functionality provided by this module
Description = ''
Description = 'Manages accounts, identities, principals, users, groups, and privileges.'

# Minimum version of the Windows PowerShell engine required by this module
PowerShellVersion = '5.1'
Expand Down Expand Up @@ -76,6 +76,10 @@

# Functions to export from this module. Only list public function here.
FunctionsToExport = @(
'ConvertTo-CSecurityIdentifier',
'Resolve-CIdentity',
'Resolve-CIdentityName',
'Test-CIdentity'
)

# Cmdlets to export from this module. By default, you get a script module, so there are no cmdlets.
Expand Down
80 changes: 74 additions & 6 deletions Carbon.Accounts/Carbon.Accounts.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,88 @@
# See the License for the specific language governing permissions and
# limitations under the License

using namespace System.ComponentModel
using namespace System.Runtime.InteropServices
using namespace System.Security.Principal

#Requires -Version 5.1
Set-StrictMode -Version 'Latest'

# Functions should use $moduleRoot as the relative root from which to find
# things. A published module has its function appended to this file, while a
# Functions should use $script:moduleRoot as the relative root from which to find
# things. A published module has its function appended to this file, while a
# module in development has its functions in the Functions directory.
$moduleRoot = $PSScriptRoot
$script:moduleRoot = $PSScriptRoot
$modulesroot = Join-Path -Path $script:moduleRoot -ChildPath 'Modules' -Resolve

Import-Module -Name (Join-Path -Path $modulesRoot -ChildPath 'PureInvoke' -Resolve) `
-Function @('Invoke-AdvapiLookupAccountName', 'Invoke-AdvapiLookupAccountSid') `
-Verbose:$false

enum Carbon_Accounts_Identity_Type
{
User = 1
Group
Domain
Alias
WellKnownGroup
DeletedAccount
Invalid
Unknown
Computer
Label
}

class Carbon_Accounts_Identity
{
Carbon_Accounts_Identity([String] $Domain,
[String] $Name,
[SecurityIdentifier]$Sid,
[Carbon_Accounts_Identity_Type]$Type)
{
$this.Domain = $Domain;
$this.Name = $Name;
$this.Sid = $Sid;
$this.Type = $Type;

$this.FullName = $Name
if ($Domain)
{
$this.FullName = "${Domain}\${Name}"
}
}

[String] $Domain

[String] $FullName

[String] $Name

[SecurityIdentifier] $Sid

[Carbon_Accounts_Identity_Type] $Type

[bool] Equals([Object] $obj)
{
if ($null -eq $obj -or $obj -isnot [Carbon_Accounts_Identity])
{
return $false;
}

return $this.Sid.Equals($obj.Sid);
}

[String] ToString()
{
return $this.FullName
}
}

# Store each of your module's functions in its own file in the Functions
# directory. On the build server, your module's functions will be appended to
# Store each of your module's functions in its own file in the Functions
# directory. On the build server, your module's functions will be appended to
# this file, so only dot-source files that exist on the file system. This allows
# developers to work on a module without having to build it first. Grab all the
# functions that are in their own files.
$functionsPath = Join-Path -Path $moduleRoot -ChildPath 'Functions\*.ps1'
$functionsPath = Join-Path -Path $script:moduleRoot -ChildPath 'Functions\*.ps1'
if( (Test-Path -Path $functionsPath) )
{
foreach( $functionPath in (Get-Item $functionsPath) )
Expand Down
88 changes: 88 additions & 0 deletions Carbon.Accounts/Functions/ConvertTo-CSecurityIdentifier.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@

function ConvertTo-CSecurityIdentifier
{
<#
.SYNOPSIS
Converts a string or byte array security identifier into a `System.Security.Principal.SecurityIdentifier` object.
.DESCRIPTION
`ConvertTo-CSecurityIdentifier` converts a SID in SDDL form (as a string), in binary form (as a byte array) into a
`System.Security.Principal.SecurityIdentifier` object. It also accepts
`System.Security.Principal.SecurityIdentifier` objects, and returns them back to you.
If the string or byte array don't represent a SID, an error is written and nothing is returned.
.LINK
Resolve-CIdentity
.LINK
Resolve-CIdentityName
.EXAMPLE
ConvertTo-CSecurityIdentifier -SID 'S-1-5-21-2678556459-1010642102-471947008-1017'
Demonstrates how to convert a a SID in SDDL into a `System.Security.Principal.SecurityIdentifier` object.
.EXAMPLE
ConvertTo-CSecurityIdentifier -SID (New-Object 'Security.Principal.SecurityIdentifier' 'S-1-5-21-2678556459-1010642102-471947008-1017')
Demonstrates that you can pass a `SecurityIdentifier` object as the value of the SID parameter. The SID you passed
in will be returned to you unchanged.
.EXAMPLE
ConvertTo-CSecurityIdentifier -SID $sidBytes
Demonstrates that you can use a byte array that represents a SID as the value of the `SID` parameter.
#>
[CmdletBinding()]
param(
# The SID to convert to a `System.Security.Principal.SecurityIdentifier`. Accepts a SID in SDDL form as a
# `string`, a `System.Security.Principal.SecurityIdentifier` object, or a SID in binary form as an array of
# bytes.
[Parameter(Mandatory)]
[Object] $SID
)

Set-StrictMode -Version 'Latest'
Use-CallerPreference -Cmdlet $PSCmdlet -Session $ExecutionContext.SessionState

try
{
if ( $SID -is [string])
{
New-Object 'Security.Principal.SecurityIdentifier' $SID
}
elseif ($SID -is [byte[]])
{
New-Object 'Security.Principal.SecurityIdentifier' $SID,0
}
elseif ($SID -is [Security.Principal.SecurityIdentifier])
{
$SID
}
else
{
$msg = "Invalid SID parameter value [$($SID.GetType().FullName)]${SID}. Only " +
'[System.Security.Principal.SecurityIdentifier] objects, SIDs in SDDL form as a [String], or SIDs ' +
'in binary form as a byte array are allowed.'
return
}
}
catch
{
$sidDisplayMsg = ''
if ($SID -is [String])
{
$sidDisplayMsg = " ""${SID}"""
}
elseif ($SID -is [byte[]])
{
$sidDisplayMsg = " [$($SID -join ', ')]"
}
$msg = "Exception converting SID${sidDisplayMsg} to a [System.Security.Principal.SecurityIdentifier] " +
'object. This usually means you passed an invalid SID in SDDL form (as a string) or an invalid SID ' +
"in binary form (as a byte array): ${_}"
Write-Error $msg -ErrorAction $ErrorActionPreference
return
}
}
144 changes: 144 additions & 0 deletions Carbon.Accounts/Functions/Resolve-CIdentity.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@

function Resolve-CIdentity
{
<#
.SYNOPSIS
Gets domain, name, type, and SID information about a user or group.
.DESCRIPTION
The `Resolve-CIdentity` function takes an identity name or security identifier (SID) and gets its canonical
representation. It returns a `Carbon_Accounts_Identity` object, which contains the following information about the
identity:
* Domain - the domain the user was found in
* FullName - the users full name, e.g. Domain\Name
* Name - the user's username or the group's name
* Type - the Sid type.
* Sid - the account's security identifier as a `System.Security.Principal.SecurityIdentifier` object.
The common name for an account is not always the canonical name used by the operating system. For example, the
local Administrators group is actually called BUILTIN\Administrators. This function uses the `LookupAccountName`
and `LookupAccountSid` Windows functions to resolve an account name or security identifier into its domain, name,
full name, SID, and SID type.
You may pass a `System.Security.Principal.SecurityIdentifer`, a SID in SDDL form (as a string), or a SID in binary
form (a byte array) as the value to the `SID` parameter. You'll get an error and nothing returned if the SDDL or
byte array SID are invalid.
If the name or security identifier doesn't represent an actual user or group, an error is written and nothing is
returned.
.LINK
Test-CIdentity
.LINK
Resolve-CIdentityName
.LINK
http://msdn.microsoft.com/en-us/library/system.security.principal.securityidentifier.aspx
.LINK
http://msdn.microsoft.com/en-us/library/windows/desktop/aa379601.aspx
.LINK
ConvertTo-CSecurityIdentifier
.LINK
Resolve-CIdentityName
.LINK
Test-CIdentity
.OUTPUTS
Carbon_Accounts_Identity.
.EXAMPLE
Resolve-CIdentity -Name 'Administrators'
Returns an object representing the `Administrators` group.
.EXAMPLE
Resolve-CIdentity -SID 'S-1-5-21-2678556459-1010642102-471947008-1017'
Demonstrates how to use a SID in SDDL form to convert a SID into an identity.
.EXAMPLE
Resolve-CIdentity -SID ([Security.Principal.SecurityIdentifier]::New()'S-1-5-21-2678556459-1010642102-471947008-1017')
Demonstrates that you can pass a `SecurityIdentifier` object as the value of the SID parameter.
.EXAMPLE
Resolve-CIdentity -SID $sidBytes
Demonstrates that you can use a byte array that represents a SID as the value of the `SID` parameter.
#>
[CmdletBinding()]
param(
# The name of the identity to return.
[Parameter(Mandatory, ParameterSetName='ByName', Position=0)]
[string] $Name,

# The SID of the identity to return. Accepts a SID in SDDL form as a `string`, a
# `System.Security.Principal.SecurityIdentifier` object, or a SID in binary form as an array of bytes.
[Parameter(Mandatory , ParameterSetName='BySid')]
[Object] $SID
)

Set-StrictMode -Version 'Latest'
Use-CallerPreference -Cmdlet $PSCmdlet -Session $ExecutionContext.SessionState

if ($PSCmdlet.ParameterSetName -eq 'BySid')
{
$SID = ConvertTo-CSecurityIdentifier -SID $SID
if (-not $SID)
{
return
}

$sidBytes = [byte[]]::New($SID.BinaryLength)
$SID.GetBinaryForm($sidBytes, 0)
$account = Invoke-AdvapiLookupAccountSid -Sid $sidBytes
if (-not $account)
{
Write-Error -Message "SID ""${SID}"" not found." -ErrorAction $ErrorActionPreference
return
}
return [Carbon_Accounts_Identity]::New($account.ReferencedDomainName, $account.Name, $SID, $account.Use)
}

if ($Name.StartsWith('.\'))
{
$username = $Name.Substring(2)
$Name = "$([Environment]::MachineName)\${username}"
$identity = Resolve-CIdentity -Name $Name
if (-not $identity)
{
$Name = "BUILTIN\${username}"
$identity = Resolve-CIdentity -Name $Name
}
return $identity
}

if ($Name.Equals("LocalSystem", [StringComparison]::InvariantCultureIgnoreCase))
{
$Name = "NT AUTHORITY\SYSTEM"
}

$account = Invoke-AdvapiLookupAccountName -AccountName $Name
if (-not $account)
{
Write-Error -Message "Identity ""${Name}"" not found." -ErrorAction $ErrorActionPreference
return
}

$sid = [SecurityIdentifier]::New($account.Sid, 0)
$ntAccount = $sid.Translate([NTAccount])
$domainName,$accountName = $ntAccount.Value.Split('\', 2)
if (-not $accountName)
{
$accountName = $domainName
$domainName = ''
}
return [Carbon_Accounts_Identity]::New($domainName, $accountName, $sid, $account.Use)

}
Loading

0 comments on commit 8bb9853

Please sign in to comment.