Skip to content

Commit 7127901

Browse files
Add interactive API secret setup
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
1 parent f30f681 commit 7127901

3 files changed

Lines changed: 74 additions & 19 deletions

File tree

src/functions/private/Auth/Test-DomeneshopSecret.ps1

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ function Test-DomeneshopSecret {
44
Validate a Domeneshop API secret.
55
66
.DESCRIPTION
7-
Reject empty string and secure string values before credential storage.
7+
Reject unsupported and empty values before credential storage.
88
99
.EXAMPLE
1010
Test-DomeneshopSecret -Secret $secret
@@ -19,10 +19,10 @@ function Test-DomeneshopSecret {
1919
.OUTPUTS
2020
System.Boolean
2121
22-
True when the secret is not empty.
22+
True when the secret has a supported type and is not empty.
2323
2424
.NOTES
25-
Secret type validation remains the responsibility of the calling command.
25+
Supported values are String and SecureString.
2626
#>
2727
[OutputType([bool])]
2828
[CmdletBinding()]
@@ -33,6 +33,10 @@ function Test-DomeneshopSecret {
3333
[object] $Secret
3434
)
3535

36+
if ($Secret -isnot [string] -and $Secret -isnot [securestring]) {
37+
throw 'Secret must be a SecureString or String value.'
38+
}
39+
3640
if (
3741
($Secret -is [string] -and [string]::IsNullOrWhiteSpace($Secret)) -or
3842
($Secret -is [securestring] -and $Secret.Length -eq 0)

src/functions/public/Auth/Connect-DomeneshopAccount.ps1

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,19 @@ function Connect-DomeneshopAccount {
77
88
.DESCRIPTION
99
Stores Domeneshop API credentials using the Context module. The first stored context becomes the
10-
default automatically, and Default replaces an existing default.
10+
default automatically, and Default replaces an existing default. When Secret is omitted, opens the
11+
Domeneshop API settings page and securely prompts for it.
1112
1213
.EXAMPLE
1314
Connect-DomeneshopAccount -Token 'my-token' -Secret (Read-Host -AsSecureString)
1415
1516
Store credentials in the default named context.
1617
18+
.EXAMPLE
19+
Connect-DomeneshopAccount -Token 'my-token'
20+
21+
Open the Domeneshop API settings page and securely prompt for the API secret.
22+
1723
.INPUTS
1824
None
1925
@@ -43,9 +49,10 @@ function Connect-DomeneshopAccount {
4349
[string] $Token,
4450

4551
# The Domeneshop API secret as a string or secure string.
46-
[Parameter(Mandatory)]
52+
[Parameter()]
4753
[ValidateNotNull()]
4854
[ValidateScript({ Test-DomeneshopSecret -Secret $_ })]
55+
[Alias('Key')]
4956
[object] $Secret,
5057

5158
# The name used to store and retrieve this credential context.
@@ -63,22 +70,28 @@ function Connect-DomeneshopAccount {
6370
[switch] $PassThru
6471
)
6572

66-
$secureSecret = switch ($Secret) {
67-
{ $_ -is [securestring] } { $_; break }
68-
{ $_ -is [string] } { ConvertTo-SecureString -AsPlainText $Secret -Force; break }
69-
default { throw 'Secret must be a SecureString or String value.' }
70-
}
73+
if ($PSCmdlet.ShouldProcess("Domeneshop context [$Context]", 'Store API credentials')) {
74+
if (-not $PSBoundParameters.ContainsKey('Secret')) {
75+
Start-Process -FilePath 'https://domene.shop/admin?view=api'
76+
$Secret = Read-Host -Prompt 'Enter the Domeneshop API secret' -AsSecureString
77+
$null = Test-DomeneshopSecret -Secret $Secret
78+
}
7179

72-
$contextObject = [ordered]@{
73-
Name = $Context
74-
Token = $Token
75-
Secret = $secureSecret
76-
AuthType = 'BasicAuth'
77-
ApiBaseUri = 'https://api.domeneshop.no/v0'
78-
ConnectedAt = Get-Date
79-
}
80+
$secureSecret = if ($Secret -is [securestring]) {
81+
$Secret
82+
} else {
83+
ConvertTo-SecureString -AsPlainText $Secret -Force
84+
}
85+
86+
$contextObject = [ordered]@{
87+
Name = $Context
88+
Token = $Token
89+
Secret = $secureSecret
90+
AuthType = 'BasicAuth'
91+
ApiBaseUri = 'https://api.domeneshop.no/v0'
92+
ConnectedAt = Get-Date
93+
}
8094

81-
if ($PSCmdlet.ShouldProcess("Domeneshop context [$Context]", 'Store API credentials')) {
8295
$null = Set-Context -ID $Context -Vault 'Domeneshop' -Context $contextObject
8396

8497
$config = Get-DomeneshopConfig

tests/Connect-DomeneshopAccount.Tests.ps1

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ Describe 'Connect-DomeneshopAccount' {
2121
Mock Get-DomeneshopConfig { [pscustomobject]@{ DefaultContext = $null } }
2222
Mock Set-DomeneshopDefaultContext {}
2323
Mock Get-DomeneshopContext { [pscustomobject]@{ ID = 'demo' } }
24+
Mock Start-Process {}
25+
Mock Read-Host { $script:PromptedSecret }
26+
$script:PromptedSecret = [securestring]::new()
27+
'prompted-secret'.ToCharArray() | ForEach-Object { $script:PromptedSecret.AppendChar($_) }
2428
}
2529

2630
It 'stores credentials securely and sets the first context as default' {
@@ -44,6 +48,40 @@ Describe 'Connect-DomeneshopAccount' {
4448
Should -Invoke Set-DomeneshopDefaultContext -Times 0 -Exactly
4549
}
4650

51+
It 'opens API settings and securely prompts when Secret is omitted' {
52+
Connect-DomeneshopAccount -Token 'token' -Context 'demo'
53+
54+
Should -Invoke Start-Process -Times 1 -Exactly -ParameterFilter {
55+
$FilePath -eq 'https://domene.shop/admin?view=api'
56+
}
57+
Should -Invoke Read-Host -Times 1 -Exactly -ParameterFilter {
58+
$Prompt -eq 'Enter the Domeneshop API secret' -and $AsSecureString
59+
}
60+
Should -Invoke Set-Context -Times 1 -Exactly -ParameterFilter {
61+
$ID -eq 'demo' -and
62+
$Vault -eq 'Domeneshop' -and
63+
[object]::ReferenceEquals($Context.Secret, $script:PromptedSecret)
64+
}
65+
}
66+
67+
It 'does not open API settings or prompt when WhatIf omits Secret' {
68+
Connect-DomeneshopAccount -Token 'token' -Context 'demo' -WhatIf
69+
70+
Should -Invoke Start-Process -Times 0 -Exactly
71+
Should -Invoke Read-Host -Times 0 -Exactly
72+
Should -Invoke Set-Context -Times 0 -Exactly
73+
}
74+
75+
It 'accepts Key as an alias for an explicit secure secret' {
76+
Connect-DomeneshopAccount -Token 'token' -Key $script:PromptedSecret -Context 'demo'
77+
78+
Should -Invoke Start-Process -Times 0 -Exactly
79+
Should -Invoke Read-Host -Times 0 -Exactly
80+
Should -Invoke Set-Context -Times 1 -Exactly -ParameterFilter {
81+
[object]::ReferenceEquals($Context.Secret, $script:PromptedSecret)
82+
}
83+
}
84+
4785
It 'rejects unsupported secret types' {
4886
{ Connect-DomeneshopAccount -Token 'token' -Secret 42 -Context 'demo' } |
4987
Should -Throw '*Secret must be a SecureString or String value*'

0 commit comments

Comments
 (0)