Skip to content

Commit

Permalink
New-TssUser - closes #127
Browse files Browse the repository at this point in the history
  • Loading branch information
wsmelton committed Apr 11, 2021
1 parent 752a6ae commit 27b69a7
Show file tree
Hide file tree
Showing 6 changed files with 237 additions and 16 deletions.
6 changes: 2 additions & 4 deletions docs/_data/navigation.yml
Expand Up @@ -58,8 +58,6 @@ commands:
url: /commands/Get-TssFolder
- title: "Get-TssFolderAudit"
url: /commands/Get-TssFolderAudit
- title: "Get-TssFolderStub"
url: /commands/Get-TssFolderStub
- title: "New-TssFolder"
url: /commands/New-TssFolder
- title: "Remove-TssFolder"
Expand Down Expand Up @@ -174,8 +172,8 @@ commands:
url: /commands/Get-TssUserRole
- title: "Get-TssUserRoleAssigned"
url: /commands/Get-TssUserRoleAssigned
- title: "Get-TssUserStub"
url: /commands/Get-TssUserStub
- title: "New-TssUser"
url: /commands/New-TssUser
- title: "Search-TssUser"
url: /commands/Search-TssUser
- title: "Show-TssCurrentUser"
Expand Down
2 changes: 1 addition & 1 deletion docs/collections/_abouttopics/about_tssuser.md
Expand Up @@ -123,4 +123,4 @@ last_modified_at: 2021-03-03T00:00:00-00:00

# RELATED LINKS:
Get-TssUser
Get-TssUserStub
New-TssUser
10 changes: 3 additions & 7 deletions docs/collections/_pages/commands.md
Expand Up @@ -45,7 +45,6 @@ A Thycotic.SecretServer command may not appear in the below list because it is n
[Find-TssFolder] | [GET /folders/lookup]
[Get-TssFolder] | [GET /folders/{id}]
[Get-TssFolderAudit] | [GET /folders/{id}/audit]
[Get-TssFolderStub] | [GET /folders/stub]
[New-TssFolder] | [POST /folders]
[Remove-TssFolder] | [DELETE /folders/{id}]
[Remove-TssFolderTemplate] | [DELETE /folders/{id}/templates/{templateId}]
Expand Down Expand Up @@ -151,9 +150,9 @@ A Thycotic.SecretServer command may not appear in the below list because it is n
[Get-TssUserAudit] | [GET /users/{userId}/audit]
[Get-TssUserRole] | [GET /users/{id}/roles]
[Get-TssUserRoleAssigned] | [GET /users/{userId}/roles-assigned]
[New-TssUser] | [POST /users]
[Search-TssUser] | [GET /users]
[Show-TssCurrentUser] | [GET /users/current]
[Get-TssUserStub] | [GET /users/stub]

[New-TssSession]:/thycotic.secretserver/commands/New-TssSession
[Get-TssFolder]:/thycotic.secretserver/commands/Get-TssFolder
Expand All @@ -177,7 +176,6 @@ A Thycotic.SecretServer command may not appear in the below list because it is n
[Search-TssFolder]:/thycotic.secretserver/commands/Search-TssFolder
[Find-TssFolder]:/thycotic.secretserver/commands/Find-TssFolder
[New-TssFolder]:/thycotic.secretserver/commands/New-TssFolder
[Get-TssFolderStub]:/thycotic.secretserver/commands/Get-TssFolderStub
[Remove-TssFolder]:/thycotic.secretserver/commands/Remove-TssFolder
[Get-TssFolderAudit]:/thycotic.secretserver/commands/Get-TssFolderAudit
[Set-TssFolder]:/thycotic.secretserver/commands/Set-TssFolder
Expand Down Expand Up @@ -214,18 +212,18 @@ A Thycotic.SecretServer command may not appear in the below list because it is n
[Search-TssSecretTemplate]:/thycotic.secretserver/commands/Search-TssSecretTemplate
[Get-TssUserAudit]:/thycotic.secretserver/commands/Get-TssUserAudit
[Protect-TssSecret]:/thycotic.secretserver/commands/Protect-TssSecret
[Get-TssUserStub]:/thycotic.secretserver/commands/Get-TssUserStub
[Set-TssSecretSecurity]:/thycotic.secretserver/commands/Set-TssSecretSecurity
[Revoke-TssSecret]:/thycotic.secretserver/commands/Revoke-TssSecret
[Set-TssSecretExpiration]:/thycotic.secretserver/commands/Set-TssExpiration
[Set-TssSecretRpcPrivileged]:/thycotic.secretserver/commands/Set-TssSecretRpcPrivileged
[Search-TssDistributedEngineSite]:/thycotic.secretserver/commands/Search-TssDistributedEngineSite
[New-TssUser]:/thycotic.secretserver/commands/New-TssUser

[POST /users]:https://updates.thycotic.net/secretserver/restapiguide/10.9.33/TokenAuth/#operation--users-post
[GET /distributed-engine/sites]:http://argos/SecretServer/documents/restapi/TokenAuth/#operation--distributed-engine-sites-get
[PUT /secrets/{id}/expiration]:http://argos/SecretServer/documents/restapi/TokenAuth/#operation--secrets--id--expiration-put
[POST /secrets/{id}/expire]:http://argos/SecretServer/documents/restapi/TokenAuth/#operation--secrets--id--expire-post
[PATCH /secrets/{id}/security-general]:https://updates.thycotic.net/secretserver/restapiguide/10.9.33/TokenAuth/#operation--secrets--id--security-general-patch
[GET /users/stub]:https://updates.thycotic.net/secretserver/restapiguide/10.9.33/TokenAuth/#operation--users-stub-get
[POST /secrets/{id}/check-in]:https://updates.thycotic.net/secretserver/restapiguide/10.9.33/TokenAuth/#operation--secrets--id--check-in-post
[GET /users/{userId}/audit]:https://updates.thycotic.net/secretserver/restapiguide/10.9.33/TokenAuth/#operation--users--userId--audit-get
[GET /secret-templates]:https://updates.thycotic.net/secretserver/restapiguide/10.9.33/TokenAuth/#operation--secret-templates-get
Expand Down Expand Up @@ -257,7 +255,6 @@ A Thycotic.SecretServer command may not appear in the below list because it is n
[GET /folders/{id}/audit]:https://updates.thycotic.net/secretserver/restapiguide/10.9.33/TokenAuth/#operation--folders--id--audit-get
[PATCH /folders/{id}]:https://updates.thycotic.net/secretserver/restapiguide/10.9.33/TokenAuth/#operation--folder--id--patch
[DELETE /folders/{id}]:https://updates.thycotic.net/secretserver/restapiguide/10.9.33/TokenAuth/#operation--folders--id--delete
[GET /folders/stub]:https://updates.thycotic.net/secretserver/restapiguide/10.9.33/TokenAuth/#operation--folders-stub-get
[POST /folders]:https://updates.thycotic.net/secretserver/restapiguide/10.9.33/TokenAuth/#operation--folders-post
[GET /folders/lookup]:https://updates.thycotic.net/secretserver/restapiguide/10.9.33/TokenAuth/#operation--folders-lookup-get
[GET /folders]:https://updates.thycotic.net/secretserver/restapiguide/10.9.33/TokenAuth/#operation--folders-get
Expand All @@ -283,6 +280,5 @@ A Thycotic.SecretServer command may not appear in the below list because it is n
[GET /secret-templates/{id}]:https://updates.thycotic.net/secretserver/restapiguide/10.9.33/TokenAuth/#operation--secret-templates--id--get
[GET /secrets/{id}/stop-password-change]:https://updates.thycotic.net/secretserver/restapiguide/10.9.33/TokenAuth/#operation--secrets--id--stop-password-change-post
[GET /version]:https://updates.thycotic.net/secretserver/restapiguide/10.9.33/TokenAuth/#operation--version-get
[GET /secrets/stubs]:https://updates.thycotic.net/secretserver/restapiguide/10.9.33/TokenAuth/#operation--secrets-stub-get
[POST /secrets]:https://updates.thycotic.net/secretserver/restapiguide/10.9.33/TokenAuth/#operation--secrets-post
[POST /secrets/{id}/stop-password-change]:https://updates.thycotic.net/secretserver/restapiguide/10.9.33/TokenAuth/#operation--secrets--id--stop-password-change-post
8 changes: 4 additions & 4 deletions src/en-us/about_tssuser.help.txt
@@ -1,14 +1,14 @@
TOPIC
This help topic describes the TssUserModel class in the Thycotic.SecretServer module
This help topic describes the TssUser class in the Thycotic.SecretServer module

CLASS
TssUserModel
TssUser

INHERITANCE
None

DESCRIPTION
The TssUserModel class represents the UserModel object returned by Secret Server endpoint GET /users/{id}
The TssUser class represents the UserModel object returned by Secret Server endpoint GET /users/{id}

CONSTRUCTORS
new()
Expand Down Expand Up @@ -117,4 +117,4 @@ METHODS

RELATED LINKS:
Get-TssUser
Get-TssUserStub
New-TssUser
126 changes: 126 additions & 0 deletions src/functions/users/New-User.ps1
@@ -0,0 +1,126 @@
function New-User {
<#
.SYNOPSIS
Create a new Secret Server User
.DESCRIPTION
Create a new Secret Server User
.LINK
https://thycotic-ps.github.io/thycotic.secretserver/commands/New-TssUser
.LINK
https://github.com/thycotic-ps/thycotic.secretserver/blob/main/src/functions/<folder>/New-User.ps1
.NOTES
Requires TssSession object returned by New-TssSession
#>
[CmdletBinding(SupportsShouldProcess)]
[OutputType('TssUser')]
param (
# TssSession object created by New-TssSession for auth
[Parameter(Mandatory,ValueFromPipeline,Position = 0)]
[TssSession]
$TssSession,

# Username
[Parameter(Mandatory,ValueFromPipeline)]
[string]
$Username,

# Display Name
[Parameter(Mandatory,ValueFromPipeline)]
[string]
$DisplayName,

# Password (for local only)
[Parameter(Mandatory)]
[securestring]
$Password,

# Enable the account on creation
[switch]
$Active,

# Create as an application account
[switch]
$IsApplicationAccount,

# Email address
[string]
$EmailAddress,

# Active Directory Domain ID
[ValidateRange(-1,[int]::MaxValue)]
[int]
$DomainId,

# Active Directory GUID
[ValidateLength(36,50)]
[string]
$AdGuid,

# 2FA type
[ValidateSet('DUO', 'FIDO', 'RADIUS', 'OATH')]
[string]
$TwoFactorType,

[string]
$RadiusUsername
)
begin {
$tssNewParams = $PSBoundParameters
$invokeParams = . $GetInvokeTssParams $TssSession
}
process {
Write-Verbose "Provided command parameters: $(. $GetInvocation $PSCmdlet.MyInvocation)"
if ($tssNewParams.ContainsKey('TssSession') -and $TssSession.IsValidSession()) {
. $CheckVersion $TssSession '10.9.000000' $PSCmdlet.MyInvocation
$restResponse = $null
$uri = $TssSession.ApiUrl, 'users' -join '/'
$invokeParams.Uri = $uri
$invokeParams.Method = 'POST'

$newBody = [ordered]@{}
switch ($tssNewParams.Keys) {
'Username' { $newBody.Add('userName',$Username) }
'DisplayName' { $newBody.Add('displayName',$DisplayName) }
'Password' {
$passwd = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($Password))
$newBody.Add('password',$passwd)
}
'Active' { $newBody.Add('enabled',[boolean]$Active) }
'IsApplicationAccount' { $newBody.Add('isApplicationAccount',[boolean]$IsApplicationAccount) }
'EmailAddress' { $newBody.Add('emailAddress',$EmailAddress) }
'DomainId' { $newBody.Add('domainId',$DomainId) }
'AdGuid' { $newBody.Add('adGuid',$AdGuid) }
'TwoFactorType' {
if ($TwoFactorType -eq 'RADIUS' -and -not $tssNewParams.ContainsKey('RadiusUsername')) {
Write-Warning 'Radius Username is required to create a user with RADIUS 2FA'
return
}
$newBody.Add('radiusTwoFactor',$true)
$newBody.Add('radiusUserName',$RadiusUsername)
}
}

$invokeParams.Body = ($newBody | ConvertTo-Json)

Write-Verbose "Performing the operation $($invokeParams.Method) $uri with:`n $newBody"
if (-not $PSCmdlet.ShouldProcess("", "$($invokeParams.Method) $uri with $($invokeParams.Body)")) { return }
try {
$restResponse = . $InvokeApi @invokeParams
} catch {
Write-Warning "Issue creating report [User]"
$err = $_
. $ErrorHandling $err
}

if ($restResponse) {
[TssUser]$restResponse
}
} else {
Write-Warning "No valid session found"
}
}
}
101 changes: 101 additions & 0 deletions tests/users/New-User.Tests.ps1
@@ -0,0 +1,101 @@
BeforeDiscovery {
$commandName = Split-Path ($PSCommandPath.Replace('.Tests.ps1','')) -Leaf
. ([IO.Path]::Combine([string]$PSScriptRoot, '..', 'constants.ps1'))
}
Describe "$commandName verify parameters" {
BeforeDiscovery {
[object[]]$knownParameters = 'TssSession', 'Username', 'DisplayName', 'Password', 'Active', 'IsApplicationAccount', 'EmailAddress', 'DomainId', 'AdGuid', 'TwoFactorType' , 'RadiusUsername'
[object[]]$currentParams = ([Management.Automation.CommandMetaData]$ExecutionContext.SessionState.InvokeCommand.GetCommand($commandName,'Function')).Parameters.Keys
[object[]]$commandDetails = [System.Management.Automation.CommandInfo]$ExecutionContext.SessionState.InvokeCommand.GetCommand($commandName,'Function')
$unknownParameters = Compare-Object -ReferenceObject $knownParameters -DifferenceObject $currentParams -PassThru
}
Context "Verify parameters" -Foreach @{currentParams = $currentParams } {
It "$commandName should contain <_> parameter" -TestCases $knownParameters {
$_ -in $currentParams | Should -Be $true
}
It "$commandName should not contain parameter: <_>" -TestCases $unknownParameters {
$_ | Should -BeNullOrEmpty
}
}
Context "Command specific details" {
It "$commandName should set OutputType to TssUser" -TestCases $commandDetails {
$_.OutputType.Name | Should -Be 'TssUser'
}
}
}
Describe "$commandName functions" {
Context "Checking" {
BeforeAll {
$session = [pscustomobject]@{
ApiVersion = 'api/v1'
Take = 2147483647
SecretServer = 'http://alpha/'
ApiUrl = 'http://alpha/api/v1'
AccessToken = 'AgJf5YLChrisPine312UcBrM1s1KB2BGZ5Ufc4qLZ'
RefreshToken = '9oacYeah0YqgBNg0L7VinDiesel6-Z9ITE51Humus'
TokenType = 'bearer'
ExpiresIn = 1199
}
Mock -Verifiable -CommandName Invoke-RestMethod -ParameterFilter { $Uri -match '/version' } -MockWith {
return @{
model = [pscustomobject]@{
Version = '10.9.000033'
}
}
}

$userId = 42
Mock -Verifiable -CommandName Invoke-RestMethod -ParameterFilter { $Uri -match '/users' } -MockWith {
return [pscustomobject]@{
adAccountExpires = "1970-01-01T00:00:00.000Z"
adGuid = $null
created = "1970-01-01T00:00:00.000Z"
dateOptionId = 0
displayName = "New User"
domainId = 0
duoTwoFactor = $false
emailAddress = "user@new.local"
enabled = $false
fido2TwoFactor = $false
id = $userId
isApplicationAccount = $false
isEmailCopiedFromAD = $false
isEmailVerified = $false
isLockedOut = $false
lastLogin = "1970-01-01T00:00:00.000Z"
lastSessionActivity = "1970-01-01T00:00:00.000Z"
lockOutReason = "string"
lockOutReasonDescription = "string"
loginFailures = 0
mustVerifyEmail = $false
oathTwoFactor = $false
oathVerified = $false
passwordLastChanged = "1970-01-01T00:00:00.000Z"
radiusTwoFactor = $false
radiusUserName = "string"
resetSessionStarted = "1970-01-01T00:00:00.000Z"
timeOptionId = 0
twoFactor = $false
unixAuthenticationMethod = 'Password'
userLcid = 0
userName = "user"
verifyEmailSentDate = "1970-01-01T00:00:00.000Z"
}
}
$object = New-User -TssSession $session -Username 'user' -DisplayName 'New User' -Password (ConvertTo-SecureString 'pass' -AsPlainText -Force) -Email 'user@new.local'
Assert-VerifiableMock
}
It "Should not be empty" {
$object | Should -Not -BeNullOrEmpty
}
It "Should have property <_>" -TestCases 'Id', 'DisplayName', 'EmailAddress' {
$object[0].PSObject.Properties.Name | Should -Contain $_
}
It "Should have property DisplayName equal 'New User'" {
$object.DisplayName | Should -Be 'New User'
}
It "Should have called Invoke-RestMethod 2 times" {
Assert-MockCalled -CommandName Invoke-RestMethod -Times 2 -Scope Describe
}
}
}

0 comments on commit 27b69a7

Please sign in to comment.