From 7b8a2cd464f4b84ab3f720bde89ce2831e46e40b Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 24 Sep 2023 13:21:44 +0200 Subject: [PATCH 01/45] Added a function to manage API calls. --- src/GitHub/public/API/Invoke-GitHubAPI.ps1 | 109 +++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 src/GitHub/public/API/Invoke-GitHubAPI.ps1 diff --git a/src/GitHub/public/API/Invoke-GitHubAPI.ps1 b/src/GitHub/public/API/Invoke-GitHubAPI.ps1 new file mode 100644 index 000000000..d1ec66ccb --- /dev/null +++ b/src/GitHub/public/API/Invoke-GitHubAPI.ps1 @@ -0,0 +1,109 @@ +function Invoke-GitHubAPI { + [CmdletBinding(DefaultParameterSetName = 'Body')] + param ( + [Parameter()] + [ValidateSet('GET', 'POST', 'PATCH', 'DELETE', 'PUT')] + [String] $Method = 'GET', + + [Parameter()] + [string] $APIBaseURI = $script:Config.App.API.BaseURI, + + [Parameter(Mandatory)] + [string] $APIEndpoint, + + [Parameter(ParameterSetName = 'Body')] + [hashtable] $Body, + + [Parameter(ParameterSetName = 'Data')] + [string] $Data, + + [Parameter()] + [string] $Accept, + + [Parameter()] + [string] $AccessToken = $script:Config.User.Auth.AccessToken.Value, + + [Parameter()] + [string] $ContentType = 'application/vnd.github+json', + + [Parameter()] + [string] $Version = $script:Config.App.API.Version, + + [Parameter()] + [switch] $UseWebRequest + ) + + $headers = @{} + + if (-not [string]::IsNullOrEmpty($ContentType)) { + $headers += @{ + 'Content-Type' = $ContentType + } + } + + if (-not [string]::IsNullOrEmpty($Version)) { + $headers += @{ + 'X-GitHub-Api-Version' = $Version + } + } + + if (-not [string]::IsNullOrEmpty($Accept)) { + $headers += @{ + 'Accept' = $Accept + } + } + + if (-not [string]::IsNullOrEmpty($AccessToken)) { + switch -Regex ($AccessToken) { + '^ghp_' { + $authorization = "token $AccessToken" # Classic tokens + } + '^github_pat_' { + $authorization = "token $AccessToken" # Fine-grained PAT + } + '^ghu_' { + $authorization = "Bearer $AccessToken" # GitHubApp access token + } + '^gho_' { + $authorization = "Bearer $AccessToken" # OAuth app access token + } + } + + $headers += @{ + Authorization = $authorization + } + } + + $URI = "$APIBaseURI$($APIEndpoint.Replace('\', '/').Replace('//', '/'))" + + $APICall = @{ + Uri = $URI + Method = $Method + Headers = $Headers + } + + if ($PSBoundParameters.ContainsKey('Body')) { + $APICall += @{ + Body = ($Body | ConvertTo-Json -Depth 100) + } + } + + if ($PSBoundParameters.ContainsKey('Data')) { + $APICall += @{ + Body = $Data + } + } + + try { + Write-Verbose ($APICall.GetEnumerator() | Out-String) + + if ($UseWebRequest) { + return Invoke-WebRequest @APICall + } + + Invoke-RestMethod @APICall + } catch { + Write-Error $_ + throw $_ + } +} From 2ac2b75bf93959306f9a36ed447ef19735a770d7 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 24 Sep 2023 13:26:47 +0200 Subject: [PATCH 02/45] Suggestions to refactor --- src/GitHub/public/API/Invoke-GitHubAPI.ps1 | 59 ++++++++-------------- 1 file changed, 20 insertions(+), 39 deletions(-) diff --git a/src/GitHub/public/API/Invoke-GitHubAPI.ps1 b/src/GitHub/public/API/Invoke-GitHubAPI.ps1 index d1ec66ccb..00c19b09e 100644 --- a/src/GitHub/public/API/Invoke-GitHubAPI.ps1 +++ b/src/GitHub/public/API/Invoke-GitHubAPI.ps1 @@ -6,10 +6,10 @@ [String] $Method = 'GET', [Parameter()] - [string] $APIBaseURI = $script:Config.App.API.BaseURI, + [string] $ApiBaseUri = $script:Config.App.API.BaseURI, [Parameter(Mandatory)] - [string] $APIEndpoint, + [string] $ApiEndpoint, [Parameter(ParameterSetName = 'Body')] [hashtable] $Body, @@ -33,48 +33,33 @@ [switch] $UseWebRequest ) - $headers = @{} - - if (-not [string]::IsNullOrEmpty($ContentType)) { - $headers += @{ - 'Content-Type' = $ContentType - } - } - - if (-not [string]::IsNullOrEmpty($Version)) { - $headers += @{ - 'X-GitHub-Api-Version' = $Version - } + $headers = @{ + 'Content-Type' = $ContentType + 'X-GitHub-Api-Version' = $Version + 'Accept' = $Accept } - if (-not [string]::IsNullOrEmpty($Accept)) { - $headers += @{ - 'Accept' = $Accept + # Remove null or empty headers + $headers.GetEnumerator() | ForEach-Object { + if ([string]::IsNullOrEmpty($_.Value)) { + $headers.Remove($_.Key) } } if (-not [string]::IsNullOrEmpty($AccessToken)) { switch -Regex ($AccessToken) { - '^ghp_' { - $authorization = "token $AccessToken" # Classic tokens - } - '^github_pat_' { - $authorization = "token $AccessToken" # Fine-grained PAT + '^gh[pou]_' { + $authorization = "token $AccessToken" } '^ghu_' { $authorization = "Bearer $AccessToken" # GitHubApp access token } - '^gho_' { - $authorization = "Bearer $AccessToken" # OAuth app access token - } } - $headers += @{ - Authorization = $authorization - } + $headers['Authorization'] = $authorization } - $URI = "$APIBaseURI$($APIEndpoint.Replace('\', '/').Replace('//', '/'))" + $URI = "$ApiBaseUri/$($ApiEndpoint.TrimStart('/'))" $APICall = @{ Uri = $URI @@ -82,16 +67,11 @@ Headers = $Headers } + # Set body from either Body or Data parameter if ($PSBoundParameters.ContainsKey('Body')) { - $APICall += @{ - Body = ($Body | ConvertTo-Json -Depth 100) - } - } - - if ($PSBoundParameters.ContainsKey('Data')) { - $APICall += @{ - Body = $Data - } + $APICall['Body'] = ($Body | ConvertTo-Json -Depth 100) + } elseif ($PSBoundParameters.ContainsKey('Data')) { + $APICall['Body'] = $Data } try { @@ -103,7 +83,8 @@ Invoke-RestMethod @APICall } catch { - Write-Error $_ + $errorMessage = "Error calling GitHub API: $($_.Exception.Message)" + Write-Error $errorMessage throw $_ } } From c0c63777008a105c4d3f8c3680ad97444bd8f603 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 24 Sep 2023 13:30:54 +0200 Subject: [PATCH 03/45] Suggestions --- src/GitHub/public/API/Invoke-GitHubAPI.ps1 | 26 ++++++++++------------ 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/GitHub/public/API/Invoke-GitHubAPI.ps1 b/src/GitHub/public/API/Invoke-GitHubAPI.ps1 index 00c19b09e..e918a2171 100644 --- a/src/GitHub/public/API/Invoke-GitHubAPI.ps1 +++ b/src/GitHub/public/API/Invoke-GitHubAPI.ps1 @@ -1,21 +1,18 @@ function Invoke-GitHubAPI { - [CmdletBinding(DefaultParameterSetName = 'Body')] + [CmdletBinding()] param ( [Parameter()] [ValidateSet('GET', 'POST', 'PATCH', 'DELETE', 'PUT')] [String] $Method = 'GET', [Parameter()] - [string] $ApiBaseUri = $script:Config.App.API.BaseURI, + [string] $ApiBaseUri = $script:Config.App.Api.BaseURI, [Parameter(Mandatory)] [string] $ApiEndpoint, - [Parameter(ParameterSetName = 'Body')] - [hashtable] $Body, - - [Parameter(ParameterSetName = 'Data')] - [string] $Data, + [Parameter()] + [Object] $Body, [Parameter()] [string] $Accept, @@ -27,7 +24,7 @@ [string] $ContentType = 'application/vnd.github+json', [Parameter()] - [string] $Version = $script:Config.App.API.Version, + [string] $Version = $script:Config.App.Api.Version, [Parameter()] [switch] $UseWebRequest @@ -59,7 +56,8 @@ $headers['Authorization'] = $authorization } - $URI = "$ApiBaseUri/$($ApiEndpoint.TrimStart('/'))" + # Avoid replacing 'https://' slashes while ensuring correct URL formation + $URI = "$ApiBaseUri".TrimEnd('/') + "/$ApiEndpoint".TrimStart('/') $APICall = @{ Uri = $URI @@ -67,11 +65,11 @@ Headers = $Headers } - # Set body from either Body or Data parameter - if ($PSBoundParameters.ContainsKey('Body')) { + # Set body depending on the type of Body (string or hashtable) + if ($Body -is [string]) { + $APICall['Body'] = $Body + } elseif ($Body -is [hashtable]) { $APICall['Body'] = ($Body | ConvertTo-Json -Depth 100) - } elseif ($PSBoundParameters.ContainsKey('Data')) { - $APICall['Body'] = $Data } try { @@ -83,7 +81,7 @@ Invoke-RestMethod @APICall } catch { - $errorMessage = "Error calling GitHub API: $($_.Exception.Message)" + $errorMessage = "Error calling GitHub Api: $($_.Exception.Message)" Write-Error $errorMessage throw $_ } From bbf55474bab471d8a5f068ed5b161e005fc49544 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 24 Sep 2023 13:38:33 +0200 Subject: [PATCH 04/45] Simplification --- src/GitHub/public/API/Invoke-GitHubAPI.ps1 | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/GitHub/public/API/Invoke-GitHubAPI.ps1 b/src/GitHub/public/API/Invoke-GitHubAPI.ps1 index e918a2171..9e6ff8443 100644 --- a/src/GitHub/public/API/Invoke-GitHubAPI.ps1 +++ b/src/GitHub/public/API/Invoke-GitHubAPI.ps1 @@ -45,11 +45,27 @@ if (-not [string]::IsNullOrEmpty($AccessToken)) { switch -Regex ($AccessToken) { - '^gh[pou]_' { - $authorization = "token $AccessToken" + '^ghp_' { + $authorization = "token $AccessToken" # Classic tokens + break + } + '^github_pat_' { + $authorization = "token $AccessToken" # Fine-grained PAT + break } '^ghu_' { $authorization = "Bearer $AccessToken" # GitHubApp access token + break + } + '^gho_' { + $authorization = "Bearer $AccessToken" # OAuth app access token + break + } + default { + $tokenPrefix = $AccessToken.Substring(0, $AccessToken.LastIndexOf('_') + 1) + $errorMessage = "Unexpected AccessToken format: $tokenPrefix*" + Write-Error $errorMessage + throw $errorMessage } } From e43569f8287fae544c79ea76380ce44a9bb75d56 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 24 Sep 2023 13:47:55 +0200 Subject: [PATCH 05/45] Suggestions --- src/GitHub/public/API/Invoke-GitHubAPI.ps1 | 61 +++++++++------------- 1 file changed, 25 insertions(+), 36 deletions(-) diff --git a/src/GitHub/public/API/Invoke-GitHubAPI.ps1 b/src/GitHub/public/API/Invoke-GitHubAPI.ps1 index 9e6ff8443..493bbaae5 100644 --- a/src/GitHub/public/API/Invoke-GitHubAPI.ps1 +++ b/src/GitHub/public/API/Invoke-GitHubAPI.ps1 @@ -36,44 +36,35 @@ 'Accept' = $Accept } - # Remove null or empty headers - $headers.GetEnumerator() | ForEach-Object { - if ([string]::IsNullOrEmpty($_.Value)) { - $headers.Remove($_.Key) - } + # Filter out null or empty headers + $headers = $headers.GetEnumerator() | Where-Object { -not [string]::IsNullOrEmpty($_.Value) } | ForEach-Object { + @{ $_.Key = $_.Value } } - if (-not [string]::IsNullOrEmpty($AccessToken)) { - switch -Regex ($AccessToken) { - '^ghp_' { - $authorization = "token $AccessToken" # Classic tokens - break - } - '^github_pat_' { - $authorization = "token $AccessToken" # Fine-grained PAT - break - } - '^ghu_' { - $authorization = "Bearer $AccessToken" # GitHubApp access token - break - } - '^gho_' { - $authorization = "Bearer $AccessToken" # OAuth app access token - break - } - default { - $tokenPrefix = $AccessToken.Substring(0, $AccessToken.LastIndexOf('_') + 1) - $errorMessage = "Unexpected AccessToken format: $tokenPrefix*" + switch -Regex ($AccessToken) { + '^ghp_|^github_pat_' { + $authorization = "token $AccessToken" + break + } + '^ghu_|^gho_' { + $authorization = "Bearer $AccessToken" + break + } + default { + if (-not [string]::IsNullOrEmpty($AccessToken)) { + $tokenPrefix = $AccessToken -replace '_.*$', '_*' + $errorMessage = "Unexpected AccessToken format: $tokenPrefix" Write-Error $errorMessage throw $errorMessage } } + } + if ($null -ne $authorization) { $headers['Authorization'] = $authorization } - # Avoid replacing 'https://' slashes while ensuring correct URL formation - $URI = "$ApiBaseUri".TrimEnd('/') + "/$ApiEndpoint".TrimStart('/') + $URI = ("$ApiBaseUri/" -replace '/$', '') + ("/$ApiEndpoint" -replace '^/', '') $APICall = @{ Uri = $URI @@ -81,20 +72,18 @@ Headers = $Headers } - # Set body depending on the type of Body (string or hashtable) - if ($Body -is [string]) { - $APICall['Body'] = $Body - } elseif ($Body -is [hashtable]) { - $APICall['Body'] = ($Body | ConvertTo-Json -Depth 100) + if ($Body) { + if ($Body -is [string]) { + $APICall['Body'] = $Body + } else { + $APICall['Body'] = $Body | ConvertTo-Json -Depth 100 + } } try { - Write-Verbose ($APICall.GetEnumerator() | Out-String) - if ($UseWebRequest) { return Invoke-WebRequest @APICall } - Invoke-RestMethod @APICall } catch { $errorMessage = "Error calling GitHub Api: $($_.Exception.Message)" From 0709f49da0ff66d5afa3dedba628103234df11ce Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 24 Sep 2023 13:48:46 +0200 Subject: [PATCH 06/45] suggestions --- src/GitHub/public/API/Invoke-GitHubAPI.ps1 | 25 +++++++++------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/src/GitHub/public/API/Invoke-GitHubAPI.ps1 b/src/GitHub/public/API/Invoke-GitHubAPI.ps1 index 493bbaae5..08dc6191f 100644 --- a/src/GitHub/public/API/Invoke-GitHubAPI.ps1 +++ b/src/GitHub/public/API/Invoke-GitHubAPI.ps1 @@ -6,7 +6,7 @@ [String] $Method = 'GET', [Parameter()] - [string] $ApiBaseUri = $script:Config.App.Api.BaseURI, + [string] $ApiBaseUri = $script:Config.App.Api.BaseUri, [Parameter(Mandatory)] [string] $ApiEndpoint, @@ -41,26 +41,21 @@ @{ $_.Key = $_.Value } } - switch -Regex ($AccessToken) { - '^ghp_|^github_pat_' { - $authorization = "token $AccessToken" - break - } - '^ghu_|^gho_' { - $authorization = "Bearer $AccessToken" - break - } - default { - if (-not [string]::IsNullOrEmpty($AccessToken)) { + if (-not [string]::IsNullOrEmpty($AccessToken)) { + switch -Regex ($AccessToken) { + '^ghp_|^github_pat_' { + $authorization = "token $AccessToken" + } + '^ghu_|^gho_' { + $authorization = "Bearer $AccessToken" + } + default { $tokenPrefix = $AccessToken -replace '_.*$', '_*' $errorMessage = "Unexpected AccessToken format: $tokenPrefix" Write-Error $errorMessage throw $errorMessage } } - } - - if ($null -ne $authorization) { $headers['Authorization'] = $authorization } From 618e27f96ef8f9f2605a08c88a729a30cf9ef66e Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 24 Sep 2023 14:31:48 +0200 Subject: [PATCH 07/45] Added built in autocomplete for HTTP Token is now a secure string using BSTR clearing. --- src/GitHub/public/API/Invoke-GitHubAPI.ps1 | 27 +++++++++++++++------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/src/GitHub/public/API/Invoke-GitHubAPI.ps1 b/src/GitHub/public/API/Invoke-GitHubAPI.ps1 index 08dc6191f..edbf2383b 100644 --- a/src/GitHub/public/API/Invoke-GitHubAPI.ps1 +++ b/src/GitHub/public/API/Invoke-GitHubAPI.ps1 @@ -2,8 +2,7 @@ [CmdletBinding()] param ( [Parameter()] - [ValidateSet('GET', 'POST', 'PATCH', 'DELETE', 'PUT')] - [String] $Method = 'GET', + [Microsoft.PowerShell.Commands.WebRequestMethod] $Method = 'GET', [Parameter()] [string] $ApiBaseUri = $script:Config.App.Api.BaseUri, @@ -18,7 +17,7 @@ [string] $Accept, [Parameter()] - [string] $AccessToken = $script:Config.User.Auth.AccessToken.Value, + [SecureString] $SecureToken = $script:Config.User.Auth.AccessToken.Value, [Parameter()] [string] $ContentType = 'application/vnd.github+json', @@ -30,6 +29,11 @@ [switch] $UseWebRequest ) + # Decrypting the secure token + $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($SecureToken) + $AccessToken = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR) + [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($BSTR) # Clear out the plain text from memory + $headers = @{ 'Content-Type' = $ContentType 'X-GitHub-Api-Version' = $Version @@ -76,13 +80,20 @@ } try { - if ($UseWebRequest) { - return Invoke-WebRequest @APICall + if ($UseWebrequest) { + $response = Invoke-WebRequest @APICall + $outputObject = [PSCustomObject]@{ + Data = $response.Content | ConvertFrom-Json + WebRequestDetails = $response + } + return $outputObject } - Invoke-RestMethod @APICall + return Invoke-RestMethod @APICall + } catch [System.Net.WebException] { + Write-Error "[Invoke-GitHubAPI] - WebException - $($_.Exception.Message)" + throw $_ } catch { - $errorMessage = "Error calling GitHub Api: $($_.Exception.Message)" - Write-Error $errorMessage + Write-Error "[Invoke-GitHubAPI] - GeneralException - $($_.Exception.Message)" throw $_ } } From 235c759953532137bead44184985b3380ca0c889 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 24 Sep 2023 14:34:54 +0200 Subject: [PATCH 08/45] test --- src/GitHub/public/API/Invoke-GitHubAPI.ps1 | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/src/GitHub/public/API/Invoke-GitHubAPI.ps1 b/src/GitHub/public/API/Invoke-GitHubAPI.ps1 index edbf2383b..37b60dd1d 100644 --- a/src/GitHub/public/API/Invoke-GitHubAPI.ps1 +++ b/src/GitHub/public/API/Invoke-GitHubAPI.ps1 @@ -23,10 +23,7 @@ [string] $ContentType = 'application/vnd.github+json', [Parameter()] - [string] $Version = $script:Config.App.Api.Version, - - [Parameter()] - [switch] $UseWebRequest + [string] $Version = $script:Config.App.Api.Version ) # Decrypting the secure token @@ -80,15 +77,7 @@ } try { - if ($UseWebrequest) { - $response = Invoke-WebRequest @APICall - $outputObject = [PSCustomObject]@{ - Data = $response.Content | ConvertFrom-Json - WebRequestDetails = $response - } - return $outputObject - } - return Invoke-RestMethod @APICall + Invoke-RestMethod @APICall } catch [System.Net.WebException] { Write-Error "[Invoke-GitHubAPI] - WebException - $($_.Exception.Message)" throw $_ From 97a1e8fd7bbbeae6c3927611379523677670b9b7 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 24 Sep 2023 14:46:34 +0200 Subject: [PATCH 09/45] added docs --- src/GitHub/public/API/Invoke-GitHubAPI.ps1 | 31 ++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/GitHub/public/API/Invoke-GitHubAPI.ps1 b/src/GitHub/public/API/Invoke-GitHubAPI.ps1 index 37b60dd1d..5cd6ff0f9 100644 --- a/src/GitHub/public/API/Invoke-GitHubAPI.ps1 +++ b/src/GitHub/public/API/Invoke-GitHubAPI.ps1 @@ -1,27 +1,58 @@ function Invoke-GitHubAPI { + <# + .SYNOPSIS + Calls the GitHub API using the provided parameters. + + .DESCRIPTION + This function is a wrapper around Invoke-RestMethod tailored for calling GitHub's API. + It automatically handles the endpoint URI construction, headers, and token authentication. + + .EXAMPLE + Invoke-GitHubAPI -ApiEndpoint '/repos/user/repo/pulls' -Method GET + + Gets all open pull requests for the specified repository. + + .EXAMPLE + Invoke-GitHubAPI -ApiEndpoint '/repos/user/repo/pulls' -Method GET -Body @{ state = 'open' } + + Gets all open pull requests for the specified repository, filtered by the 'state' parameter. + + .EXAMPLE + Invoke-GitHubAPI -ApiEndpoint '/repos/user/repo/pulls' -Method GET -Body @{ state = 'open' } -Accept 'application/vnd.github.v3+json' + + Gets all open pull requests for the specified repository, filtered by the 'state' parameter, and using the specified 'Accept' header. +#> [CmdletBinding()] param ( + # The HTTP method to be used for the API request. It can be one of the following: GET, POST, PUT, DELETE, or PATCH. [Parameter()] [Microsoft.PowerShell.Commands.WebRequestMethod] $Method = 'GET', + # The base URI for the GitHub API. This is usually 'https://api.github.com', but can be adjusted if necessary. [Parameter()] [string] $ApiBaseUri = $script:Config.App.Api.BaseUri, + # The specific endpoint for the API call, e.g., '/repos/user/repo/pulls'. [Parameter(Mandatory)] [string] $ApiEndpoint, + # The body of the API request. This can be a hashtable or a string. If a hashtable is provided, it will be converted to JSON. [Parameter()] [Object] $Body, + # The 'Accept' header for the API request. If not provided, the default will be used by GitHub's API. [Parameter()] [string] $Accept, + # The secure token used for authentication in the GitHub API. It should be stored as a SecureString to ensure it's kept safe in memory. [Parameter()] [SecureString] $SecureToken = $script:Config.User.Auth.AccessToken.Value, + # The 'Content-Type' header for the API request. The default is 'application/vnd.github+json'. [Parameter()] [string] $ContentType = 'application/vnd.github+json', + # The GitHub API version to be used. By default, it pulls from a configuration script variable. [Parameter()] [string] $Version = $script:Config.App.Api.Version ) From 5d5dd2379a29b1a657b5c4b9facd02a778267a72 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 24 Sep 2023 14:51:58 +0200 Subject: [PATCH 10/45] Soon done :) --- src/GitHub/public/API/Invoke-GitHubAPI.ps1 | 27 ++++++++++------------ 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/src/GitHub/public/API/Invoke-GitHubAPI.ps1 b/src/GitHub/public/API/Invoke-GitHubAPI.ps1 index 5cd6ff0f9..a6052321a 100644 --- a/src/GitHub/public/API/Invoke-GitHubAPI.ps1 +++ b/src/GitHub/public/API/Invoke-GitHubAPI.ps1 @@ -46,7 +46,7 @@ # The secure token used for authentication in the GitHub API. It should be stored as a SecureString to ensure it's kept safe in memory. [Parameter()] - [SecureString] $SecureToken = $script:Config.User.Auth.AccessToken.Value, + [SecureString] $AccessToken = $script:Config.User.Auth.AccessToken.Value, # The 'Content-Type' header for the API request. The default is 'application/vnd.github+json'. [Parameter()] @@ -57,11 +57,6 @@ [string] $Version = $script:Config.App.Api.Version ) - # Decrypting the secure token - $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($SecureToken) - $AccessToken = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR) - [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($BSTR) # Clear out the plain text from memory - $headers = @{ 'Content-Type' = $ContentType 'X-GitHub-Api-Version' = $Version @@ -73,22 +68,24 @@ @{ $_.Key = $_.Value } } - if (-not [string]::IsNullOrEmpty($AccessToken)) { - switch -Regex ($AccessToken) { - '^ghp_|^github_pat_' { - $authorization = "token $AccessToken" - } - '^ghu_|^gho_' { - $authorization = "Bearer $AccessToken" - } + # Authorization handling + $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($AccessToken) + try { + $AccessTokenAsPlainText = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR) + + $authorization = switch -Regex ($AccessTokenAsPlainText) { + '^ghp_|^github_pat_' { "token $AccessTokenAsPlainText" } + '^ghu_|^gho_' { "Bearer $AccessTokenAsPlainText" } default { - $tokenPrefix = $AccessToken -replace '_.*$', '_*' + $tokenPrefix = $AccessTokenAsPlainText -replace '_.*$', '_*' $errorMessage = "Unexpected AccessToken format: $tokenPrefix" Write-Error $errorMessage throw $errorMessage } } $headers['Authorization'] = $authorization + } finally { + [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($BSTR) } $URI = ("$ApiBaseUri/" -replace '/$', '') + ("/$ApiEndpoint" -replace '^/', '') From 2cef822c169200205478ace6b04fc5db7e6bf7c1 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 24 Sep 2023 15:11:40 +0200 Subject: [PATCH 11/45] pagination first attempt --- src/GitHub/public/API/Invoke-GitHubAPI.ps1 | 27 +++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/src/GitHub/public/API/Invoke-GitHubAPI.ps1 b/src/GitHub/public/API/Invoke-GitHubAPI.ps1 index a6052321a..a46512df4 100644 --- a/src/GitHub/public/API/Invoke-GitHubAPI.ps1 +++ b/src/GitHub/public/API/Invoke-GitHubAPI.ps1 @@ -57,6 +57,8 @@ [string] $Version = $script:Config.App.Api.Version ) + $functionName = $MyInvocation.MyCommand.Name + $headers = @{ 'Content-Type' = $ContentType 'X-GitHub-Api-Version' = $Version @@ -105,12 +107,31 @@ } try { - Invoke-RestMethod @APICall + $allResults = @() + do { + Invoke-RestMethod @APICall + + $allResults += $response + + # Check for pagination links + $nextLink = ($response.Headers.Link -split ',') | Where-Object { + $_ -like '*rel="next"*' + } | ForEach-Object { + # Extract the URL for the next page + ($_ -split '<|>')[1] + } + + if ($nextLink) { + $APICall.Uri = $nextLink + } + + } while ($nextLink) + $allResults } catch [System.Net.WebException] { - Write-Error "[Invoke-GitHubAPI] - WebException - $($_.Exception.Message)" + Write-Error "[$functionName] - WebException - $($_.Exception.Message)" throw $_ } catch { - Write-Error "[Invoke-GitHubAPI] - GeneralException - $($_.Exception.Message)" + Write-Error "[$functionName] - GeneralException - $($_.Exception.Message)" throw $_ } } From e9ec3186585b746eb18ba12bc3b2edd86055fef0 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 24 Sep 2023 15:16:07 +0200 Subject: [PATCH 12/45] pagination --- src/GitHub/public/API/Invoke-GitHubAPI.ps1 | 29 ++++++++++++++-------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/src/GitHub/public/API/Invoke-GitHubAPI.ps1 b/src/GitHub/public/API/Invoke-GitHubAPI.ps1 index a46512df4..4da8a6805 100644 --- a/src/GitHub/public/API/Invoke-GitHubAPI.ps1 +++ b/src/GitHub/public/API/Invoke-GitHubAPI.ps1 @@ -109,16 +109,23 @@ try { $allResults = @() do { - Invoke-RestMethod @APICall - - $allResults += $response + $response = Invoke-RestMethod @APICall + + # Parse Data + if ($response -is [System.Array]) { + $allResults += $response + } elseif ($response) { + $response.PSObject.Properties | Where-Object { + $_.Name -notin @('incomplete_results', 'repository_selection', 'total_count') + } | ForEach-Object { + $allResults += $_.Value + } + } - # Check for pagination links - $nextLink = ($response.Headers.Link -split ',') | Where-Object { - $_ -like '*rel="next"*' - } | ForEach-Object { - # Extract the URL for the next page - ($_ -split '<|>')[1] + # Extract next page's URL from Link header if exists + $nextLink = $null + if ($response.Headers.Link -match '<(?[^>]+)>;\s*rel="next"') { + $nextLink = $matches['url'] } if ($nextLink) { @@ -126,7 +133,9 @@ } } while ($nextLink) - $allResults + + return $allResults + } catch [System.Net.WebException] { Write-Error "[$functionName] - WebException - $($_.Exception.Message)" throw $_ From b95ac43eb0e487b1affcc730e843aeefcdfb8d3b Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 24 Sep 2023 15:18:53 +0200 Subject: [PATCH 13/45] Stream output to pipeline --- src/GitHub/public/API/Invoke-GitHubAPI.ps1 | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/GitHub/public/API/Invoke-GitHubAPI.ps1 b/src/GitHub/public/API/Invoke-GitHubAPI.ps1 index 4da8a6805..ab3007246 100644 --- a/src/GitHub/public/API/Invoke-GitHubAPI.ps1 +++ b/src/GitHub/public/API/Invoke-GitHubAPI.ps1 @@ -107,18 +107,19 @@ } try { - $allResults = @() do { $response = Invoke-RestMethod @APICall # Parse Data if ($response -is [System.Array]) { - $allResults += $response + $response | ForEach-Object { + Write-Output $_ + } } elseif ($response) { $response.PSObject.Properties | Where-Object { $_.Name -notin @('incomplete_results', 'repository_selection', 'total_count') } | ForEach-Object { - $allResults += $_.Value + Write-Output $_.Value } } @@ -134,8 +135,6 @@ } while ($nextLink) - return $allResults - } catch [System.Net.WebException] { Write-Error "[$functionName] - WebException - $($_.Exception.Message)" throw $_ From eba91202e1af5cc57a836e6f2dfe771e01b46b98 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 24 Sep 2023 15:45:14 +0200 Subject: [PATCH 14/45] Updates to the loader --- src/GitHub/GitHub.psm1 | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/GitHub/GitHub.psm1 b/src/GitHub/GitHub.psm1 index bbac83a75..eec39f2b3 100644 --- a/src/GitHub/GitHub.psm1 +++ b/src/GitHub/GitHub.psm1 @@ -6,28 +6,27 @@ Write-Verbose "[$scriptName] - Importing module" #region - Importing data files Write-Verbose "[$scriptName] - [data] - Processing folder" -$dataFolder = (Join-Path $PSScriptRoot 'data') -Write-Verbose "[$scriptName] - [data] - [$dataFolder]" -Get-ChildItem -Path "$dataFolder" -Recurse -Force -Include '*.psd1' | ForEach-Object { +$dataFolder = Join-Path $PSScriptRoot 'data' +Get-ChildItem -Path $dataFolder -Recurse -Force -Include '*.psd1' | ForEach-Object { Write-Verbose "[$scriptName] - [data] - [$($_.Name)] - Importing data file" New-Variable -Name $_.BaseName -Value (Import-PowerShellDataFile -Path $_.FullName) -Force Write-Verbose "[$scriptName] - [data] - [$($_.Name)] - Done" } Write-Verbose "[$scriptName] - [data] - Done" -#endregion - Importing datas +#endregion - Importing data files #region - Importing script files $folders = 'init', 'classes', 'private', 'public' foreach ($folder in $folders) { Write-Verbose "[$scriptName] - [$folder] - Processing folder" - $folderPath = Join-Path -Path $PSScriptRoot -ChildPath $folder + $folderPath = Join-Path $PSScriptRoot $folder if (Test-Path -Path $folderPath) { - $files = Get-ChildItem -Path $folderPath -Include '*.ps1', '*.psm1' -Recurse | Sort-Object -Property FullName - foreach ($file in $files) { - Write-Verbose "[$scriptName] - [$folder] - [$($file.Name)] - Importing script file" - Import-Module $file -Verbose:$false - Write-Verbose "[$scriptName] - [$folder] - [$($file.Name)] - Done" - } + Get-ChildItem -Path $folderPath -Include '*.ps1', '*.psm1' -Recurse | + Sort-Object -Property FullName | ForEach-Object { + Write-Verbose "[$scriptName] - [$folder] - [$($_.Name)] - Importing script file" + Import-Module $_ -Verbose:$false + Write-Verbose "[$scriptName] - [$folder] - [$($_.Name)] - Done" + } } Write-Verbose "[$scriptName] - [$folder] - Done" } @@ -44,7 +43,7 @@ Write-Verbose "[$scriptName] - [Root] - Done" #endregion - Importing root script files #region Export module members -$foldersToProcess = Get-ChildItem -Path $PSScriptRoot -Directory | Where-Object -Property Name -In $folders +$foldersToProcess = Get-ChildItem -Path $PSScriptRoot -Directory | Where-Object Name -In $folders $moduleFiles = $foldersToProcess | Get-ChildItem -Include '*.ps1' -Recurse -File -Force $functions = $moduleFiles.BaseName $param = @{ @@ -54,9 +53,9 @@ $param = @{ Alias = '*' } -Write-Verbose 'Exporting module members' - +Write-Verbose "[$scriptName] - Exporting module members" Export-ModuleMember @param +Write-Verbose "[$scriptName] - Exporting module members - Done" #endregion Export module members Write-Verbose "[$scriptName] - Done" From 93276d65e5c80d6f34806809822b2f8518fe05ca Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 24 Sep 2023 16:10:16 +0200 Subject: [PATCH 15/45] Improve InitSecVault --- .../private/Config/Initialize-SecretVault.ps1 | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/GitHub/private/Config/Initialize-SecretVault.ps1 b/src/GitHub/private/Config/Initialize-SecretVault.ps1 index 05b619d5c..4ab712e9f 100644 --- a/src/GitHub/private/Config/Initialize-SecretVault.ps1 +++ b/src/GitHub/private/Config/Initialize-SecretVault.ps1 @@ -16,8 +16,9 @@ function Initialize-SecretVault { Initializes a secret vault named 'SecretStore' using the 'Microsoft.PowerShell.SecretStore' module. .NOTES - For more information aobut secret vaults, see https://learn.microsoft.com/en-us/powershell/utility-modules/secretmanagement/overview?view=ps-modules + For more information about secret vaults, see https://learn.microsoft.com/en-us/powershell/utility-modules/secretmanagement/overview?view=ps-modules #> + [OutputType([void])] [CmdletBinding()] param ( @@ -31,11 +32,11 @@ function Initialize-SecretVault { [string] $Type ) - $secretVault = Get-SecretVault | Where-Object { $_.ModuleName -eq $Type } - $secretVaultExists = $secretVault.count -ne 0 - Write-Verbose "[$Name] - exists - [$secretVaultExists]" - if (-not $secretVaultExists) { - Write-Verbose "[$Name] - Registering" + $functionName = $MyInvocation.MyCommand.Name + + $vault = Get-SecretVault | Where-Object { $_.ModuleName -eq $Type } + if (-not $vault) { + Write-Verbose "[$functionName] - [$Type] - Registering" switch ($Type) { 'Microsoft.PowerShell.SecretStore' { @@ -51,17 +52,21 @@ function Initialize-SecretVault { Reset-SecretStore @vaultParameters } } + } else { + Write-Verbose "[$functionName] - [$Type] - already registered" } $secretStore = Get-SecretVault | Where-Object { $_.Name -eq $Name } - $secretStoreExists = $secretStore.count -ne 0 - if (-not $secretStoreExists) { + if (-not $secretStore) { + Write-Verbose "[$functionName] - [$Name] - Registering" $secretVault = @{ Name = $Name ModuleName = $Type - DefaultVault = $true + DefaultVault = true Description = 'SecretStore' } Register-SecretVault @secretVault + } else { + Write-Verbose "[$functionName] - [$Name] - already registered" } } From 29ff4ef9f275054539f00fde4520c9be101a1d1b Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 24 Sep 2023 16:10:37 +0200 Subject: [PATCH 16/45] Make token values secure string --- src/GitHub/private/Config/Config.ps1 | 4 ++-- src/GitHub/scripts/1-InitializeModule.ps1 | 1 + src/GitHub/scripts/2-Prereqs.ps1 | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 src/GitHub/scripts/1-InitializeModule.ps1 create mode 100644 src/GitHub/scripts/2-Prereqs.ps1 diff --git a/src/GitHub/private/Config/Config.ps1 b/src/GitHub/private/Config/Config.ps1 index de2131326..d34a3967e 100644 --- a/src/GitHub/private/Config/Config.ps1 +++ b/src/GitHub/private/Config/Config.ps1 @@ -9,13 +9,13 @@ User = [pscustomobject]@{ # $script:ConfigTemplate.User Auth = [pscustomobject]@{ # $script:ConfigTemplate.User.Auth AccessToken = [pscustomobject]@{ # $script:ConfigTemplate.User.Auth.AccessToken - Value = '' # $script:ConfigTemplate.User.Auth.AccessToken.Value + Value = [securestring]::new() # $script:ConfigTemplate.User.Auth.AccessToken.Value ExpirationDate = [datetime]::MinValue # $script:ConfigTemplate.User.Auth.AccessToken.ExpirationDate } ClientID = '' # $script:ConfigTemplate.User.Auth.ClientID Mode = '' # $script:ConfigTemplate.User.Auth.Mode RefreshToken = [pscustomobject]@{ - Value = '' # $script:ConfigTemplate.User.Auth.RefreshToken.Value + Value = [securestring]::new() # $script:ConfigTemplate.User.Auth.RefreshToken.Value ExpirationDate = [datetime]::MinValue # $script:ConfigTemplate.User.Auth.RefreshToken.ExpirationDate } Scope = '' # $script:ConfigTemplate.User.Auth.Scope diff --git a/src/GitHub/scripts/1-InitializeModule.ps1 b/src/GitHub/scripts/1-InitializeModule.ps1 new file mode 100644 index 000000000..497ba77fe --- /dev/null +++ b/src/GitHub/scripts/1-InitializeModule.ps1 @@ -0,0 +1 @@ +Write-Verbose 'Initializing GitHub module...' -Verbose diff --git a/src/GitHub/scripts/2-Prereqs.ps1 b/src/GitHub/scripts/2-Prereqs.ps1 new file mode 100644 index 000000000..dd5ad5362 --- /dev/null +++ b/src/GitHub/scripts/2-Prereqs.ps1 @@ -0,0 +1 @@ +Write-Verbose 'Loading prereqs...' -Verbose From ea2388357539c154765e6a57fb5979f86f86cb14 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 24 Sep 2023 19:35:56 +0200 Subject: [PATCH 17/45] fix Initialize-SecretVault --- src/GitHub/private/Config/Initialize-SecretVault.ps1 | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/GitHub/private/Config/Initialize-SecretVault.ps1 b/src/GitHub/private/Config/Initialize-SecretVault.ps1 index 4ab712e9f..6da65c475 100644 --- a/src/GitHub/private/Config/Initialize-SecretVault.ps1 +++ b/src/GitHub/private/Config/Initialize-SecretVault.ps1 @@ -23,13 +23,14 @@ function Initialize-SecretVault { [CmdletBinding()] param ( # The name of the secret vault. - [Parameter()] + [Parameter(Mandatory)] [string] $Name, # The type of the secret vault. [Parameter()] + [ValidateSet('Microsoft.PowerShell.SecretStore')] [Alias('ModuleName')] - [string] $Type + [string] $Type = 'Microsoft.PowerShell.SecretStore' ) $functionName = $MyInvocation.MyCommand.Name @@ -52,6 +53,7 @@ function Initialize-SecretVault { Reset-SecretStore @vaultParameters } } + Write-Verbose "[$functionName] - [$Type] - Done" } else { Write-Verbose "[$functionName] - [$Type] - already registered" } @@ -62,10 +64,11 @@ function Initialize-SecretVault { $secretVault = @{ Name = $Name ModuleName = $Type - DefaultVault = true + DefaultVault = $true Description = 'SecretStore' } Register-SecretVault @secretVault + Write-Verbose "[$functionName] - [$Name] - Done" } else { Write-Verbose "[$functionName] - [$Name] - already registered" } From ec63890e7770a00e9b6e33c0cc1784fb7b86233d Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 24 Sep 2023 19:36:38 +0200 Subject: [PATCH 18/45] fix pathing --- src/GitHub/GitHub.ps1 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/GitHub/GitHub.ps1 b/src/GitHub/GitHub.ps1 index 6122c7dd3..9cdb8c5f0 100644 --- a/src/GitHub/GitHub.ps1 +++ b/src/GitHub/GitHub.ps1 @@ -1,5 +1,6 @@ -Write-Verbose "Initializing GitHub module..." -Verbose +$scriptFilePath = $MyInvocation.MyCommand.Path +Write-Verbose "[$scriptFilePath] - Initializing GitHub module..." -Verbose $script:Config = $script:ConfigTemplate | ConvertTo-Json -Depth 100 | ConvertFrom-Json Initialize-SecretVault -Name $script:SecretVault.Name -Type $script:SecretVault.Type Restore-GitHubConfig From 8da9e31bcbd33752b1a374df81597f631aa8e1f1 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 24 Sep 2023 19:36:51 +0200 Subject: [PATCH 19/45] fix pathing --- src/GitHub/scripts/1-InitializeModule.ps1 | 4 +++- src/GitHub/scripts/2-Prereqs.ps1 | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/GitHub/scripts/1-InitializeModule.ps1 b/src/GitHub/scripts/1-InitializeModule.ps1 index 497ba77fe..da7dc2b46 100644 --- a/src/GitHub/scripts/1-InitializeModule.ps1 +++ b/src/GitHub/scripts/1-InitializeModule.ps1 @@ -1 +1,3 @@ -Write-Verbose 'Initializing GitHub module...' -Verbose +$scriptFilePath = $MyInvocation.MyCommand.Path + +Write-Verbose "[$scriptFilePath] - Initializing GitHub module..." -Verbose diff --git a/src/GitHub/scripts/2-Prereqs.ps1 b/src/GitHub/scripts/2-Prereqs.ps1 index dd5ad5362..3da5221e9 100644 --- a/src/GitHub/scripts/2-Prereqs.ps1 +++ b/src/GitHub/scripts/2-Prereqs.ps1 @@ -1 +1,3 @@ -Write-Verbose 'Loading prereqs...' -Verbose +$scriptFilePath = $MyInvocation.MyCommand.Path + +Write-Verbose "[$scriptFilePath] - Loading prereqs..." -Verbose From 9d87f2d23c0ecad314b2b5afac0871b54df54f30 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 24 Sep 2023 19:56:42 +0200 Subject: [PATCH 20/45] Default to SecretStore, as we only support one :) --- src/GitHub/private/Config/Initialize-SecretVault.ps1 | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/GitHub/private/Config/Initialize-SecretVault.ps1 b/src/GitHub/private/Config/Initialize-SecretVault.ps1 index 6da65c475..b17dee6f6 100644 --- a/src/GitHub/private/Config/Initialize-SecretVault.ps1 +++ b/src/GitHub/private/Config/Initialize-SecretVault.ps1 @@ -23,12 +23,11 @@ function Initialize-SecretVault { [CmdletBinding()] param ( # The name of the secret vault. - [Parameter(Mandatory)] - [string] $Name, + [Parameter()] + [string] $Name = 'SecretStore', # The type of the secret vault. [Parameter()] - [ValidateSet('Microsoft.PowerShell.SecretStore')] [Alias('ModuleName')] [string] $Type = 'Microsoft.PowerShell.SecretStore' ) From 06bad48531dce5c5454e2d361f6be1a30ff5f3f3 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 24 Sep 2023 23:34:53 +0200 Subject: [PATCH 21/45] Add all the things --- src/GitHub/GitHub.ps1 | 18 +-- src/GitHub/data/SecretVault.psd1 | 4 +- src/GitHub/en_US/about_Config.help.txt | 4 +- .../DeviceFlow/Check-GitHubAccessToken.ps1 | 12 -- .../Invoke-GitHubDeviceFlowLogin.ps1 | 2 +- .../DeviceFlow/Request-GitHubAccessToken.ps1 | 8 +- .../DeviceFlow/Request-GitHubDeviceCode.ps1 | 1 - .../DeviceFlow/Wait-GitHubAccessToken.ps1 | 2 +- src/GitHub/private/Config/Config.ps1 | 28 ---- src/GitHub/private/Config/ConfigTemplate.ps1 | 15 +++ .../private/Config/Reset-GitHubConfig.ps1 | 46 +++++++ .../Config/Restore-GitHubConfig.ps1 | 8 +- .../Config/Save-GitHubConfig.ps1 | 3 +- src/GitHub/public/API/Invoke-GitHubAPI.ps1 | 4 +- .../public/Auth/Connect-GitHubAccount.ps1 | 121 ++++++++++-------- .../public/Auth/Disconnect-GitHubAccount.ps1 | 2 +- src/GitHub/public/Config/Get-GitHubConfig.ps1 | 15 +-- .../public/Config/Reset-GitHubConfig.ps1 | 55 -------- src/GitHub/public/Config/Set-GitHubConfig.ps1 | 34 +++-- 19 files changed, 188 insertions(+), 194 deletions(-) delete mode 100644 src/GitHub/private/Auth/DeviceFlow/Check-GitHubAccessToken.ps1 delete mode 100644 src/GitHub/private/Config/Config.ps1 create mode 100644 src/GitHub/private/Config/ConfigTemplate.ps1 create mode 100644 src/GitHub/private/Config/Reset-GitHubConfig.ps1 rename src/GitHub/{public => private}/Config/Restore-GitHubConfig.ps1 (80%) rename src/GitHub/{public => private}/Config/Save-GitHubConfig.ps1 (75%) delete mode 100644 src/GitHub/public/Config/Reset-GitHubConfig.ps1 diff --git a/src/GitHub/GitHub.ps1 b/src/GitHub/GitHub.ps1 index 9cdb8c5f0..0cf982655 100644 --- a/src/GitHub/GitHub.ps1 +++ b/src/GitHub/GitHub.ps1 @@ -1,15 +1,17 @@ $scriptFilePath = $MyInvocation.MyCommand.Path Write-Verbose "[$scriptFilePath] - Initializing GitHub module..." -Verbose -$script:Config = $script:ConfigTemplate | ConvertTo-Json -Depth 100 | ConvertFrom-Json + Initialize-SecretVault -Name $script:SecretVault.Name -Type $script:SecretVault.Type + +# Autoload settings if present +$script:Config = $script:ConfigTemplate | ConvertTo-Json -Depth 100 | ConvertFrom-Json -AsHashtable # Essentially a deep clone of the AppConfig template Restore-GitHubConfig +Save-GitHubConfig -if (-not [string]::IsNullOrEmpty($env:GH_TOKEN)) { - Write-Verbose 'Logging on using GH_TOKEN' - Connect-GitHubAccount -AccessToken $env:GH_TOKEN -} -if (-not [string]::IsNullOrEmpty($env:GITHUB_TOKEN)) { - Write-Verbose 'Logging on using GITHUB_TOKEN' - Connect-GitHubAccount -AccessToken $env:GITHUB_TOKEN +# Autologon if a token is present in environment variables +$envVar = Get-ChildItem -Path 'Env:' | Where-Object Name -In 'GH_TOKEN', 'GITHUB_TOKEN' | Select-Object -First 1 +$envVarPresent = $envVar.count -gt 0 +if ($envVarPresent) { + Connect-GitHubAccount } diff --git a/src/GitHub/data/SecretVault.psd1 b/src/GitHub/data/SecretVault.psd1 index e119766f1..8a096f38c 100644 --- a/src/GitHub/data/SecretVault.psd1 +++ b/src/GitHub/data/SecretVault.psd1 @@ -1,7 +1,7 @@ @{ - Name = 'GitHub' # $script:SecretVault.Name + Name = 'SecretStore' # $script:SecretVault.Name Type = 'Microsoft.PowerShell.SecretStore' # $script:SecretVault.Type Secret = @{ - Name = 'Config' # $script:SecretVault.Secret.Name + Name = 'GitHub_Config' # $script:SecretVault.Secret.Name } } diff --git a/src/GitHub/en_US/about_Config.help.txt b/src/GitHub/en_US/about_Config.help.txt index 315028b52..7344cbde0 100644 --- a/src/GitHub/en_US/about_Config.help.txt +++ b/src/GitHub/en_US/about_Config.help.txt @@ -93,9 +93,9 @@ EXAMPLES -------------------------- EXAMPLE 4 -------------------------- - Reset-GitHubConfig -Scope 'App.API' + Reset-GitHubConfig -Scope Auth - This command resets the 'App.API' section of the GitHub configuration to its default values. + This command resets the Auth related settings of the GitHub configuration to its default values. -------------------------- EXAMPLE 5 -------------------------- diff --git a/src/GitHub/private/Auth/DeviceFlow/Check-GitHubAccessToken.ps1 b/src/GitHub/private/Auth/DeviceFlow/Check-GitHubAccessToken.ps1 deleted file mode 100644 index 1ef8db1af..000000000 --- a/src/GitHub/private/Auth/DeviceFlow/Check-GitHubAccessToken.ps1 +++ /dev/null @@ -1,12 +0,0 @@ -function Check-GitHubAccessToken { - - [DateTime]$accessTokenExirationDate = $script:Config.User.Auth.AccessToken.ExpirationDate - $accessTokenValid = $accessTokenExirationDate -gt (Get-Date) - - if (-not $accessTokenValid) { - Write-Warning 'Your access token has expired. Refreshing it...' - Connect-GitHubAccount -Refresh - } - $TimeSpan = New-TimeSpan -Start (Get-Date) -End $accessTokenExirationDate - Write-Host "Your access token will expire in $($TimeSpan.Days)-$($TimeSpan.Hours):$($TimeSpan.Minutes):$($TimeSpan.Seconds)." -} diff --git a/src/GitHub/private/Auth/DeviceFlow/Invoke-GitHubDeviceFlowLogin.ps1 b/src/GitHub/private/Auth/DeviceFlow/Invoke-GitHubDeviceFlowLogin.ps1 index 93762f95c..712462fbb 100644 --- a/src/GitHub/private/Auth/DeviceFlow/Invoke-GitHubDeviceFlowLogin.ps1 +++ b/src/GitHub/private/Auth/DeviceFlow/Invoke-GitHubDeviceFlowLogin.ps1 @@ -33,7 +33,7 @@ # The refresh token to use for re-authentication. [Parameter()] - [string] $RefreshToken + [securestring] $RefreshToken ) do { diff --git a/src/GitHub/private/Auth/DeviceFlow/Request-GitHubAccessToken.ps1 b/src/GitHub/private/Auth/DeviceFlow/Request-GitHubAccessToken.ps1 index fda7406cb..a3a74f61b 100644 --- a/src/GitHub/private/Auth/DeviceFlow/Request-GitHubAccessToken.ps1 +++ b/src/GitHub/private/Auth/DeviceFlow/Request-GitHubAccessToken.ps1 @@ -35,16 +35,20 @@ Mandatory, ParameterSetName = 'RefreshToken' )] - [string] $RefreshToken + [securestring] $RefreshToken ) $body = @{ 'client_id' = $ClientID } + $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($RefreshToken) + $RefreshTokenAsPlainText = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR) + [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($BSTR) + if ($PSBoundParameters.ContainsKey('RefreshToken')) { $body += @{ - 'refresh_token' = $RefreshToken + 'refresh_token' = $RefreshTokenAsPlainText 'grant_type' = 'refresh_token' } } diff --git a/src/GitHub/private/Auth/DeviceFlow/Request-GitHubDeviceCode.ps1 b/src/GitHub/private/Auth/DeviceFlow/Request-GitHubDeviceCode.ps1 index bdc393b51..dc6cdf796 100644 --- a/src/GitHub/private/Auth/DeviceFlow/Request-GitHubDeviceCode.ps1 +++ b/src/GitHub/private/Auth/DeviceFlow/Request-GitHubDeviceCode.ps1 @@ -56,4 +56,3 @@ throw $_ } } - diff --git a/src/GitHub/private/Auth/DeviceFlow/Wait-GitHubAccessToken.ps1 b/src/GitHub/private/Auth/DeviceFlow/Wait-GitHubAccessToken.ps1 index cc1e0411d..867d5890e 100644 --- a/src/GitHub/private/Auth/DeviceFlow/Wait-GitHubAccessToken.ps1 +++ b/src/GitHub/private/Auth/DeviceFlow/Wait-GitHubAccessToken.ps1 @@ -38,7 +38,7 @@ Mandatory, ParameterSetName = 'RefreshToken' )] - [string] $RefreshToken, + [securestring] $RefreshToken, # The interval to wait between polling for the token. [Parameter()] diff --git a/src/GitHub/private/Config/Config.ps1 b/src/GitHub/private/Config/Config.ps1 deleted file mode 100644 index d34a3967e..000000000 --- a/src/GitHub/private/Config/Config.ps1 +++ /dev/null @@ -1,28 +0,0 @@ -$script:ConfigTemplate = [pscustomobject]@{ # $script:ConfigTemplate - App = [pscustomobject]@{ # $script:ConfigTemplate.App - API = [pscustomobject]@{ # $script:ConfigTemplate.App.API - BaseURI = 'https://api.github.com' # $script:ConfigTemplate.App.API.BaseURI - Version = '2022-11-28' # $script:ConfigTemplate.App.API.Version - } - Defaults = [pscustomobject]@{} # $script:ConfigTemplate.App.Defaults - } - User = [pscustomobject]@{ # $script:ConfigTemplate.User - Auth = [pscustomobject]@{ # $script:ConfigTemplate.User.Auth - AccessToken = [pscustomobject]@{ # $script:ConfigTemplate.User.Auth.AccessToken - Value = [securestring]::new() # $script:ConfigTemplate.User.Auth.AccessToken.Value - ExpirationDate = [datetime]::MinValue # $script:ConfigTemplate.User.Auth.AccessToken.ExpirationDate - } - ClientID = '' # $script:ConfigTemplate.User.Auth.ClientID - Mode = '' # $script:ConfigTemplate.User.Auth.Mode - RefreshToken = [pscustomobject]@{ - Value = [securestring]::new() # $script:ConfigTemplate.User.Auth.RefreshToken.Value - ExpirationDate = [datetime]::MinValue # $script:ConfigTemplate.User.Auth.RefreshToken.ExpirationDate - } - Scope = '' # $script:ConfigTemplate.User.Auth.Scope - } - Defaults = [pscustomobject]@{ # $script:ConfigTemplate.User.Defaults - Owner = '' # $script:ConfigTemplate.User.Defaults.Owner - Repo = '' # $script:ConfigTemplate.User.Defaults.Repo - } - } -} diff --git a/src/GitHub/private/Config/ConfigTemplate.ps1 b/src/GitHub/private/Config/ConfigTemplate.ps1 new file mode 100644 index 000000000..32960e690 --- /dev/null +++ b/src/GitHub/private/Config/ConfigTemplate.ps1 @@ -0,0 +1,15 @@ +$script:ConfigTemplate = @{ + AccessTokenType = '' #OAuth,App,Legacy,FineGrained + AccessToken = '' + AccessTokenExpirationDate = [datetime]::MinValue + ApiBaseUri = 'https://api.github.com' + ApiVersion = '2022-11-28' + DefaultOwner = '' #Default Owner for the current session + DefaultRepo = '' #Default Repo for the current session + DeviceFlowType = '' #OAuthApp,GitHubApp + RefreshToken = '' + RefreshTokenExpirationDate = [datetime]::MinValue + Scope = '' #OAuth Scopes for the access token + AuthType = '' #sPAT,PAT,DeviceFlow + UserName = '' #GHUsername +} diff --git a/src/GitHub/private/Config/Reset-GitHubConfig.ps1 b/src/GitHub/private/Config/Reset-GitHubConfig.ps1 new file mode 100644 index 000000000..99c7f68ee --- /dev/null +++ b/src/GitHub/private/Config/Reset-GitHubConfig.ps1 @@ -0,0 +1,46 @@ +function Reset-GitHubConfig { + <# + .SYNOPSIS + Reset the GitHub configuration. + + .DESCRIPTION + Reset the GitHub configuration. Specific scopes can be reset by using the Scope parameter. + + .EXAMPLE + Reset-GitHubConfig + + Resets the entire GitHub configuration. + + .EXAMPLE + Reset-GitHubConfig -Scope 'Auth' + + Resets the Auth related variables of the GitHub configuration. + #> + [Alias('Reset-GHConfig')] + [OutputType([void])] + [CmdletBinding()] + param( + # Reset the GitHub configuration for a specific scope. + [Parameter()] + [ValidateSet('Auth', 'All')] + [string] $Scope = 'All' + ) + + Write-Verbose "Resetting GitHub configuration for scope '$Scope'..." + switch ($Scope) { + 'Auth' { + $script:Config.AccessTokenType = '' + $script:Config.AccessToken = [securestring]::new() + $script:Config.AccessTokenExpirationDate = [datetime]::MinValue + $script:Config.DeviceFlowType = '' + $script:Config.RefreshToken = [securestring]::new() + $script:Config.RefreshTokenExpirationDate = [datetime]::MinValue + $script:Config.Scope = '' + $script:Config.AuthType = '' + } + 'All' { + $script:Config = $script:ConfigTemplate | ConvertTo-Json -Depth 100 | ConvertFrom-Json -AsHashtable + } + } + Save-GitHubConfig +} diff --git a/src/GitHub/public/Config/Restore-GitHubConfig.ps1 b/src/GitHub/private/Config/Restore-GitHubConfig.ps1 similarity index 80% rename from src/GitHub/public/Config/Restore-GitHubConfig.ps1 rename to src/GitHub/private/Config/Restore-GitHubConfig.ps1 index 48ed8d390..f021fe3de 100644 --- a/src/GitHub/public/Config/Restore-GitHubConfig.ps1 +++ b/src/GitHub/private/Config/Restore-GitHubConfig.ps1 @@ -21,11 +21,13 @@ function Restore-GitHubConfig { [CmdletBinding()] param() - $vault = Get-SecretVault -Name $script:SecretVault.Name - $vaultExists = $vault.count -eq 1 - if ($vaultExists) { + $secretVault = Get-SecretVault | Where-Object -Property ModuleName -EQ $script:SecretVault.Type + + if ($SecretVault) { + Write-Verbose "Restoring configuration from [$($script:SecretVault.Name)]." $secretExists = Get-SecretInfo -Name $script:SecretVault.Secret.Name -Vault $script:SecretVault.Name if ($secretExists) { + Write-Verbose "Configuration restored from [$($script:SecretVault.Name)]." $script:Config = Get-Secret -Name $script:SecretVault.Secret.Name -AsPlainText -Vault $script:SecretVault.Name | ConvertFrom-Json } else { Write-Verbose "Unable to restore configuration." diff --git a/src/GitHub/public/Config/Save-GitHubConfig.ps1 b/src/GitHub/private/Config/Save-GitHubConfig.ps1 similarity index 75% rename from src/GitHub/public/Config/Save-GitHubConfig.ps1 rename to src/GitHub/private/Config/Save-GitHubConfig.ps1 index 76a861eb7..d175cfe5b 100644 --- a/src/GitHub/public/Config/Save-GitHubConfig.ps1 +++ b/src/GitHub/private/Config/Save-GitHubConfig.ps1 @@ -19,6 +19,5 @@ function Save-GitHubConfig { [CmdletBinding()] param() - $configJson = $script:Config | ConvertTo-Json -Depth 100 - Set-Secret -Name $script:SecretVault.Secret.Name -Secret $configJson -Vault $script:SecretVault.Name + Set-Secret -Name $script:SecretVault.Secret.Name -Secret $script:Config -Vault $script:SecretVault.Name } diff --git a/src/GitHub/public/API/Invoke-GitHubAPI.ps1 b/src/GitHub/public/API/Invoke-GitHubAPI.ps1 index ab3007246..aa06e09e8 100644 --- a/src/GitHub/public/API/Invoke-GitHubAPI.ps1 +++ b/src/GitHub/public/API/Invoke-GitHubAPI.ps1 @@ -30,7 +30,7 @@ # The base URI for the GitHub API. This is usually 'https://api.github.com', but can be adjusted if necessary. [Parameter()] - [string] $ApiBaseUri = $script:Config.App.Api.BaseUri, + [string] $ApiBaseUri = $script:Config.ApiBaseUri, # The specific endpoint for the API call, e.g., '/repos/user/repo/pulls'. [Parameter(Mandatory)] @@ -46,7 +46,7 @@ # The secure token used for authentication in the GitHub API. It should be stored as a SecureString to ensure it's kept safe in memory. [Parameter()] - [SecureString] $AccessToken = $script:Config.User.Auth.AccessToken.Value, + [SecureString] $AccessToken = $script:Config.AccessToken, # The 'Content-Type' header for the API request. The default is 'application/vnd.github+json'. [Parameter()] diff --git a/src/GitHub/public/Auth/Connect-GitHubAccount.ps1 b/src/GitHub/public/Auth/Connect-GitHubAccount.ps1 index af7999361..31614a540 100644 --- a/src/GitHub/public/Auth/Connect-GitHubAccount.ps1 +++ b/src/GitHub/public/Auth/Connect-GitHubAccount.ps1 @@ -16,16 +16,14 @@ Connect-GitHubAccount Connects to GitHub using a device flow login. + If the user has already logged in, the access token will be refreshed. .EXAMPLE - Connect-GitHubAccount -AccessToken 'ghp_####' + Connect-GitHubAccount -AccessToken + ! Enter your personal access token: ************* - Connects to GitHub using a personal access token (PAT). - - .EXAMPLE - Connect-GitHubAccount -Refresh - - Refreshes the access token. + User gets prompted for the access token and stores it in the secret store. + The token is used when connecting to GitHub. .EXAMPLE Connect-GitHubAccount -Mode 'OAuthApp' -Scope 'gist read:org repo workflow' @@ -57,69 +55,86 @@ # For more information on scopes visit: # https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/scopes-for-oauth-apps [Parameter(ParameterSetName = 'DeviceFlow')] - [string] $Scope, - - # Refresh the access token. - [Parameter( - Mandatory, - ParameterSetName = 'Refresh' - )] - [switch] $Refresh, + [string] $Scope = 'gist read:org repo workflow', # The personal access token to use for authentication. [Parameter( Mandatory, ParameterSetName = 'PAT' )] - [String] $AccessToken + [switch] $AccessToken ) - $vault = Get-SecretVault | Where-Object -Property ModuleName -EQ $script:SecretVault.Type - - if ($null -eq $vault) { - Initialize-SecretVault -Name $script:SecretVault.Name -Type $script:SecretVault.Type - $vault = Get-SecretVault | Where-Object -Property ModuleName -EQ $script:SecretVault.Type - } - - $clientID = $script:Auth.$Mode.ClientID + $envVar = Get-ChildItem -Path 'Env:' | Where-Object Name -In 'GH_TOKEN', 'GITHUB_TOKEN' | Select-Object -First 1 + $envVarPresent = $envVar.count -gt 0 + $AuthType = $envVarPresent ? 'sPAT' : $PSCmdlet.ParameterSetName - switch ($PSCmdlet.ParameterSetName) { - 'Refresh' { - Write-Verbose 'Refreshing access token...' - $tokenResponse = Invoke-GitHubDeviceFlowLogin -ClientID $clientID -RefreshToken $script:Config.User.Auth.RefreshToken.Value - } + switch ($AuthType) { 'DeviceFlow' { Write-Verbose 'Logging in using device flow...' - if ([string]::IsNullOrEmpty($Scope) -and ($Mode -eq 'OAuthApp')) { - $Scope = 'gist read:org repo workflow' - } - if ($script:Config.PSObject.Properties.Name -contains 'App') { - Reset-GitHubConfig -Scope 'User.Auth' + $clientID = $script:Auth.$Mode.ClientID + if ($Mode -ne $script:Config.DeviceFlowType) { + Write-Verbose "Using $Mode authentication..." + $tokenResponse = Invoke-GitHubDeviceFlowLogin -ClientID $clientID -Scope $Scope } else { - Reset-GitHubConfig -Scope 'All' + $accessTokenValidity = [datetime]$script:Config.AccessTokenExpirationDate - (Get-Date) + $accessTokenIsValid = $accessTokenValidity.Seconds -gt 0 + $accessTokenValidityText = "$($accessTokenValidity.Hours):$($accessTokenValidity.Minutes):$($accessTokenValidity.Seconds)" + if ($accessTokenIsValid) { + if ($accessTokenValidity -gt 4) { + Write-Host '✓ ' -ForegroundColor Green -NoNewline + Write-Host "Access token is still valid for $accessTokenValidityText ..." + return + } else { + Write-Host '⚠ ' -ForegroundColor Yellow -NoNewline + Write-Host "Access token remaining validity $accessTokenValidityText. Refreshing access token..." + $tokenResponse = Invoke-GitHubDeviceFlowLogin -ClientID $clientID -RefreshToken $script:Config.RefreshToken + } + } else { + $refreshTokenValidity = [datetime]$script:Config.RefreshTokenExpirationDate - (Get-Date) + $refreshTokenIsValid = $refreshTokenValidity.Seconds -gt 0 + if ($refreshTokenIsValid) { + Write-Host '⚠ ' -ForegroundColor Yellow -NoNewline + Write-Verbose 'Access token expired. Refreshing access token...' + $tokenResponse = Invoke-GitHubDeviceFlowLogin -ClientID $clientID -RefreshToken $script:Config.RefreshToken + } else { + Write-Verbose "Using $Mode authentication..." + $tokenResponse = Invoke-GitHubDeviceFlowLogin -ClientID $clientID -Scope $Scope + } + } } - $tokenResponse = Invoke-GitHubDeviceFlowLogin -ClientID $clientID -Scope $Scope - $script:Config.User.Auth.Mode = $Mode - $script:Config.User.Auth.ClientID = $clientID + Reset-GitHubConfig -Auth + $script:Config.DeviceFlowType = $Mode + $script:AuthType = $AuthType + $script:AccessTokenType = $tokenResponse.access_token -replace '_.*$', '_*' + $script:Config.AccessToken = ConvertTo-SecureString -AsPlainText $tokenResponse.access_token + $script:Config.AccessTokenExpirationDate = (Get-Date).AddSeconds($tokenResponse.expires_in) + $script:Config.RefreshToken = ConvertTo-SecureString -AsPlainText $tokenResponse.refresh_token + $script:Config.RefreshTokenExpirationDate = (Get-Date).AddSeconds($tokenResponse.refresh_token_expires_in) + break } 'PAT' { Write-Verbose 'Logging in using personal access token...' - Reset-GitHubConfig -Scope 'User.Auth' - $script:Config.User.Auth.AccessToken.Value = $Token - $script:Config.User.Auth.Mode = 'PAT' - Save-GitHubConfig - Write-Host '✓ ' -ForegroundColor Green -NoNewline - Write-Host 'Logged in using a personal access token (PAT)!' - return + Reset-GitHubConfig -Auth + $script:Config.AuthType = $AuthType + Write-Host '! ' -ForegroundColor DarkYellow -NoNewline + $script:Config.AccessToken = Read-Host -Prompt 'Enter your personal access token' -AsSecureString + $prefix = (ConvertFrom-SecureString $script:Config.AccessToken -AsPlainText) -replace '_.*$', '_*' + $script:AccessTokenType = $prefix + if ($prefix -notmatch '^ghp_|^github_pat_') { + Write-Host '⚠ ' -ForegroundColor Yellow -NoNewline + Write-Host "Unexpected access token format: $prefix" + } + break + } + 'sPAT' { + Write-Verbose 'Logging in using system access token...' + Reset-GitHubConfig -Auth + $script:AuthType = 'sPAT' + $script:Config.AccessToken = ConvertTo-SecureString -AsPlainText $envVar.Value + $prefix = $envVar.Value -replace '_.*$', '_*' + $script:AccessTokenType = $prefix } - } - - if ($tokenResponse) { - $script:Config.User.Auth.AccessToken.Value = $tokenResponse.access_token - $script:Config.User.Auth.AccessToken.ExpirationDate = (Get-Date).AddSeconds($tokenResponse.expires_in) - $script:Config.User.Auth.RefreshToken.Value = $tokenResponse.refresh_token - $script:Config.User.Auth.RefreshToken.ExpirationDate = (Get-Date).AddSeconds($tokenResponse.refresh_token_expires_in) - $script:Config.User.Auth.Scope = $tokenResponse.scope } Save-GitHubConfig diff --git a/src/GitHub/public/Auth/Disconnect-GitHubAccount.ps1 b/src/GitHub/public/Auth/Disconnect-GitHubAccount.ps1 index 6df6137a1..16a11be7c 100644 --- a/src/GitHub/public/Auth/Disconnect-GitHubAccount.ps1 +++ b/src/GitHub/public/Auth/Disconnect-GitHubAccount.ps1 @@ -26,7 +26,7 @@ [CmdletBinding()] param () - Reset-GitHubConfig + Reset-GitHubConfig -All Write-Host "✓ " -ForegroundColor Green -NoNewline Write-Host "Logged out of GitHub!" diff --git a/src/GitHub/public/Config/Get-GitHubConfig.ps1 b/src/GitHub/public/Config/Get-GitHubConfig.ps1 index 8f2ff66ba..3b8c9e706 100644 --- a/src/GitHub/public/Config/Get-GitHubConfig.ps1 +++ b/src/GitHub/public/Config/Get-GitHubConfig.ps1 @@ -5,30 +5,21 @@ .DESCRIPTION Get the current GitHub configuration. - If the Refresh switch is used, the configuration will be refreshed from the configuration file. + The configuration is first loaded from the configuration file. .EXAMPLE Get-GitHubConfig Returns the current GitHub configuration. - .EXAMPLE - Get-GitHubConfig -Refresh - - Refreshes the current GitHub configuration from the configuration store beofre returning it. #> [Alias('Get-GHConfig')] [OutputType([PSCustomObject])] [CmdletBinding()] param ( - # Refresh the configuration from the configuration store before returning it. - [Parameter()] - [switch] $Refresh + $Name ) - if ($Refresh) { - Restore-GitHubConfig - } - + Restore-GitHubConfig $script:Config } diff --git a/src/GitHub/public/Config/Reset-GitHubConfig.ps1 b/src/GitHub/public/Config/Reset-GitHubConfig.ps1 deleted file mode 100644 index 42e5994ad..000000000 --- a/src/GitHub/public/Config/Reset-GitHubConfig.ps1 +++ /dev/null @@ -1,55 +0,0 @@ -function Reset-GitHubConfig { - <# - .SYNOPSIS - Reset the GitHub configuration. - - .DESCRIPTION - Reset the GitHub configuration. Specific scopes can be reset by using the Scope parameter. - - .EXAMPLE - Reset-GitHubConfig - - Resets the entire GitHub configuration. - - .EXAMPLE - Reset-GitHubConfig -Scope 'App.API' - - Resets the App.API scope of the GitHub configuration. - #> - [Alias('Reset-GHConfig')] - [OutputType([void])] - [CmdletBinding()] - param( - # Reset the GitHub configuration for a specific scope. - [Parameter()] - [ValidateSet('App', 'App.API', 'App.Defaults', 'User', 'User.Auth', 'User.Defaults', 'All')] - [string] $Scope = 'All' - ) - - Write-Verbose "Resetting GitHub configuration for scope '$Scope'..." - switch ($Scope) { - 'App' { - $script:Config.App = $script:ConfigTemplate.App | ConvertTo-Json -Depth 100 | ConvertFrom-Json - } - 'App.API' { - $script:Config.App.API = $script:ConfigTemplate.App.API | ConvertTo-Json -Depth 100 | ConvertFrom-Json - } - 'App.Defaults' { - $script:Config.App.Defaults = $script:ConfigTemplate.App.Defaults | ConvertTo-Json -Depth 100 | ConvertFrom-Json - } - 'User' { - $script:Config.User = $script:ConfigTemplate.User | ConvertTo-Json -Depth 100 | ConvertFrom-Json - } - 'User.Auth' { - $script:Config.User.Auth = $script:ConfigTemplate.User.Auth | ConvertTo-Json -Depth 100 | ConvertFrom-Json - } - 'User.Defaults' { - $script:Config.User.Defaults = $script:ConfigTemplate.User.Defaults | ConvertTo-Json -Depth 100 | ConvertFrom-Json - } - 'All' { - $script:Config = $script:ConfigTemplate | ConvertTo-Json -Depth 100 | ConvertFrom-Json - } - } - - Save-GitHubConfig -} diff --git a/src/GitHub/public/Config/Set-GitHubConfig.ps1 b/src/GitHub/public/Config/Set-GitHubConfig.ps1 index 8b85baf7a..1957f0866 100644 --- a/src/GitHub/public/Config/Set-GitHubConfig.ps1 +++ b/src/GitHub/public/Config/Set-GitHubConfig.ps1 @@ -10,17 +10,22 @@ Set-GitHubConfig -APIBaseURI 'https://api.github.com' -APIVersion '2022-11-28' Sets the App.API scope of the GitHub configuration. + + .EXAMPLE + Set-GitHubConfig -Name 'MyFavouriteRepo' -Value 'https://github.com/PSModule/GitHub' + + Sets a item called 'MyFavouriteRepo' in the GitHub configuration. #> [Alias('Set-GHConfig')] [CmdletBinding()] param ( # Set the API Base URI. [Parameter()] - [string] $APIBaseURI, + [string] $ApiBaseUri, # Set the GitHub API Version. [Parameter()] - [string] $APIVersion, + [string] $ApiVersion, # Set the default for the Owner parameter. [Parameter()] @@ -28,21 +33,32 @@ # Set the default for the Repo parameter. [Parameter()] - [string] $Repo + [string] $Repo, + + # Choose a custom name to set. + [Parameter()] + [string] $Name, + + # Choose a custom value to set. + [Parameter()] + [string] $Value = '' ) switch ($PSBoundParameters.Keys) { - 'APIBaseURI' { - $script:Config.App.API.BaseURI = $APIBaseURI + 'ApiBaseUri' { + $script:Config.ApiBaseUri = $ApiBaseUri } - 'APIVersion' { - $script:Config.App.API.Version = $APIVersion + 'ApiVersion' { + $script:Config.ApiVersion = $ApiVersion } 'Owner' { - $script:Config.User.Defaults.Owner = $Owner + $script:Config.Owner = $Owner } 'Repo' { - $script:Config.User.Defaults.Repo = $Repo + $script:Config.Repo = $Repo + } + 'Name' { + $script:Config.$Name = $Value } } Save-GitHubConfig From 250a3560307c1fa9d2e88595a7592f043d038a78 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 24 Sep 2023 23:37:45 +0200 Subject: [PATCH 22/45] Fix data storage --- src/GitHub/private/Config/ConfigTemplate.ps1 | 6 +++--- tools/utilities/Local-Testing.ps1 | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/GitHub/private/Config/ConfigTemplate.ps1 b/src/GitHub/private/Config/ConfigTemplate.ps1 index 32960e690..3134008d0 100644 --- a/src/GitHub/private/Config/ConfigTemplate.ps1 +++ b/src/GitHub/private/Config/ConfigTemplate.ps1 @@ -1,14 +1,14 @@ $script:ConfigTemplate = @{ - AccessTokenType = '' #OAuth,App,Legacy,FineGrained + AccessTokenType = '' AccessToken = '' - AccessTokenExpirationDate = [datetime]::MinValue + AccessTokenExpirationDate = [datetime]::MinValue.ToString() ApiBaseUri = 'https://api.github.com' ApiVersion = '2022-11-28' DefaultOwner = '' #Default Owner for the current session DefaultRepo = '' #Default Repo for the current session DeviceFlowType = '' #OAuthApp,GitHubApp RefreshToken = '' - RefreshTokenExpirationDate = [datetime]::MinValue + RefreshTokenExpirationDate = [datetime]::MinValue.ToString() Scope = '' #OAuth Scopes for the access token AuthType = '' #sPAT,PAT,DeviceFlow UserName = '' #GHUsername diff --git a/tools/utilities/Local-Testing.ps1 b/tools/utilities/Local-Testing.ps1 index e3b0bb031..3db708b67 100644 --- a/tools/utilities/Local-Testing.ps1 +++ b/tools/utilities/Local-Testing.ps1 @@ -4,10 +4,12 @@ Get-Module -Name GitHub -ListAvailable | Uninstall-Module -Force -AllVersions Get-SecretVault | Unregister-SecretVault Get-SecretVault +Get-SecretInfo Get-Module -Name GitHub -ListAvailable Install-Module -Name GitHub -Verbose -Force -AllowPrerelease $VerbosePreference = 'Continue' +$env:PSModulePath += ';C:\Repos\GitHub\PSModule\Modules\GitHub\outputs' Import-Module -Name 'C:\Repos\GitHub\PSModule\Modules\GitHub\src\GitHub\GitHub.psm1' -Verbose -Force Import-Module -Name GitHub -Verbose @@ -17,7 +19,6 @@ Get-Variable | Where-Object -Property Module -ne $null | Select-Object Name, Mod Connect-GitHubAccount Get-GitHubConfig | ConvertTo-Json -Depth 100 Get-GitHubConfig -Refresh | ConvertTo-Json -Depth 100 -Restore-GitHubConfig -Verbose Get-GitHubContext Connect-GitHubAccount -Refresh -Verbose From 7950cbcec0ca531b54e938ee4411fe6819389574 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 24 Sep 2023 23:42:08 +0200 Subject: [PATCH 23/45] Fix auth issues --- src/GitHub/public/Auth/Connect-GitHubAccount.ps1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/GitHub/public/Auth/Connect-GitHubAccount.ps1 b/src/GitHub/public/Auth/Connect-GitHubAccount.ps1 index 31614a540..939fc8c76 100644 --- a/src/GitHub/public/Auth/Connect-GitHubAccount.ps1 +++ b/src/GitHub/public/Auth/Connect-GitHubAccount.ps1 @@ -103,7 +103,7 @@ } } } - Reset-GitHubConfig -Auth + Reset-GitHubConfig -Scope 'Auth' $script:Config.DeviceFlowType = $Mode $script:AuthType = $AuthType $script:AccessTokenType = $tokenResponse.access_token -replace '_.*$', '_*' @@ -115,7 +115,7 @@ } 'PAT' { Write-Verbose 'Logging in using personal access token...' - Reset-GitHubConfig -Auth + Reset-GitHubConfig -Scope 'Auth' $script:Config.AuthType = $AuthType Write-Host '! ' -ForegroundColor DarkYellow -NoNewline $script:Config.AccessToken = Read-Host -Prompt 'Enter your personal access token' -AsSecureString @@ -129,7 +129,7 @@ } 'sPAT' { Write-Verbose 'Logging in using system access token...' - Reset-GitHubConfig -Auth + Reset-GitHubConfig -Scope 'Auth' $script:AuthType = 'sPAT' $script:Config.AccessToken = ConvertTo-SecureString -AsPlainText $envVar.Value $prefix = $envVar.Value -replace '_.*$', '_*' From dbe236de83a27ede0b3d60923ac656226634518a Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Mon, 25 Sep 2023 00:41:41 +0200 Subject: [PATCH 24/45] All the fixes :) --- src/GitHub/GitHub.ps1 | 5 -- src/GitHub/data/SecretVault.psd1 | 4 +- src/GitHub/en_US/about_Config.help.txt | 13 --- src/GitHub/private/Config/ConfigTemplate.ps1 | 15 ---- .../private/Config/Reset-GitHubConfig.ps1 | 36 ++++++--- .../private/Config/Restore-GitHubConfig.ps1 | 40 ---------- .../private/Config/Save-GitHubConfig.ps1 | 23 ------ src/GitHub/public/API/Invoke-GitHubAPI.ps1 | 6 +- .../public/Auth/Connect-GitHubAccount.ps1 | 50 +++++++----- src/GitHub/public/Config/Get-GitHubConfig.ps1 | 12 +-- src/GitHub/public/Config/Set-GitHubConfig.ps1 | 80 +++++++++++++++++-- 11 files changed, 139 insertions(+), 145 deletions(-) delete mode 100644 src/GitHub/private/Config/ConfigTemplate.ps1 delete mode 100644 src/GitHub/private/Config/Restore-GitHubConfig.ps1 delete mode 100644 src/GitHub/private/Config/Save-GitHubConfig.ps1 diff --git a/src/GitHub/GitHub.ps1 b/src/GitHub/GitHub.ps1 index 0cf982655..a0a45426b 100644 --- a/src/GitHub/GitHub.ps1 +++ b/src/GitHub/GitHub.ps1 @@ -4,11 +4,6 @@ Write-Verbose "[$scriptFilePath] - Initializing GitHub module..." -Verbose Initialize-SecretVault -Name $script:SecretVault.Name -Type $script:SecretVault.Type -# Autoload settings if present -$script:Config = $script:ConfigTemplate | ConvertTo-Json -Depth 100 | ConvertFrom-Json -AsHashtable # Essentially a deep clone of the AppConfig template -Restore-GitHubConfig -Save-GitHubConfig - # Autologon if a token is present in environment variables $envVar = Get-ChildItem -Path 'Env:' | Where-Object Name -In 'GH_TOKEN', 'GITHUB_TOKEN' | Select-Object -First 1 $envVarPresent = $envVar.count -gt 0 diff --git a/src/GitHub/data/SecretVault.psd1 b/src/GitHub/data/SecretVault.psd1 index 8a096f38c..6a669c66a 100644 --- a/src/GitHub/data/SecretVault.psd1 +++ b/src/GitHub/data/SecretVault.psd1 @@ -1,7 +1,5 @@ @{ Name = 'SecretStore' # $script:SecretVault.Name Type = 'Microsoft.PowerShell.SecretStore' # $script:SecretVault.Type - Secret = @{ - Name = 'GitHub_Config' # $script:SecretVault.Secret.Name - } + Prefix = 'GHPS_' # $script:SecretVault.Prefix } diff --git a/src/GitHub/en_US/about_Config.help.txt b/src/GitHub/en_US/about_Config.help.txt index 7344cbde0..d6e25c783 100644 --- a/src/GitHub/en_US/about_Config.help.txt +++ b/src/GitHub/en_US/about_Config.help.txt @@ -53,8 +53,6 @@ FUNCTIONS - Get-GitHubConfig: Fetches the current module configuration. - Reset-GitHubConfig: Resets all or specific sections to its default values. - - Restore-GitHubConfig: Restores the configuration from the secret vault. - - Save-GitHubConfig: Saves the current configuration to the secret vault. - Set-GitHubConfig: Allows setting specific elements of the configuration. CONFIGURATION @@ -87,21 +85,10 @@ EXAMPLES -------------------------- EXAMPLE 3 -------------------------- - Restore-GitHubConfig - - This command restores the GitHub configuration from the secret vault. - - -------------------------- EXAMPLE 4 -------------------------- - Reset-GitHubConfig -Scope Auth This command resets the Auth related settings of the GitHub configuration to its default values. - -------------------------- EXAMPLE 5 -------------------------- - - Save-GitHubConfig - - This command saves the current GitHub configuration to the secret vault. KEYWORDS diff --git a/src/GitHub/private/Config/ConfigTemplate.ps1 b/src/GitHub/private/Config/ConfigTemplate.ps1 deleted file mode 100644 index 3134008d0..000000000 --- a/src/GitHub/private/Config/ConfigTemplate.ps1 +++ /dev/null @@ -1,15 +0,0 @@ -$script:ConfigTemplate = @{ - AccessTokenType = '' - AccessToken = '' - AccessTokenExpirationDate = [datetime]::MinValue.ToString() - ApiBaseUri = 'https://api.github.com' - ApiVersion = '2022-11-28' - DefaultOwner = '' #Default Owner for the current session - DefaultRepo = '' #Default Repo for the current session - DeviceFlowType = '' #OAuthApp,GitHubApp - RefreshToken = '' - RefreshTokenExpirationDate = [datetime]::MinValue.ToString() - Scope = '' #OAuth Scopes for the access token - AuthType = '' #sPAT,PAT,DeviceFlow - UserName = '' #GHUsername -} diff --git a/src/GitHub/private/Config/Reset-GitHubConfig.ps1 b/src/GitHub/private/Config/Reset-GitHubConfig.ps1 index 99c7f68ee..2ead46f75 100644 --- a/src/GitHub/private/Config/Reset-GitHubConfig.ps1 +++ b/src/GitHub/private/Config/Reset-GitHubConfig.ps1 @@ -29,18 +29,34 @@ Write-Verbose "Resetting GitHub configuration for scope '$Scope'..." switch ($Scope) { 'Auth' { - $script:Config.AccessTokenType = '' - $script:Config.AccessToken = [securestring]::new() - $script:Config.AccessTokenExpirationDate = [datetime]::MinValue - $script:Config.DeviceFlowType = '' - $script:Config.RefreshToken = [securestring]::new() - $script:Config.RefreshTokenExpirationDate = [datetime]::MinValue - $script:Config.Scope = '' - $script:Config.AuthType = '' + $Settings = @{ + AccessTokenType = '' + AccessToken = '' + AccessTokenExpirationDate = [datetime]::MinValue + DeviceFlowType = '' + RefreshToken = '' + RefreshTokenExpirationDate = [datetime]::MinValue + Scope = '' + AuthType = '' + } } 'All' { - $script:Config = $script:ConfigTemplate | ConvertTo-Json -Depth 100 | ConvertFrom-Json -AsHashtable + $Settings = @{ + AccessToken = '' + AccessTokenExpirationDate = [datetime]::MinValue + AccessTokenType = '' + ApiBaseUri = 'https://api.github.com' + ApiVersion = '2022-11-28' + AuthType = '' + DeviceFlowType = '' + Owner = '' + RefreshToken = '' + RefreshTokenExpirationDate = [datetime]::MinValue + Repo = '' + Scope = '' + UserName = '' + } } } - Save-GitHubConfig + Set-GitHubConfig @Settings } diff --git a/src/GitHub/private/Config/Restore-GitHubConfig.ps1 b/src/GitHub/private/Config/Restore-GitHubConfig.ps1 deleted file mode 100644 index f021fe3de..000000000 --- a/src/GitHub/private/Config/Restore-GitHubConfig.ps1 +++ /dev/null @@ -1,40 +0,0 @@ -#Requires -Version 7.0 -#Requires -Modules Microsoft.PowerShell.SecretManagement - -function Restore-GitHubConfig { - <# - .SYNOPSIS - Restore the GitHub configuration from the configuration store. - - .DESCRIPTION - Restore the GitHub configuration from the configuration store. - - .EXAMPLE - Restore-GitHubConfig - - Restores the GitHub configuration from the configuration store. - #> - [Alias('Load-GitHubConfig')] - [Alias('Load-GHConfig')] - [Alias('Restore-GHConfig')] - [OutputType([void])] - [CmdletBinding()] - param() - - $secretVault = Get-SecretVault | Where-Object -Property ModuleName -EQ $script:SecretVault.Type - - if ($SecretVault) { - Write-Verbose "Restoring configuration from [$($script:SecretVault.Name)]." - $secretExists = Get-SecretInfo -Name $script:SecretVault.Secret.Name -Vault $script:SecretVault.Name - if ($secretExists) { - Write-Verbose "Configuration restored from [$($script:SecretVault.Name)]." - $script:Config = Get-Secret -Name $script:SecretVault.Secret.Name -AsPlainText -Vault $script:SecretVault.Name | ConvertFrom-Json - } else { - Write-Verbose "Unable to restore configuration." - Write-Verbose "The secret [$($script:SecretVault.Secret.Name)] does not exist in the vault [$($script:SecretVault.Name)]." - } - } else { - Write-Verbose "Unable to restore configuration." - Write-Verbose "The vault [$($script:SecretVault.Name)] does not exist." - } -} diff --git a/src/GitHub/private/Config/Save-GitHubConfig.ps1 b/src/GitHub/private/Config/Save-GitHubConfig.ps1 deleted file mode 100644 index d175cfe5b..000000000 --- a/src/GitHub/private/Config/Save-GitHubConfig.ps1 +++ /dev/null @@ -1,23 +0,0 @@ -#Requires -Version 7.0 -#Requires -Modules Microsoft.PowerShell.SecretManagement - -function Save-GitHubConfig { - <# - .SYNOPSIS - Save the GitHub configuration to the configuration store. - - .DESCRIPTION - Save the GitHub configuration to the configuration store. - - .EXAMPLE - Save-GitHubConfig - - Saves the GitHub configuration to the configuration store. - #> - [Alias('Save-GHConfig')] - [OutputType([void])] - [CmdletBinding()] - param() - - Set-Secret -Name $script:SecretVault.Secret.Name -Secret $script:Config -Vault $script:SecretVault.Name -} diff --git a/src/GitHub/public/API/Invoke-GitHubAPI.ps1 b/src/GitHub/public/API/Invoke-GitHubAPI.ps1 index aa06e09e8..bed1a7df7 100644 --- a/src/GitHub/public/API/Invoke-GitHubAPI.ps1 +++ b/src/GitHub/public/API/Invoke-GitHubAPI.ps1 @@ -30,7 +30,7 @@ # The base URI for the GitHub API. This is usually 'https://api.github.com', but can be adjusted if necessary. [Parameter()] - [string] $ApiBaseUri = $script:Config.ApiBaseUri, + [string] $ApiBaseUri = (Get-GitHubConfig -Name ApiBaseUri), # The specific endpoint for the API call, e.g., '/repos/user/repo/pulls'. [Parameter(Mandatory)] @@ -46,7 +46,7 @@ # The secure token used for authentication in the GitHub API. It should be stored as a SecureString to ensure it's kept safe in memory. [Parameter()] - [SecureString] $AccessToken = $script:Config.AccessToken, + [SecureString] $AccessToken = (Get-GitHubConfig -Name AccessToken), # The 'Content-Type' header for the API request. The default is 'application/vnd.github+json'. [Parameter()] @@ -54,7 +54,7 @@ # The GitHub API version to be used. By default, it pulls from a configuration script variable. [Parameter()] - [string] $Version = $script:Config.App.Api.Version + [string] $Version = (Get-GitHubConfig -Name ApiVersion) ) $functionName = $MyInvocation.MyCommand.Name diff --git a/src/GitHub/public/Auth/Connect-GitHubAccount.ps1 b/src/GitHub/public/Auth/Connect-GitHubAccount.ps1 index 939fc8c76..e307bc631 100644 --- a/src/GitHub/public/Auth/Connect-GitHubAccount.ps1 +++ b/src/GitHub/public/Auth/Connect-GitHubAccount.ps1 @@ -73,11 +73,11 @@ 'DeviceFlow' { Write-Verbose 'Logging in using device flow...' $clientID = $script:Auth.$Mode.ClientID - if ($Mode -ne $script:Config.DeviceFlowType) { + if ($Mode -ne (Get-GitHubConfig -Name DeviceFlowType)) { Write-Verbose "Using $Mode authentication..." $tokenResponse = Invoke-GitHubDeviceFlowLogin -ClientID $clientID -Scope $Scope } else { - $accessTokenValidity = [datetime]$script:Config.AccessTokenExpirationDate - (Get-Date) + $accessTokenValidity = (Get-GitHubConfig -Name 'AccessTokenExpirationDate') - (Get-Date) $accessTokenIsValid = $accessTokenValidity.Seconds -gt 0 $accessTokenValidityText = "$($accessTokenValidity.Hours):$($accessTokenValidity.Minutes):$($accessTokenValidity.Seconds)" if ($accessTokenIsValid) { @@ -88,15 +88,15 @@ } else { Write-Host '⚠ ' -ForegroundColor Yellow -NoNewline Write-Host "Access token remaining validity $accessTokenValidityText. Refreshing access token..." - $tokenResponse = Invoke-GitHubDeviceFlowLogin -ClientID $clientID -RefreshToken $script:Config.RefreshToken + $tokenResponse = Invoke-GitHubDeviceFlowLogin -ClientID $clientID -RefreshToken (Get-GitHubConfig -Name RefreshToken) } } else { - $refreshTokenValidity = [datetime]$script:Config.RefreshTokenExpirationDate - (Get-Date) + $refreshTokenValidity = (Get-GitHubConfig -Name 'RefreshTokenExpirationDate') - (Get-Date) $refreshTokenIsValid = $refreshTokenValidity.Seconds -gt 0 if ($refreshTokenIsValid) { Write-Host '⚠ ' -ForegroundColor Yellow -NoNewline Write-Verbose 'Access token expired. Refreshing access token...' - $tokenResponse = Invoke-GitHubDeviceFlowLogin -ClientID $clientID -RefreshToken $script:Config.RefreshToken + $tokenResponse = Invoke-GitHubDeviceFlowLogin -ClientID $clientID -RefreshToken (Get-GitHubConfig -Name RefreshToken) } else { Write-Verbose "Using $Mode authentication..." $tokenResponse = Invoke-GitHubDeviceFlowLogin -ClientID $clientID -Scope $Scope @@ -104,40 +104,50 @@ } } Reset-GitHubConfig -Scope 'Auth' - $script:Config.DeviceFlowType = $Mode - $script:AuthType = $AuthType - $script:AccessTokenType = $tokenResponse.access_token -replace '_.*$', '_*' - $script:Config.AccessToken = ConvertTo-SecureString -AsPlainText $tokenResponse.access_token - $script:Config.AccessTokenExpirationDate = (Get-Date).AddSeconds($tokenResponse.expires_in) - $script:Config.RefreshToken = ConvertTo-SecureString -AsPlainText $tokenResponse.refresh_token - $script:Config.RefreshTokenExpirationDate = (Get-Date).AddSeconds($tokenResponse.refresh_token_expires_in) + $settings = @{ + AccessToken = ConvertTo-SecureString -AsPlainText $tokenResponse.access_token + AccessTokenExpirationDate = (Get-Date).AddSeconds($tokenResponse.expires_in) + RefreshToken = ConvertTo-SecureString -AsPlainText $tokenResponse.refresh_token + RefreshTokenExpirationDate = (Get-Date).AddSeconds($tokenResponse.refresh_token_expires_in) + Scope = $tokenResponse.scope + AuthType = $AuthType + AccessTokenType = $tokenResponse.access_token -replace '_.*$', '_*' + DeviceFlowType = $Mode + } + Set-GitHubConfig @settings break } 'PAT' { Write-Verbose 'Logging in using personal access token...' Reset-GitHubConfig -Scope 'Auth' - $script:Config.AuthType = $AuthType Write-Host '! ' -ForegroundColor DarkYellow -NoNewline - $script:Config.AccessToken = Read-Host -Prompt 'Enter your personal access token' -AsSecureString - $prefix = (ConvertFrom-SecureString $script:Config.AccessToken -AsPlainText) -replace '_.*$', '_*' - $script:AccessTokenType = $prefix + $accessToken = Read-Host -Prompt 'Enter your personal access token' -AsSecureString + $prefix = (ConvertFrom-SecureString $accessToken -AsPlainText) -replace '_.*$', '_*' if ($prefix -notmatch '^ghp_|^github_pat_') { Write-Host '⚠ ' -ForegroundColor Yellow -NoNewline Write-Host "Unexpected access token format: $prefix" } + $settings = @{ + AuthType = $AuthType + AccessToken = $accessToken + AccessTokenType = $prefix + } + Set-GitHubConfig @settings break } 'sPAT' { Write-Verbose 'Logging in using system access token...' Reset-GitHubConfig -Scope 'Auth' - $script:AuthType = 'sPAT' - $script:Config.AccessToken = ConvertTo-SecureString -AsPlainText $envVar.Value $prefix = $envVar.Value -replace '_.*$', '_*' - $script:AccessTokenType = $prefix + $settings = @{ + AuthType = 'sPAT' + AccessToken = ConvertTo-SecureString -AsPlainText $envVar.Value + AccessTokenType = $prefix + } + Set-GitHubConfig @settings } } - Save-GitHubConfig Write-Host '✓ ' -ForegroundColor Green -NoNewline Write-Host 'Logged in to GitHub!' } diff --git a/src/GitHub/public/Config/Get-GitHubConfig.ps1 b/src/GitHub/public/Config/Get-GitHubConfig.ps1 index 3b8c9e706..2f5d3394d 100644 --- a/src/GitHub/public/Config/Get-GitHubConfig.ps1 +++ b/src/GitHub/public/Config/Get-GitHubConfig.ps1 @@ -14,12 +14,14 @@ #> [Alias('Get-GHConfig')] - [OutputType([PSCustomObject])] + [Alias('GGHC')] + [OutputType([object])] [CmdletBinding()] param ( - $Name + [string] $Name, + [switch] $AsPlainText ) - - Restore-GitHubConfig - $script:Config + $prefix = $script:SecretVault.Prefix + $Name = "$prefix$Name" + Get-Secret -Name $Name -Vault $script:SecretVault.Name -AsPlainText:$AsPlainText } diff --git a/src/GitHub/public/Config/Set-GitHubConfig.ps1 b/src/GitHub/public/Config/Set-GitHubConfig.ps1 index 1957f0866..5f107a360 100644 --- a/src/GitHub/public/Config/Set-GitHubConfig.ps1 +++ b/src/GitHub/public/Config/Set-GitHubConfig.ps1 @@ -7,18 +7,30 @@ Set the GitHub configuration. Specific scopes can be set by using the parameters. .EXAMPLE - Set-GitHubConfig -APIBaseURI 'https://api.github.com' -APIVersion '2022-11-28' + Set-GitHubConfig -APIBaseURI 'https://api.github.com" -APIVersion '2022-11-28' Sets the App.API scope of the GitHub configuration. .EXAMPLE - Set-GitHubConfig -Name 'MyFavouriteRepo' -Value 'https://github.com/PSModule/GitHub' + Set-GitHubConfig -Name "MyFavouriteRepo" -Value 'https://github.com/PSModule/GitHub' Sets a item called 'MyFavouriteRepo' in the GitHub configuration. #> [Alias('Set-GHConfig')] [CmdletBinding()] param ( + # Set the access token type. + [Parameter()] + [string] $AccessTokenType = '', + + # Set the access token. + [Parameter()] + [securestring] $AccessToken = '', + + # Set the access token expiration date. + [Parameter()] + [datetime] $AccessTokenExpirationDate, + # Set the API Base URI. [Parameter()] [string] $ApiBaseUri, @@ -27,14 +39,38 @@ [Parameter()] [string] $ApiVersion, + # Set the authentication type. + [Parameter()] + [string] $AuthType, + + # Set the device flow type. + [Parameter()] + [string] $DeviceFlowType, + # Set the default for the Owner parameter. [Parameter()] [string] $Owner, + # Set the refresh token. + [Parameter()] + [securestring] $RefreshToken, + + # Set the refresh token expiration date. + [Parameter()] + [datetime] $RefreshTokenExpirationDate, + # Set the default for the Repo parameter. [Parameter()] [string] $Repo, + # Set the scope. + [Parameter()] + [string] $Scope, + + # Set the GitHub username. + [Parameter()] + [string] $UserName, + # Choose a custom name to set. [Parameter()] [string] $Name, @@ -44,22 +80,50 @@ [string] $Value = '' ) + $prefix = $script:SecretVault.Prefix + switch ($PSBoundParameters.Keys) { + 'AccessToken' { + Set-Secret -Name "$prefix`AccessToken" -SecureStringSecret $AccessToken -Vault $script:SecretVault.Name + } + 'AccessTokenExpirationDate' { + Set-Secret -Name "$prefix`AccessTokenExpirationDate" -Secret $AccessTokenExpirationDate.ToString() -Vault $script:SecretVault.Name + } + 'AccessTokenType' { + Set-Secret -Name "$prefix`AccessTokenType" -Secret $AccessTokenType -Vault $script:SecretVault.Name + } 'ApiBaseUri' { - $script:Config.ApiBaseUri = $ApiBaseUri + Set-Secret -Name "$prefix`ApiBaseUri" -Secret $ApiBaseUri -Vault $script:SecretVault.Name } 'ApiVersion' { - $script:Config.ApiVersion = $ApiVersion + Set-Secret -Name "$prefix`ApiVersion" -Secret $ApiVersion -Vault $script:SecretVault.Name + } + 'AuthType' { + Set-Secret -Name "$prefix`AuthType" -Secret $AuthType -Vault $script:SecretVault.Name + } + 'DeviceFlowType' { + Set-Secret -Name "$prefix`DeviceFlowType" -Secret $DeviceFlowType -Vault $script:SecretVault.Name } 'Owner' { - $script:Config.Owner = $Owner + Set-Secret -Name "$prefix`Owner" -Secret $Owner -Vault $script:SecretVault.Name + } + 'RefreshToken' { + Set-Secret -Name "$prefix`RefreshToken" -SecureStringSecret $RefreshToken -Vault $script:SecretVault.Name + } + 'RefreshTokenExpirationDate' { + Set-Secret -Name "$prefix`RefreshTokenExpirationDate" -Secret $RefreshTokenExpirationDate.ToString() -Vault $script:SecretVault.Name } 'Repo' { - $script:Config.Repo = $Repo + Set-Secret -Name "$prefix`Repo" -Secret $Repo -Vault $script:SecretVault.Name + } + 'Scope' { + Set-Secret -Name "$prefix`Scope" -Secret $Scope -Vault $script:SecretVault.Name + } + 'UserName' { + Set-Secret -Name "$prefix`UserName" -Secret $UserName -Vault $script:SecretVault.Name } 'Name' { - $script:Config.$Name = $Value + Set-Secret -Name "$prefix$Name" -Secret $Value -Vault $script:SecretVault.Name } } - Save-GitHubConfig } From dea9266cdd2de2f6bd4cf0a224471e9a530142a3 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Mon, 25 Sep 2023 00:44:02 +0200 Subject: [PATCH 25/45] More fixes then... --- .../private/Config/Reset-GitHubConfig.ps1 | 38 +++++++++---------- tools/utilities/Local-Testing.ps1 | 3 +- 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/src/GitHub/private/Config/Reset-GitHubConfig.ps1 b/src/GitHub/private/Config/Reset-GitHubConfig.ps1 index 2ead46f75..54f0933bc 100644 --- a/src/GitHub/private/Config/Reset-GitHubConfig.ps1 +++ b/src/GitHub/private/Config/Reset-GitHubConfig.ps1 @@ -30,31 +30,31 @@ switch ($Scope) { 'Auth' { $Settings = @{ - AccessTokenType = '' - AccessToken = '' - AccessTokenExpirationDate = [datetime]::MinValue - DeviceFlowType = '' - RefreshToken = '' + AccessToken = [securestring]::new() + AccessTokenExpirationDate = [datetime]::MinValue + AccessTokenType = '' + AuthType = '' + DeviceFlowType = '' + RefreshToken = [securestring]::new() RefreshTokenExpirationDate = [datetime]::MinValue - Scope = '' - AuthType = '' + Scope = '' } } 'All' { $Settings = @{ - AccessToken = '' - AccessTokenExpirationDate = [datetime]::MinValue - AccessTokenType = '' - ApiBaseUri = 'https://api.github.com' - ApiVersion = '2022-11-28' - AuthType = '' - DeviceFlowType = '' - Owner = '' - RefreshToken = '' + AccessToken = [securestring]::new() + AccessTokenExpirationDate = [datetime]::MinValue + AccessTokenType = '' + ApiBaseUri = 'https://api.github.com' + ApiVersion = '2022-11-28' + AuthType = '' + DeviceFlowType = '' + Owner = '' + RefreshToken = [securestring]::new() RefreshTokenExpirationDate = [datetime]::MinValue - Repo = '' - Scope = '' - UserName = '' + Repo = '' + Scope = '' + UserName = '' } } } diff --git a/tools/utilities/Local-Testing.ps1 b/tools/utilities/Local-Testing.ps1 index 3db708b67..811e222b9 100644 --- a/tools/utilities/Local-Testing.ps1 +++ b/tools/utilities/Local-Testing.ps1 @@ -17,8 +17,7 @@ Clear-Host Get-Command -Module GitHub Get-Variable | Where-Object -Property Module -ne $null | Select-Object Name, Module, ModuleName Connect-GitHubAccount -Get-GitHubConfig | ConvertTo-Json -Depth 100 -Get-GitHubConfig -Refresh | ConvertTo-Json -Depth 100 +Get-GitHubConfig -Name AccessToken Get-GitHubContext Connect-GitHubAccount -Refresh -Verbose From e6d23acd9238b95ca7d3052fe076d0f42fcfe478 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Mon, 25 Sep 2023 00:49:14 +0200 Subject: [PATCH 26/45] Fixes --- src/GitHub/public/Auth/Disconnect-GitHubAccount.ps1 | 6 +++--- tools/utilities/Local-Testing.ps1 | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/GitHub/public/Auth/Disconnect-GitHubAccount.ps1 b/src/GitHub/public/Auth/Disconnect-GitHubAccount.ps1 index 16a11be7c..754786541 100644 --- a/src/GitHub/public/Auth/Disconnect-GitHubAccount.ps1 +++ b/src/GitHub/public/Auth/Disconnect-GitHubAccount.ps1 @@ -26,8 +26,8 @@ [CmdletBinding()] param () - Reset-GitHubConfig -All + Reset-GitHubConfig -Scope 'All' - Write-Host "✓ " -ForegroundColor Green -NoNewline - Write-Host "Logged out of GitHub!" + Write-Host '✓ ' -ForegroundColor Green -NoNewline + Write-Host 'Logged out of GitHub!' } diff --git a/tools/utilities/Local-Testing.ps1 b/tools/utilities/Local-Testing.ps1 index 811e222b9..6a3e3f26a 100644 --- a/tools/utilities/Local-Testing.ps1 +++ b/tools/utilities/Local-Testing.ps1 @@ -6,16 +6,16 @@ Get-SecretVault | Unregister-SecretVault Get-SecretVault Get-SecretInfo Get-Module -Name GitHub -ListAvailable -Install-Module -Name GitHub -Verbose -Force -AllowPrerelease - $VerbosePreference = 'Continue' + +Install-Module -Name GitHub -Verbose -Force -AllowPrerelease $env:PSModulePath += ';C:\Repos\GitHub\PSModule\Modules\GitHub\outputs' Import-Module -Name 'C:\Repos\GitHub\PSModule\Modules\GitHub\src\GitHub\GitHub.psm1' -Verbose -Force Import-Module -Name GitHub -Verbose Clear-Host Get-Command -Module GitHub -Get-Variable | Where-Object -Property Module -ne $null | Select-Object Name, Module, ModuleName +Get-Variable | Where-Object -Property Module -NE $null | Select-Object Name, Module, ModuleName Connect-GitHubAccount Get-GitHubConfig -Name AccessToken Get-GitHubContext From bf302d8bb0e2cdc63c6add28c83150c1a4c204ad Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Mon, 25 Sep 2023 00:58:16 +0200 Subject: [PATCH 27/45] fixes for -AsPlainText --- src/GitHub/public/API/Invoke-GitHubAPI.ps1 | 4 ++-- src/GitHub/public/Auth/Connect-GitHubAccount.ps1 | 6 +++--- tools/utilities/Local-Testing.ps1 | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/GitHub/public/API/Invoke-GitHubAPI.ps1 b/src/GitHub/public/API/Invoke-GitHubAPI.ps1 index bed1a7df7..f3ffbb3c6 100644 --- a/src/GitHub/public/API/Invoke-GitHubAPI.ps1 +++ b/src/GitHub/public/API/Invoke-GitHubAPI.ps1 @@ -30,7 +30,7 @@ # The base URI for the GitHub API. This is usually 'https://api.github.com', but can be adjusted if necessary. [Parameter()] - [string] $ApiBaseUri = (Get-GitHubConfig -Name ApiBaseUri), + [string] $ApiBaseUri = (Get-GitHubConfig -Name ApiBaseUri -AsPlainText), # The specific endpoint for the API call, e.g., '/repos/user/repo/pulls'. [Parameter(Mandatory)] @@ -54,7 +54,7 @@ # The GitHub API version to be used. By default, it pulls from a configuration script variable. [Parameter()] - [string] $Version = (Get-GitHubConfig -Name ApiVersion) + [string] $Version = (Get-GitHubConfig -Name ApiVersion -AsPlainText) ) $functionName = $MyInvocation.MyCommand.Name diff --git a/src/GitHub/public/Auth/Connect-GitHubAccount.ps1 b/src/GitHub/public/Auth/Connect-GitHubAccount.ps1 index e307bc631..1d6c7b42a 100644 --- a/src/GitHub/public/Auth/Connect-GitHubAccount.ps1 +++ b/src/GitHub/public/Auth/Connect-GitHubAccount.ps1 @@ -73,11 +73,11 @@ 'DeviceFlow' { Write-Verbose 'Logging in using device flow...' $clientID = $script:Auth.$Mode.ClientID - if ($Mode -ne (Get-GitHubConfig -Name DeviceFlowType)) { + if ($Mode -ne (Get-GitHubConfig -Name DeviceFlowType -AsPlainText -ea SilentlyContinue)) { Write-Verbose "Using $Mode authentication..." $tokenResponse = Invoke-GitHubDeviceFlowLogin -ClientID $clientID -Scope $Scope } else { - $accessTokenValidity = (Get-GitHubConfig -Name 'AccessTokenExpirationDate') - (Get-Date) + $accessTokenValidity = [datetime](Get-GitHubConfig -Name 'AccessTokenExpirationDate' -AsPlainText) - (Get-Date) $accessTokenIsValid = $accessTokenValidity.Seconds -gt 0 $accessTokenValidityText = "$($accessTokenValidity.Hours):$($accessTokenValidity.Minutes):$($accessTokenValidity.Seconds)" if ($accessTokenIsValid) { @@ -91,7 +91,7 @@ $tokenResponse = Invoke-GitHubDeviceFlowLogin -ClientID $clientID -RefreshToken (Get-GitHubConfig -Name RefreshToken) } } else { - $refreshTokenValidity = (Get-GitHubConfig -Name 'RefreshTokenExpirationDate') - (Get-Date) + $refreshTokenValidity = [datetime](Get-GitHubConfig -Name 'RefreshTokenExpirationDate' -AsPlainText) - (Get-Date) $refreshTokenIsValid = $refreshTokenValidity.Seconds -gt 0 if ($refreshTokenIsValid) { Write-Host '⚠ ' -ForegroundColor Yellow -NoNewline diff --git a/tools/utilities/Local-Testing.ps1 b/tools/utilities/Local-Testing.ps1 index 6a3e3f26a..22848b359 100644 --- a/tools/utilities/Local-Testing.ps1 +++ b/tools/utilities/Local-Testing.ps1 @@ -17,10 +17,10 @@ Clear-Host Get-Command -Module GitHub Get-Variable | Where-Object -Property Module -NE $null | Select-Object Name, Module, ModuleName Connect-GitHubAccount -Get-GitHubConfig -Name AccessToken -Get-GitHubContext - -Connect-GitHubAccount -Refresh -Verbose +Get-GitHubConfig -Name AccessToken -AsPlainText +Get-GitHubConfig -Name AccessTokenExpirationDate -AsPlainText +Get-GitHubConfig -Name RefreshToken -AsPlainText +Get-GitHubConfig -Name RefreshTokenExpirationDate -AsPlainText Disconnect-GitHubAccount -Verbose Reset-GitHubConfig -Verbose From c3e4febf4249f86915611411d53783a10107d5ce Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Mon, 25 Sep 2023 01:10:26 +0200 Subject: [PATCH 28/45] Fixes --- .../DeviceFlow/Request-GitHubAccessToken.ps1 | 6 +---- src/GitHub/public/API/Invoke-GitHubAPI.ps1 | 27 +++++++------------ tools/utilities/Local-Testing.ps1 | 4 +-- 3 files changed, 12 insertions(+), 25 deletions(-) diff --git a/src/GitHub/private/Auth/DeviceFlow/Request-GitHubAccessToken.ps1 b/src/GitHub/private/Auth/DeviceFlow/Request-GitHubAccessToken.ps1 index a3a74f61b..e3f3ffdf0 100644 --- a/src/GitHub/private/Auth/DeviceFlow/Request-GitHubAccessToken.ps1 +++ b/src/GitHub/private/Auth/DeviceFlow/Request-GitHubAccessToken.ps1 @@ -42,13 +42,9 @@ 'client_id' = $ClientID } - $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($RefreshToken) - $RefreshTokenAsPlainText = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR) - [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($BSTR) - if ($PSBoundParameters.ContainsKey('RefreshToken')) { $body += @{ - 'refresh_token' = $RefreshTokenAsPlainText + 'refresh_token' = (ConvertFrom-SecureString $RefreshToken -AsPlainText) 'grant_type' = 'refresh_token' } } diff --git a/src/GitHub/public/API/Invoke-GitHubAPI.ps1 b/src/GitHub/public/API/Invoke-GitHubAPI.ps1 index f3ffbb3c6..a8430aa0e 100644 --- a/src/GitHub/public/API/Invoke-GitHubAPI.ps1 +++ b/src/GitHub/public/API/Invoke-GitHubAPI.ps1 @@ -70,25 +70,18 @@ @{ $_.Key = $_.Value } } - # Authorization handling - $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($AccessToken) - try { - $AccessTokenAsPlainText = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR) - - $authorization = switch -Regex ($AccessTokenAsPlainText) { - '^ghp_|^github_pat_' { "token $AccessTokenAsPlainText" } - '^ghu_|^gho_' { "Bearer $AccessTokenAsPlainText" } - default { - $tokenPrefix = $AccessTokenAsPlainText -replace '_.*$', '_*' - $errorMessage = "Unexpected AccessToken format: $tokenPrefix" - Write-Error $errorMessage - throw $errorMessage - } + $AccessTokenAsPlainText = ConvertFrom-SecureString $AccessToken -AsPlainText + $authorization = switch -Regex ($AccessTokenAsPlainText) { + '^ghp_|^github_pat_' { "token $AccessTokenAsPlainText" } + '^ghu_|^gho_' { "Bearer $AccessTokenAsPlainText" } + default { + $tokenPrefix = $AccessTokenAsPlainText -replace '_.*$', '_*' + $errorMessage = "Unexpected AccessToken format: $tokenPrefix" + Write-Error $errorMessage + throw $errorMessage } - $headers['Authorization'] = $authorization - } finally { - [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($BSTR) } + $headers['Authorization'] = $authorization $URI = ("$ApiBaseUri/" -replace '/$', '') + ("/$ApiEndpoint" -replace '^/', '') diff --git a/tools/utilities/Local-Testing.ps1 b/tools/utilities/Local-Testing.ps1 index 22848b359..b699fc167 100644 --- a/tools/utilities/Local-Testing.ps1 +++ b/tools/utilities/Local-Testing.ps1 @@ -16,11 +16,9 @@ Import-Module -Name GitHub -Verbose Clear-Host Get-Command -Module GitHub Get-Variable | Where-Object -Property Module -NE $null | Select-Object Name, Module, ModuleName -Connect-GitHubAccount +Connect-GitHubAccount -Mode OAuthApp Get-GitHubConfig -Name AccessToken -AsPlainText Get-GitHubConfig -Name AccessTokenExpirationDate -AsPlainText Get-GitHubConfig -Name RefreshToken -AsPlainText Get-GitHubConfig -Name RefreshTokenExpirationDate -AsPlainText - Disconnect-GitHubAccount -Verbose -Reset-GitHubConfig -Verbose From ab98315549db966dfcf4eb16913e1533a8dd5099 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Mon, 25 Sep 2023 01:19:45 +0200 Subject: [PATCH 29/45] Add debug to the auth calls --- .../private/Auth/DeviceFlow/Request-GitHubAccessToken.ps1 | 2 ++ tools/utilities/Local-Testing.ps1 | 1 + 2 files changed, 3 insertions(+) diff --git a/src/GitHub/private/Auth/DeviceFlow/Request-GitHubAccessToken.ps1 b/src/GitHub/private/Auth/DeviceFlow/Request-GitHubAccessToken.ps1 index e3f3ffdf0..171d5bbe1 100644 --- a/src/GitHub/private/Auth/DeviceFlow/Request-GitHubAccessToken.ps1 +++ b/src/GitHub/private/Auth/DeviceFlow/Request-GitHubAccessToken.ps1 @@ -67,6 +67,8 @@ Write-Verbose ($RESTParams.GetEnumerator() | Out-String) $tokenResponse = Invoke-RestMethod @RESTParams -Verbose:$false + + Write-Verbose ($tokenResponse | ConvertTo-Json | Out-String) return $tokenResponse } catch { Write-Error $_ diff --git a/tools/utilities/Local-Testing.ps1 b/tools/utilities/Local-Testing.ps1 index b699fc167..e59a711f9 100644 --- a/tools/utilities/Local-Testing.ps1 +++ b/tools/utilities/Local-Testing.ps1 @@ -16,6 +16,7 @@ Import-Module -Name GitHub -Verbose Clear-Host Get-Command -Module GitHub Get-Variable | Where-Object -Property Module -NE $null | Select-Object Name, Module, ModuleName +Connect-GitHubAccount Connect-GitHubAccount -Mode OAuthApp Get-GitHubConfig -Name AccessToken -AsPlainText Get-GitHubConfig -Name AccessTokenExpirationDate -AsPlainText From 56e9af4fb2f29bce830e3f9700a4b930249ecd0f Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Mon, 25 Sep 2023 01:29:13 +0200 Subject: [PATCH 30/45] Fix logon --- .../public/Auth/Connect-GitHubAccount.ps1 | 31 +++++++++++++------ 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/src/GitHub/public/Auth/Connect-GitHubAccount.ps1 b/src/GitHub/public/Auth/Connect-GitHubAccount.ps1 index 1d6c7b42a..af33807bf 100644 --- a/src/GitHub/public/Auth/Connect-GitHubAccount.ps1 +++ b/src/GitHub/public/Auth/Connect-GitHubAccount.ps1 @@ -104,15 +104,28 @@ } } Reset-GitHubConfig -Scope 'Auth' - $settings = @{ - AccessToken = ConvertTo-SecureString -AsPlainText $tokenResponse.access_token - AccessTokenExpirationDate = (Get-Date).AddSeconds($tokenResponse.expires_in) - RefreshToken = ConvertTo-SecureString -AsPlainText $tokenResponse.refresh_token - RefreshTokenExpirationDate = (Get-Date).AddSeconds($tokenResponse.refresh_token_expires_in) - Scope = $tokenResponse.scope - AuthType = $AuthType - AccessTokenType = $tokenResponse.access_token -replace '_.*$', '_*' - DeviceFlowType = $Mode + switch ($Mode) { + 'GitHubApp' { + $settings = @{ + AccessToken = ConvertTo-SecureString -AsPlainText $tokenResponse.access_token + AccessTokenExpirationDate = (Get-Date).AddSeconds($tokenResponse.expires_in) + RefreshToken = ConvertTo-SecureString -AsPlainText $tokenResponse.refresh_token + RefreshTokenExpirationDate = (Get-Date).AddSeconds($tokenResponse.refresh_token_expires_in) + Scope = $tokenResponse.scope + AuthType = $AuthType + AccessTokenType = $tokenResponse.access_token -replace '_.*$', '_*' + DeviceFlowType = $Mode + } + } + 'OAuthApp' { + $settings = @{ + AccessToken = ConvertTo-SecureString -AsPlainText $tokenResponse.access_token + Scope = $tokenResponse.scope + AuthType = $AuthType + AccessTokenType = $tokenResponse.access_token -replace '_.*$', '_*' + DeviceFlowType = $Mode + } + } } Set-GitHubConfig @settings break From 692b79ae0b40d73e3a90c7dca6643d59d9e09a0d Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Mon, 25 Sep 2023 01:45:03 +0200 Subject: [PATCH 31/45] fix API calls and add ability to get all config --- src/GitHub/public/API/Invoke-GitHubAPI.ps1 | 6 +++--- src/GitHub/public/Config/Get-GitHubConfig.ps1 | 10 ++++++++-- tools/utilities/Local-Testing.ps1 | 1 + 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/GitHub/public/API/Invoke-GitHubAPI.ps1 b/src/GitHub/public/API/Invoke-GitHubAPI.ps1 index a8430aa0e..ad13e3109 100644 --- a/src/GitHub/public/API/Invoke-GitHubAPI.ps1 +++ b/src/GitHub/public/API/Invoke-GitHubAPI.ps1 @@ -81,7 +81,7 @@ throw $errorMessage } } - $headers['Authorization'] = $authorization + $headers.Authorization = $authorization $URI = ("$ApiBaseUri/" -replace '/$', '') + ("/$ApiEndpoint" -replace '^/', '') @@ -93,9 +93,9 @@ if ($Body) { if ($Body -is [string]) { - $APICall['Body'] = $Body + $APICall.Body = $Body } else { - $APICall['Body'] = $Body | ConvertTo-Json -Depth 100 + $APICall.Body = $Body | ConvertTo-Json -Depth 100 } } diff --git a/src/GitHub/public/Config/Get-GitHubConfig.ps1 b/src/GitHub/public/Config/Get-GitHubConfig.ps1 index 2f5d3394d..8de2774f3 100644 --- a/src/GitHub/public/Config/Get-GitHubConfig.ps1 +++ b/src/GitHub/public/Config/Get-GitHubConfig.ps1 @@ -22,6 +22,12 @@ [switch] $AsPlainText ) $prefix = $script:SecretVault.Prefix - $Name = "$prefix$Name" - Get-Secret -Name $Name -Vault $script:SecretVault.Name -AsPlainText:$AsPlainText + if ($Name) { + $Name = "$prefix$Name" + Get-Secret -Name $Name -Vault $script:SecretVault.Name -AsPlainText:$AsPlainText + } else { + Get-SecretInfo | Where-Object Name -like "$prefix*" | ForEach-Object { + Get-Secret -Name $_.Name -Vault $script:SecretVault.Name -AsPlainText:$AsPlainText + } + } } diff --git a/tools/utilities/Local-Testing.ps1 b/tools/utilities/Local-Testing.ps1 index e59a711f9..7e1a7e773 100644 --- a/tools/utilities/Local-Testing.ps1 +++ b/tools/utilities/Local-Testing.ps1 @@ -22,4 +22,5 @@ Get-GitHubConfig -Name AccessToken -AsPlainText Get-GitHubConfig -Name AccessTokenExpirationDate -AsPlainText Get-GitHubConfig -Name RefreshToken -AsPlainText Get-GitHubConfig -Name RefreshTokenExpirationDate -AsPlainText +Get-GitHubConfig -Name ApiBaseUri -AsPlainText Disconnect-GitHubAccount -Verbose From c57c65152ecc8d393dbe3532ca99b02f87469d5e Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Mon, 25 Sep 2023 01:53:33 +0200 Subject: [PATCH 32/45] Fix API stuff --- src/GitHub/public/API/Invoke-GitHubAPI.ps1 | 28 +++++++++++++--------- tools/utilities/Local-Testing.ps1 | 1 + 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/src/GitHub/public/API/Invoke-GitHubAPI.ps1 b/src/GitHub/public/API/Invoke-GitHubAPI.ps1 index ad13e3109..823806a1a 100644 --- a/src/GitHub/public/API/Invoke-GitHubAPI.ps1 +++ b/src/GitHub/public/API/Invoke-GitHubAPI.ps1 @@ -59,21 +59,28 @@ $functionName = $MyInvocation.MyCommand.Name - $headers = @{ - 'Content-Type' = $ContentType - 'X-GitHub-Api-Version' = $Version - 'Accept' = $Accept + $headers = @{} + + if (-not [string]::IsNullOrEmpty($ContentType)) { + $headers.'Content-Type' = $ContentType + } + + if (-not [string]::IsNullOrEmpty($Accept)) { + $headers.Accept = $Accept } - # Filter out null or empty headers - $headers = $headers.GetEnumerator() | Where-Object { -not [string]::IsNullOrEmpty($_.Value) } | ForEach-Object { - @{ $_.Key = $_.Value } + if (-not [string]::IsNullOrEmpty($Version)) { + $headers.'X-GitHub-Api-Version' = $Version } $AccessTokenAsPlainText = ConvertFrom-SecureString $AccessToken -AsPlainText - $authorization = switch -Regex ($AccessTokenAsPlainText) { - '^ghp_|^github_pat_' { "token $AccessTokenAsPlainText" } - '^ghu_|^gho_' { "Bearer $AccessTokenAsPlainText" } + switch -Regex ($AccessTokenAsPlainText) { + '^ghp_|^github_pat_' { + $headers.authorization = "token $AccessTokenAsPlainText" + } + '^ghu_|^gho_' { + $headers.authorization = "Bearer $AccessTokenAsPlainText" + } default { $tokenPrefix = $AccessTokenAsPlainText -replace '_.*$', '_*' $errorMessage = "Unexpected AccessToken format: $tokenPrefix" @@ -81,7 +88,6 @@ throw $errorMessage } } - $headers.Authorization = $authorization $URI = ("$ApiBaseUri/" -replace '/$', '') + ("/$ApiEndpoint" -replace '^/', '') diff --git a/tools/utilities/Local-Testing.ps1 b/tools/utilities/Local-Testing.ps1 index 7e1a7e773..f9ef2e6e8 100644 --- a/tools/utilities/Local-Testing.ps1 +++ b/tools/utilities/Local-Testing.ps1 @@ -23,4 +23,5 @@ Get-GitHubConfig -Name AccessTokenExpirationDate -AsPlainText Get-GitHubConfig -Name RefreshToken -AsPlainText Get-GitHubConfig -Name RefreshTokenExpirationDate -AsPlainText Get-GitHubConfig -Name ApiBaseUri -AsPlainText +Invoke-GitHubAPI -Method Get -ApiEndpoint / Disconnect-GitHubAccount -Verbose From 3c06ccab14e0bf37c5d1a5b6ea2dcdb53299bcfa Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Mon, 25 Sep 2023 02:04:16 +0200 Subject: [PATCH 33/45] API fixes --- src/GitHub/public/API/Invoke-GitHubAPI.ps1 | 13 +++++---- .../public/Auth/Connect-GitHubAccount.ps1 | 28 ++++++++++++------- tools/utilities/Local-Testing.ps1 | 2 +- 3 files changed, 26 insertions(+), 17 deletions(-) diff --git a/src/GitHub/public/API/Invoke-GitHubAPI.ps1 b/src/GitHub/public/API/Invoke-GitHubAPI.ps1 index 823806a1a..11b7ab9a1 100644 --- a/src/GitHub/public/API/Invoke-GitHubAPI.ps1 +++ b/src/GitHub/public/API/Invoke-GitHubAPI.ps1 @@ -112,14 +112,15 @@ # Parse Data if ($response -is [System.Array]) { $response | ForEach-Object { - Write-Output $_ + $_ } } elseif ($response) { - $response.PSObject.Properties | Where-Object { - $_.Name -notin @('incomplete_results', 'repository_selection', 'total_count') - } | ForEach-Object { - Write-Output $_.Value - } + $_ + # $response.PSObject.Properties | Where-Object { + # $_.Name -notin @('incomplete_results', 'repository_selection', 'total_count') + # } | ForEach-Object { + # Write-Output $_.Value + # } } # Extract next page's URL from Link header if exists diff --git a/src/GitHub/public/Auth/Connect-GitHubAccount.ps1 b/src/GitHub/public/Auth/Connect-GitHubAccount.ps1 index af33807bf..f6d279cfa 100644 --- a/src/GitHub/public/Auth/Connect-GitHubAccount.ps1 +++ b/src/GitHub/public/Auth/Connect-GitHubAccount.ps1 @@ -109,21 +109,25 @@ $settings = @{ AccessToken = ConvertTo-SecureString -AsPlainText $tokenResponse.access_token AccessTokenExpirationDate = (Get-Date).AddSeconds($tokenResponse.expires_in) + AccessTokenType = $tokenResponse.access_token -replace '_.*$', '_*' + ApiBaseUri = 'https://api.github.com' + ApiVersion = '2022-11-28' + AuthType = $AuthType + DeviceFlowType = $Mode RefreshToken = ConvertTo-SecureString -AsPlainText $tokenResponse.refresh_token RefreshTokenExpirationDate = (Get-Date).AddSeconds($tokenResponse.refresh_token_expires_in) Scope = $tokenResponse.scope - AuthType = $AuthType - AccessTokenType = $tokenResponse.access_token -replace '_.*$', '_*' - DeviceFlowType = $Mode } } 'OAuthApp' { $settings = @{ - AccessToken = ConvertTo-SecureString -AsPlainText $tokenResponse.access_token - Scope = $tokenResponse.scope - AuthType = $AuthType - AccessTokenType = $tokenResponse.access_token -replace '_.*$', '_*' - DeviceFlowType = $Mode + AccessToken = ConvertTo-SecureString -AsPlainText $tokenResponse.access_token + AccessTokenType = $tokenResponse.access_token -replace '_.*$', '_*' + ApiBaseUri = 'https://api.github.com' + ApiVersion = '2022-11-28' + AuthType = $AuthType + DeviceFlowType = $Mode + Scope = $tokenResponse.scope } } } @@ -141,9 +145,11 @@ Write-Host "Unexpected access token format: $prefix" } $settings = @{ - AuthType = $AuthType AccessToken = $accessToken AccessTokenType = $prefix + ApiBaseUri = 'https://api.github.com' + ApiVersion = '2022-11-28' + AuthType = $AuthType } Set-GitHubConfig @settings break @@ -153,9 +159,11 @@ Reset-GitHubConfig -Scope 'Auth' $prefix = $envVar.Value -replace '_.*$', '_*' $settings = @{ - AuthType = 'sPAT' AccessToken = ConvertTo-SecureString -AsPlainText $envVar.Value AccessTokenType = $prefix + ApiBaseUri = 'https://api.github.com' + ApiVersion = '2022-11-28' + AuthType = 'sPAT' } Set-GitHubConfig @settings } diff --git a/tools/utilities/Local-Testing.ps1 b/tools/utilities/Local-Testing.ps1 index f9ef2e6e8..67fdc8a2f 100644 --- a/tools/utilities/Local-Testing.ps1 +++ b/tools/utilities/Local-Testing.ps1 @@ -23,5 +23,5 @@ Get-GitHubConfig -Name AccessTokenExpirationDate -AsPlainText Get-GitHubConfig -Name RefreshToken -AsPlainText Get-GitHubConfig -Name RefreshTokenExpirationDate -AsPlainText Get-GitHubConfig -Name ApiBaseUri -AsPlainText -Invoke-GitHubAPI -Method Get -ApiEndpoint / +Invoke-GitHubAPI -Method Get -ApiEndpoint /user Disconnect-GitHubAccount -Verbose From 2fc8b2fd1c79259b718968d4fa3616b59f532c63 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Mon, 25 Sep 2023 02:09:50 +0200 Subject: [PATCH 34/45] Troubleshooting the api function --- src/GitHub/public/API/Invoke-GitHubAPI.ps1 | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/src/GitHub/public/API/Invoke-GitHubAPI.ps1 b/src/GitHub/public/API/Invoke-GitHubAPI.ps1 index 11b7ab9a1..6dd5eabee 100644 --- a/src/GitHub/public/API/Invoke-GitHubAPI.ps1 +++ b/src/GitHub/public/API/Invoke-GitHubAPI.ps1 @@ -105,23 +105,16 @@ } } + Write-Verbose "[$functionName] - API call: " + Write-Verbose ($APICall | ConvertTo-Json -Depth 100) + try { do { $response = Invoke-RestMethod @APICall - # Parse Data - if ($response -is [System.Array]) { - $response | ForEach-Object { - $_ - } - } elseif ($response) { - $_ - # $response.PSObject.Properties | Where-Object { - # $_.Name -notin @('incomplete_results', 'repository_selection', 'total_count') - # } | ForEach-Object { - # Write-Output $_.Value - # } - } + $response + Write-Verbose "[$functionName] - Response: " + Write-Verbose ($response | ConvertTo-Json -Depth 100) # Extract next page's URL from Link header if exists $nextLink = $null From 96329b7d877ecf138ffc8be3d73f941da5d88d6f Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Mon, 25 Sep 2023 02:17:04 +0200 Subject: [PATCH 35/45] Fixes --- src/GitHub/public/API/Invoke-GitHubAPI.ps1 | 9 +++++---- tools/utilities/Local-Testing.ps1 | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/GitHub/public/API/Invoke-GitHubAPI.ps1 b/src/GitHub/public/API/Invoke-GitHubAPI.ps1 index 6dd5eabee..3a865edc4 100644 --- a/src/GitHub/public/API/Invoke-GitHubAPI.ps1 +++ b/src/GitHub/public/API/Invoke-GitHubAPI.ps1 @@ -105,16 +105,17 @@ } } - Write-Verbose "[$functionName] - API call: " - Write-Verbose ($APICall | ConvertTo-Json -Depth 100) + # Write-Verbose "[$functionName] - API call: " + # Write-Verbose ($APICall | ConvertTo-Json -Depth 100) try { do { $response = Invoke-RestMethod @APICall $response - Write-Verbose "[$functionName] - Response: " - Write-Verbose ($response | ConvertTo-Json -Depth 100) + + # Write-Verbose "[$functionName] - Response: " + # Write-Verbose ($response | ConvertTo-Json -Depth 100) # Extract next page's URL from Link header if exists $nextLink = $null diff --git a/tools/utilities/Local-Testing.ps1 b/tools/utilities/Local-Testing.ps1 index 67fdc8a2f..f9ef2e6e8 100644 --- a/tools/utilities/Local-Testing.ps1 +++ b/tools/utilities/Local-Testing.ps1 @@ -23,5 +23,5 @@ Get-GitHubConfig -Name AccessTokenExpirationDate -AsPlainText Get-GitHubConfig -Name RefreshToken -AsPlainText Get-GitHubConfig -Name RefreshTokenExpirationDate -AsPlainText Get-GitHubConfig -Name ApiBaseUri -AsPlainText -Invoke-GitHubAPI -Method Get -ApiEndpoint /user +Invoke-GitHubAPI -Method Get -ApiEndpoint / Disconnect-GitHubAccount -Verbose From 520eac6dcf8fdd782db4ad79abde61718edbb631 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Mon, 25 Sep 2023 17:04:46 +0200 Subject: [PATCH 36/45] Remove POC files --- src/GitHub/scripts/1-InitializeModule.ps1 | 3 --- src/GitHub/scripts/2-Prereqs.ps1 | 3 --- 2 files changed, 6 deletions(-) delete mode 100644 src/GitHub/scripts/1-InitializeModule.ps1 delete mode 100644 src/GitHub/scripts/2-Prereqs.ps1 diff --git a/src/GitHub/scripts/1-InitializeModule.ps1 b/src/GitHub/scripts/1-InitializeModule.ps1 deleted file mode 100644 index da7dc2b46..000000000 --- a/src/GitHub/scripts/1-InitializeModule.ps1 +++ /dev/null @@ -1,3 +0,0 @@ -$scriptFilePath = $MyInvocation.MyCommand.Path - -Write-Verbose "[$scriptFilePath] - Initializing GitHub module..." -Verbose diff --git a/src/GitHub/scripts/2-Prereqs.ps1 b/src/GitHub/scripts/2-Prereqs.ps1 deleted file mode 100644 index 3da5221e9..000000000 --- a/src/GitHub/scripts/2-Prereqs.ps1 +++ /dev/null @@ -1,3 +0,0 @@ -$scriptFilePath = $MyInvocation.MyCommand.Path - -Write-Verbose "[$scriptFilePath] - Loading prereqs..." -Verbose From 3b6b081109898a982b2caa7635b0fa9acdb28275 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Mon, 25 Sep 2023 17:35:48 +0200 Subject: [PATCH 37/45] Adding Meta component --- .../public/Meta/Get-GitHubAPIVersions.ps1 | 18 ++++++++++++++++++ src/GitHub/public/Meta/Get-GitHubMeta.ps1 | 17 +++++++++++++++++ src/GitHub/public/Meta/Get-GitHubOctocat.ps1 | 17 +++++++++++++++++ src/GitHub/public/Meta/Get-GitHubRoot.ps1 | 17 +++++++++++++++++ src/GitHub/public/Meta/Get-GitHubZen.ps1 | 17 +++++++++++++++++ 5 files changed, 86 insertions(+) create mode 100644 src/GitHub/public/Meta/Get-GitHubAPIVersions.ps1 create mode 100644 src/GitHub/public/Meta/Get-GitHubMeta.ps1 create mode 100644 src/GitHub/public/Meta/Get-GitHubOctocat.ps1 create mode 100644 src/GitHub/public/Meta/Get-GitHubRoot.ps1 create mode 100644 src/GitHub/public/Meta/Get-GitHubZen.ps1 diff --git a/src/GitHub/public/Meta/Get-GitHubAPIVersions.ps1 b/src/GitHub/public/Meta/Get-GitHubAPIVersions.ps1 new file mode 100644 index 000000000..ac6755dc5 --- /dev/null +++ b/src/GitHub/public/Meta/Get-GitHubAPIVersions.ps1 @@ -0,0 +1,18 @@ +function Get-GitHubAPIVersions { + <# + .NOTES + https://docs.github.com/en/rest/meta/meta?apiVersion=2022-11-28#get-all-api-versions + #> + [OutputType([string[]])] + [CmdletBinding()] + param () + + $InputObject = @{ + APIEndpoint = '/versions' + Method = 'GET' + } + + $response = Invoke-GitHubAPI @InputObject + + $response +} diff --git a/src/GitHub/public/Meta/Get-GitHubMeta.ps1 b/src/GitHub/public/Meta/Get-GitHubMeta.ps1 new file mode 100644 index 000000000..60085708e --- /dev/null +++ b/src/GitHub/public/Meta/Get-GitHubMeta.ps1 @@ -0,0 +1,17 @@ +function Get-GitHubMeta { + <# + .NOTES + https://docs.github.com/en/rest/reference/meta#github-api-root + #> + [CmdletBinding()] + param () + + $InputObject = @{ + APIEndpoint = '/meta' + Method = 'GET' + } + + $response = Invoke-GitHubAPI @InputObject -AccessToken $null + + $response +} diff --git a/src/GitHub/public/Meta/Get-GitHubOctocat.ps1 b/src/GitHub/public/Meta/Get-GitHubOctocat.ps1 new file mode 100644 index 000000000..e0633e3ab --- /dev/null +++ b/src/GitHub/public/Meta/Get-GitHubOctocat.ps1 @@ -0,0 +1,17 @@ +function Get-GitHubOctocat { + <# + .NOTES + https://docs.github.com/en/rest/reference/meta#github-api-root + #> + [CmdletBinding()] + param () + + $InputObject = @{ + APIEndpoint = '/octocat' + Method = 'GET' + } + + $Response = Invoke-GitHubAPI @InputObject + + $Response +} diff --git a/src/GitHub/public/Meta/Get-GitHubRoot.ps1 b/src/GitHub/public/Meta/Get-GitHubRoot.ps1 new file mode 100644 index 000000000..c56123230 --- /dev/null +++ b/src/GitHub/public/Meta/Get-GitHubRoot.ps1 @@ -0,0 +1,17 @@ +function Get-GitHubRoot { + <# + .NOTES + https://docs.github.com/en/rest/reference/meta#github-api-root + #> + [CmdletBinding()] + param () + + $InputObject = @{ + APIEndpoint = '/' + Method = 'GET' + } + + $response = Invoke-GitHubAPI @InputObject + + $response +} diff --git a/src/GitHub/public/Meta/Get-GitHubZen.ps1 b/src/GitHub/public/Meta/Get-GitHubZen.ps1 new file mode 100644 index 000000000..fc92ce284 --- /dev/null +++ b/src/GitHub/public/Meta/Get-GitHubZen.ps1 @@ -0,0 +1,17 @@ +function Get-GitHubZen { + <# + .NOTES + https://docs.github.com/en/rest/reference/meta#github-api-root + #> + [CmdletBinding()] + param () + + $InputObject = @{ + APIEndpoint = '/zen' + Method = 'GET' + } + + $Response = Invoke-GitHubAPI @InputObject + + $Response +} From 4cad56ec887f0def8037981a751f843e81613b02 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Mon, 25 Sep 2023 18:39:45 +0200 Subject: [PATCH 38/45] Fix Meta --- .../public/Meta/Get-GitHubAPIVersions.ps1 | 19 ++++++++++++++++--- src/GitHub/public/Meta/Get-GitHubMeta.ps1 | 2 +- tools/utilities/Local-Testing.ps1 | 1 + 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/GitHub/public/Meta/Get-GitHubAPIVersions.ps1 b/src/GitHub/public/Meta/Get-GitHubAPIVersions.ps1 index ac6755dc5..8eab87a29 100644 --- a/src/GitHub/public/Meta/Get-GitHubAPIVersions.ps1 +++ b/src/GitHub/public/Meta/Get-GitHubAPIVersions.ps1 @@ -1,7 +1,20 @@ -function Get-GitHubAPIVersions { +<# + .NOTES + https://docs.github.com/en/rest/meta/meta?apiVersion=2022-11-28#get-all-api-versions +#> +function Get-GitHubApiVersions { <# - .NOTES - https://docs.github.com/en/rest/meta/meta?apiVersion=2022-11-28#get-all-api-versions + .SYNOPSIS + Get all supported GitHub API versions. + + .DESCRIPTION + Long description + + .EXAMPLE + An example + + .NOTES + General notes #> [OutputType([string[]])] [CmdletBinding()] diff --git a/src/GitHub/public/Meta/Get-GitHubMeta.ps1 b/src/GitHub/public/Meta/Get-GitHubMeta.ps1 index 60085708e..68991e6fa 100644 --- a/src/GitHub/public/Meta/Get-GitHubMeta.ps1 +++ b/src/GitHub/public/Meta/Get-GitHubMeta.ps1 @@ -11,7 +11,7 @@ Method = 'GET' } - $response = Invoke-GitHubAPI @InputObject -AccessToken $null + $response = Invoke-GitHubAPI @InputObject $response } diff --git a/tools/utilities/Local-Testing.ps1 b/tools/utilities/Local-Testing.ps1 index f9ef2e6e8..5627c9b4a 100644 --- a/tools/utilities/Local-Testing.ps1 +++ b/tools/utilities/Local-Testing.ps1 @@ -24,4 +24,5 @@ Get-GitHubConfig -Name RefreshToken -AsPlainText Get-GitHubConfig -Name RefreshTokenExpirationDate -AsPlainText Get-GitHubConfig -Name ApiBaseUri -AsPlainText Invoke-GitHubAPI -Method Get -ApiEndpoint / +Get-GitHubMeta Disconnect-GitHubAccount -Verbose From d4a22ef28944af83e600e14837e3ff4ccc4d49f6 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Mon, 25 Sep 2023 22:27:26 +0200 Subject: [PATCH 39/45] Full test --- src/GitHub/public/API/Invoke-GitHubAPI.ps1 | 61 ++++++++----------- .../public/Auth/Connect-GitHubAccount.ps1 | 7 ++- src/GitHub/public/Config/Set-GitHubConfig.ps1 | 4 ++ .../public/Meta/Get-GitHubAPIVersions.ps1 | 30 +++++---- src/GitHub/public/Meta/Get-GitHubMeta.ps1 | 29 +++++++-- src/GitHub/public/Meta/Get-GitHubOctocat.ps1 | 45 ++++++++++++-- tools/utilities/Local-Testing.ps1 | 1 + 7 files changed, 112 insertions(+), 65 deletions(-) diff --git a/src/GitHub/public/API/Invoke-GitHubAPI.ps1 b/src/GitHub/public/API/Invoke-GitHubAPI.ps1 index 3a865edc4..61b933bda 100644 --- a/src/GitHub/public/API/Invoke-GitHubAPI.ps1 +++ b/src/GitHub/public/API/Invoke-GitHubAPI.ps1 @@ -44,6 +44,14 @@ [Parameter()] [string] $Accept, + # Specifies the HTTP version used for the request. + [Parameter()] + $HttpVersion = '2.0', + + # Support Pagination Relation Links per RFC5988. + [Parameter()] + $FollowRelLink = $true, + # The secure token used for authentication in the GitHub API. It should be stored as a SecureString to ensure it's kept safe in memory. [Parameter()] [SecureString] $AccessToken = (Get-GitHubConfig -Name AccessToken), @@ -59,24 +67,19 @@ $functionName = $MyInvocation.MyCommand.Name - $headers = @{} + $headers = @{ - if (-not [string]::IsNullOrEmpty($ContentType)) { - $headers.'Content-Type' = $ContentType + Accept = $Accept + 'X-GitHub-Api-Version' = $Version } - if (-not [string]::IsNullOrEmpty($Accept)) { - $headers.Accept = $Accept - } - - if (-not [string]::IsNullOrEmpty($Version)) { - $headers.'X-GitHub-Api-Version' = $Version - } + ($headers.GetEnumerator() | Where-Object { -not $_.Value }) | ForEach-Object { $headers.Remove($_.Name) } $AccessTokenAsPlainText = ConvertFrom-SecureString $AccessToken -AsPlainText + # Swap out this by using the -Authentication Bearer -Token $AccessToken switch -Regex ($AccessTokenAsPlainText) { '^ghp_|^github_pat_' { - $headers.authorization = "token $AccessTokenAsPlainText" + $headers.authorization = "Bearer $AccessTokenAsPlainText" } '^ghu_|^gho_' { $headers.authorization = "Bearer $AccessTokenAsPlainText" @@ -92,11 +95,19 @@ $URI = ("$ApiBaseUri/" -replace '/$', '') + ("/$ApiEndpoint" -replace '^/', '') $APICall = @{ - Uri = $URI - Method = $Method - Headers = $Headers + Uri = $URI + Method = $Method + Headers = $Headers + ContentType = $ContentType + HttpVersion = $HttpVersion + FollowRelLink = $FollowRelLink + SessionVariable = 'Session' + StatusCodeVariable = 'StatusCode' + ResponseHeadersVariable = 'ResponseHeaders' } + + if ($Body) { if ($Body -is [string]) { $APICall.Body = $Body @@ -105,29 +116,9 @@ } } - # Write-Verbose "[$functionName] - API call: " - # Write-Verbose ($APICall | ConvertTo-Json -Depth 100) - try { - do { - $response = Invoke-RestMethod @APICall - - $response - - # Write-Verbose "[$functionName] - Response: " - # Write-Verbose ($response | ConvertTo-Json -Depth 100) - - # Extract next page's URL from Link header if exists - $nextLink = $null - if ($response.Headers.Link -match '<(?[^>]+)>;\s*rel="next"') { - $nextLink = $matches['url'] - } - - if ($nextLink) { - $APICall.Uri = $nextLink - } - } while ($nextLink) + Invoke-RestMethod @APICall | Write-Output } catch [System.Net.WebException] { Write-Error "[$functionName] - WebException - $($_.Exception.Message)" diff --git a/src/GitHub/public/Auth/Connect-GitHubAccount.ps1 b/src/GitHub/public/Auth/Connect-GitHubAccount.ps1 index f6d279cfa..da99ded35 100644 --- a/src/GitHub/public/Auth/Connect-GitHubAccount.ps1 +++ b/src/GitHub/public/Auth/Connect-GitHubAccount.ps1 @@ -138,14 +138,15 @@ Write-Verbose 'Logging in using personal access token...' Reset-GitHubConfig -Scope 'Auth' Write-Host '! ' -ForegroundColor DarkYellow -NoNewline - $accessToken = Read-Host -Prompt 'Enter your personal access token' -AsSecureString - $prefix = (ConvertFrom-SecureString $accessToken -AsPlainText) -replace '_.*$', '_*' + Start-Process 'https://github.com/settings/tokens' + $accessTokenValue = Read-Host -Prompt 'Enter your personal access token' -AsSecureString + $prefix = (ConvertFrom-SecureString $accessTokenValue -AsPlainText) -replace '_.*$', '_*' if ($prefix -notmatch '^ghp_|^github_pat_') { Write-Host '⚠ ' -ForegroundColor Yellow -NoNewline Write-Host "Unexpected access token format: $prefix" } $settings = @{ - AccessToken = $accessToken + AccessToken = $accessTokenValue AccessTokenType = $prefix ApiBaseUri = 'https://api.github.com' ApiVersion = '2022-11-28' diff --git a/src/GitHub/public/Config/Set-GitHubConfig.ps1 b/src/GitHub/public/Config/Set-GitHubConfig.ps1 index 5f107a360..dd128166a 100644 --- a/src/GitHub/public/Config/Set-GitHubConfig.ps1 +++ b/src/GitHub/public/Config/Set-GitHubConfig.ps1 @@ -82,6 +82,10 @@ $prefix = $script:SecretVault.Prefix + #All timestamps return in UTC time, ISO 8601 format: YYYY-MM-DDTHH:MM:SSZ + #Also: use Set-Secret -NAme ... -Value ... -Metadata @{Type = 'DateTime'} to set a datetime value + # https://learn.microsoft.com/en-us/powershell/utility-modules/secretmanagement/how-to/manage-secretstore?view=ps-modules#adding-metadata + switch ($PSBoundParameters.Keys) { 'AccessToken' { Set-Secret -Name "$prefix`AccessToken" -SecureStringSecret $AccessToken -Vault $script:SecretVault.Name diff --git a/src/GitHub/public/Meta/Get-GitHubAPIVersions.ps1 b/src/GitHub/public/Meta/Get-GitHubAPIVersions.ps1 index 8eab87a29..c9bfe25f9 100644 --- a/src/GitHub/public/Meta/Get-GitHubAPIVersions.ps1 +++ b/src/GitHub/public/Meta/Get-GitHubAPIVersions.ps1 @@ -1,31 +1,29 @@ -<# - .NOTES - https://docs.github.com/en/rest/meta/meta?apiVersion=2022-11-28#get-all-api-versions -#> -function Get-GitHubApiVersions { +function Get-GitHubApiVersions { <# - .SYNOPSIS - Get all supported GitHub API versions. + .SYNOPSIS + Get all supported GitHub API versions. - .DESCRIPTION - Long description + .DESCRIPTION + Get all supported GitHub API versions. - .EXAMPLE - An example + .EXAMPLE + Get-GitHubApiVersions - .NOTES - General notes + Get all supported GitHub API versions. + + .NOTES + https://docs.github.com/en/rest/meta/meta?apiVersion=2022-11-28#get-all-api-versions #> [OutputType([string[]])] [CmdletBinding()] param () - $InputObject = @{ - APIEndpoint = '/versions' + $inputObject = @{ + ApiEndpoint = '/versions' Method = 'GET' } - $response = Invoke-GitHubAPI @InputObject + $response = Invoke-GitHubAPI @inputObject $response } diff --git a/src/GitHub/public/Meta/Get-GitHubMeta.ps1 b/src/GitHub/public/Meta/Get-GitHubMeta.ps1 index 68991e6fa..7837725a9 100644 --- a/src/GitHub/public/Meta/Get-GitHubMeta.ps1 +++ b/src/GitHub/public/Meta/Get-GitHubMeta.ps1 @@ -1,17 +1,36 @@ function Get-GitHubMeta { <# - .NOTES - https://docs.github.com/en/rest/reference/meta#github-api-root + .SYNOPSIS + Returns meta information about GitHub, including a list of GitHub's IP addresses. + + .DESCRIPTION + Returns meta information about GitHub, including a list of GitHub's IP addresses. + + The API's response also includes a list of GitHub's domain names. + + The values shown in the documentation's response are example values. You must always query the API directly to get the latest values. + + Note: This endpoint returns both IPv4 and IPv6 addresses. However, not all features support IPv6. You should refer to the specific documentation for each feature to determine if IPv6 is supported. + + .EXAMPLE + Get-GitHubMeta + + Returns meta information about GitHub, including a list of GitHub's IP addresses. + + .NOTES + https://docs.github.com/en/rest/meta/meta?apiVersion=2022-11-28#github-api-root + https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/about-githubs-ip-addresses #> + [OutputType([object])] [CmdletBinding()] param () - $InputObject = @{ - APIEndpoint = '/meta' + $inputObject = @{ + ApiEndpoint = '/meta' Method = 'GET' } - $response = Invoke-GitHubAPI @InputObject + $response = Invoke-GitHubAPI @inputObject $response } diff --git a/src/GitHub/public/Meta/Get-GitHubOctocat.ps1 b/src/GitHub/public/Meta/Get-GitHubOctocat.ps1 index e0633e3ab..9c9eeecef 100644 --- a/src/GitHub/public/Meta/Get-GitHubOctocat.ps1 +++ b/src/GitHub/public/Meta/Get-GitHubOctocat.ps1 @@ -1,17 +1,50 @@ function Get-GitHubOctocat { <# + .SYNOPSIS + Get the octocat as ASCII art + + .DESCRIPTION + Get the octocat as ASCII art + + .EXAMPLE + Get-GitHubOctocat + + Get the octocat as ASCII art + + .EXAMPLE + Get-GitHubOctocat -S 'The glass is never half empty. It's just twice as big as it needs to be.' + + Get the octocat as ASCII art with a custom saying + .NOTES - https://docs.github.com/en/rest/reference/meta#github-api-root + https://docs.github.com/en/rest/meta/meta?apiVersion=2022-11-28#get-octocat #> + [OutputType([string])] [CmdletBinding()] - param () + param ( + # The words to show in Octocat's speech bubble + [Parameter()] + [Alias('Say')] + [Alias('Saying')] + [string] + $S = "The glass is never half empty. Its just twice as big as it needs to be." + ) + + # $query = [System.Web.HttpUtility]::UrlEncode($S) + # $query = [System.Uri]::EscapeDataString($S) + + + $body = @{ + s = $S + } - $InputObject = @{ - APIEndpoint = '/octocat' + $inputObject = @{ + APIEndpoint = "/octocat?s=$query" Method = 'GET' + Body = $body } - $Response = Invoke-GitHubAPI @InputObject + $response = Invoke-GitHubAPI @inputObject - $Response + $response } diff --git a/tools/utilities/Local-Testing.ps1 b/tools/utilities/Local-Testing.ps1 index 5627c9b4a..6651bdea1 100644 --- a/tools/utilities/Local-Testing.ps1 +++ b/tools/utilities/Local-Testing.ps1 @@ -18,6 +18,7 @@ Get-Command -Module GitHub Get-Variable | Where-Object -Property Module -NE $null | Select-Object Name, Module, ModuleName Connect-GitHubAccount Connect-GitHubAccount -Mode OAuthApp +Connect-GitHubAccount -AccessToken Get-GitHubConfig -Name AccessToken -AsPlainText Get-GitHubConfig -Name AccessTokenExpirationDate -AsPlainText Get-GitHubConfig -Name RefreshToken -AsPlainText From 6a1a4d789679c7fdc829a290867430c46b9aab4b Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Mon, 25 Sep 2023 22:35:55 +0200 Subject: [PATCH 40/45] Check the response headers --- src/GitHub/public/API/Invoke-GitHubAPI.ps1 | 22 +++++++++++--------- src/GitHub/public/Meta/Get-GitHubOctocat.ps1 | 2 +- tools/utilities/Local-Testing.ps1 | 1 + 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/GitHub/public/API/Invoke-GitHubAPI.ps1 b/src/GitHub/public/API/Invoke-GitHubAPI.ps1 index 61b933bda..d929b5912 100644 --- a/src/GitHub/public/API/Invoke-GitHubAPI.ps1 +++ b/src/GitHub/public/API/Invoke-GitHubAPI.ps1 @@ -95,19 +95,16 @@ $URI = ("$ApiBaseUri/" -replace '/$', '') + ("/$ApiEndpoint" -replace '^/', '') $APICall = @{ - Uri = $URI - Method = $Method - Headers = $Headers - ContentType = $ContentType - HttpVersion = $HttpVersion - FollowRelLink = $FollowRelLink - SessionVariable = 'Session' - StatusCodeVariable = 'StatusCode' + Uri = $URI + Method = $Method + Headers = $Headers + ContentType = $ContentType + HttpVersion = $HttpVersion + FollowRelLink = $FollowRelLink + StatusCodeVariable = 'StatusCode' ResponseHeadersVariable = 'ResponseHeaders' } - - if ($Body) { if ($Body -is [string]) { $APICall.Body = $Body @@ -120,6 +117,11 @@ Invoke-RestMethod @APICall | Write-Output + Write-Verbose ($ResponseHeaders | ConvertTo-Json -Depth 100) + Write-Verbose ($StatusCode | ConvertTo-Json -Depth 100) + + + } catch [System.Net.WebException] { Write-Error "[$functionName] - WebException - $($_.Exception.Message)" throw $_ diff --git a/src/GitHub/public/Meta/Get-GitHubOctocat.ps1 b/src/GitHub/public/Meta/Get-GitHubOctocat.ps1 index 9c9eeecef..cbe675cd6 100644 --- a/src/GitHub/public/Meta/Get-GitHubOctocat.ps1 +++ b/src/GitHub/public/Meta/Get-GitHubOctocat.ps1 @@ -39,7 +39,7 @@ } $inputObject = @{ - APIEndpoint = "/octocat?s=$query" + APIEndpoint = "/octocat" Method = 'GET' Body = $body } diff --git a/tools/utilities/Local-Testing.ps1 b/tools/utilities/Local-Testing.ps1 index 6651bdea1..edca7c500 100644 --- a/tools/utilities/Local-Testing.ps1 +++ b/tools/utilities/Local-Testing.ps1 @@ -26,4 +26,5 @@ Get-GitHubConfig -Name RefreshTokenExpirationDate -AsPlainText Get-GitHubConfig -Name ApiBaseUri -AsPlainText Invoke-GitHubAPI -Method Get -ApiEndpoint / Get-GitHubMeta +Get-GitHubOctocat -Say 'Hello, World!' Disconnect-GitHubAccount -Verbose From 0088de471b41a7bc26aaa0c612d15926a5ca2a3f Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Mon, 25 Sep 2023 22:44:33 +0200 Subject: [PATCH 41/45] Test API --- src/GitHub/public/API/Invoke-GitHubAPI.ps1 | 27 +++------------------- 1 file changed, 3 insertions(+), 24 deletions(-) diff --git a/src/GitHub/public/API/Invoke-GitHubAPI.ps1 b/src/GitHub/public/API/Invoke-GitHubAPI.ps1 index d929b5912..4dbc66c65 100644 --- a/src/GitHub/public/API/Invoke-GitHubAPI.ps1 +++ b/src/GitHub/public/API/Invoke-GitHubAPI.ps1 @@ -68,36 +68,20 @@ $functionName = $MyInvocation.MyCommand.Name $headers = @{ - Accept = $Accept 'X-GitHub-Api-Version' = $Version } ($headers.GetEnumerator() | Where-Object { -not $_.Value }) | ForEach-Object { $headers.Remove($_.Name) } - $AccessTokenAsPlainText = ConvertFrom-SecureString $AccessToken -AsPlainText - # Swap out this by using the -Authentication Bearer -Token $AccessToken - switch -Regex ($AccessTokenAsPlainText) { - '^ghp_|^github_pat_' { - $headers.authorization = "Bearer $AccessTokenAsPlainText" - } - '^ghu_|^gho_' { - $headers.authorization = "Bearer $AccessTokenAsPlainText" - } - default { - $tokenPrefix = $AccessTokenAsPlainText -replace '_.*$', '_*' - $errorMessage = "Unexpected AccessToken format: $tokenPrefix" - Write-Error $errorMessage - throw $errorMessage - } - } - $URI = ("$ApiBaseUri/" -replace '/$', '') + ("/$ApiEndpoint" -replace '^/', '') $APICall = @{ Uri = $URI Method = $Method Headers = $Headers + Authentication = 'Bearer' + Token = $AccessToken ContentType = $ContentType HttpVersion = $HttpVersion FollowRelLink = $FollowRelLink @@ -114,14 +98,9 @@ } try { - Invoke-RestMethod @APICall | Write-Output - - Write-Verbose ($ResponseHeaders | ConvertTo-Json -Depth 100) Write-Verbose ($StatusCode | ConvertTo-Json -Depth 100) - - - + Write-Verbose ($ResponseHeaders | ConvertTo-Json -Depth 100) } catch [System.Net.WebException] { Write-Error "[$functionName] - WebException - $($_.Exception.Message)" throw $_ From 2ef05904f8a0f3f920dbb81a9faa76301f4320a3 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Mon, 25 Sep 2023 22:56:39 +0200 Subject: [PATCH 42/45] Final testing stuff --- tools/utilities/Local-Testing.ps1 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/utilities/Local-Testing.ps1 b/tools/utilities/Local-Testing.ps1 index edca7c500..ecbe3441c 100644 --- a/tools/utilities/Local-Testing.ps1 +++ b/tools/utilities/Local-Testing.ps1 @@ -26,5 +26,6 @@ Get-GitHubConfig -Name RefreshTokenExpirationDate -AsPlainText Get-GitHubConfig -Name ApiBaseUri -AsPlainText Invoke-GitHubAPI -Method Get -ApiEndpoint / Get-GitHubMeta -Get-GitHubOctocat -Say 'Hello, World!' +Get-GitHubOctocat -S 'Hello, World!' Disconnect-GitHubAccount -Verbose +$VerbosePreference = 'SIlentlyContinue' From 68d3c78c5824c74d6080de14c8fa1a60e72e6164 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Mon, 25 Sep 2023 23:09:36 +0200 Subject: [PATCH 43/45] What happends with the framework if i move it like this? --- src/GitHub.Meta/GitHub.Meta.ps1 | 3 + src/GitHub.Meta/GitHub.Meta.psd1 | 29 +++++++++ src/GitHub.Meta/GitHub.Meta.psm1 | 61 +++++++++++++++++++ .../public/Meta/Get-GitHubApiVersions.ps1} | 8 ++- .../public/Meta/Get-GitHubMeta.ps1 | 37 +++++++++++ .../public/Meta/Get-GitHubOctocat.ps1 | 11 ++-- .../public/Meta/Get-GitHubRoot.ps1 | 30 +++++++++ src/GitHub.Meta/public/Meta/Get-GitHubZen.ps1 | 30 +++++++++ src/GitHub/public/Meta/Get-GitHubMeta.ps1 | 36 ----------- src/GitHub/public/Meta/Get-GitHubRoot.ps1 | 17 ------ src/GitHub/public/Meta/Get-GitHubZen.ps1 | 17 ------ tools/utilities/GitHubAPI.ps1 | 17 ++++++ 12 files changed, 218 insertions(+), 78 deletions(-) create mode 100644 src/GitHub.Meta/GitHub.Meta.ps1 create mode 100644 src/GitHub.Meta/GitHub.Meta.psd1 create mode 100644 src/GitHub.Meta/GitHub.Meta.psm1 rename src/{GitHub/public/Meta/Get-GitHubAPIVersions.ps1 => GitHub.Meta/public/Meta/Get-GitHubApiVersions.ps1} (71%) create mode 100644 src/GitHub.Meta/public/Meta/Get-GitHubMeta.ps1 rename src/{GitHub => GitHub.Meta}/public/Meta/Get-GitHubOctocat.ps1 (83%) create mode 100644 src/GitHub.Meta/public/Meta/Get-GitHubRoot.ps1 create mode 100644 src/GitHub.Meta/public/Meta/Get-GitHubZen.ps1 delete mode 100644 src/GitHub/public/Meta/Get-GitHubMeta.ps1 delete mode 100644 src/GitHub/public/Meta/Get-GitHubRoot.ps1 delete mode 100644 src/GitHub/public/Meta/Get-GitHubZen.ps1 create mode 100644 tools/utilities/GitHubAPI.ps1 diff --git a/src/GitHub.Meta/GitHub.Meta.ps1 b/src/GitHub.Meta/GitHub.Meta.ps1 new file mode 100644 index 000000000..da7dc2b46 --- /dev/null +++ b/src/GitHub.Meta/GitHub.Meta.ps1 @@ -0,0 +1,3 @@ +$scriptFilePath = $MyInvocation.MyCommand.Path + +Write-Verbose "[$scriptFilePath] - Initializing GitHub module..." -Verbose diff --git a/src/GitHub.Meta/GitHub.Meta.psd1 b/src/GitHub.Meta/GitHub.Meta.psd1 new file mode 100644 index 000000000..531b269f3 --- /dev/null +++ b/src/GitHub.Meta/GitHub.Meta.psd1 @@ -0,0 +1,29 @@ +@{ + # Author of this module + Author = 'Marius Storhaug' + + # Version number of this module + ModuleVersion = '0.0.1' + + # Description of the functionality provided by this module + Description = 'GitHub PowerShell Module' + + # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. + PrivateData = @{ + + PSData = @{ + + # Tags applied to this module. These help with module discovery in online galleries. + Tags = 'GitHub', 'PSModule' + + # A URL to the license for this module. + LicenseUri = 'https://github.com/PSModule/GitHub/blob/main/LICENSE.md' + + # A URL to the main website for this project. + ProjectUri = 'https://github.com/PSModule/GitHub' + + # A URL to an icon representing this module. + IconUri = 'https://raw.githubusercontent.com/PSModule/GitHub/main/media/icon.png' + } + } +} diff --git a/src/GitHub.Meta/GitHub.Meta.psm1 b/src/GitHub.Meta/GitHub.Meta.psm1 new file mode 100644 index 000000000..eec39f2b3 --- /dev/null +++ b/src/GitHub.Meta/GitHub.Meta.psm1 @@ -0,0 +1,61 @@ +[Cmdletbinding()] +param() + +$scriptName = $MyInvocation.MyCommand.Name +Write-Verbose "[$scriptName] - Importing module" + +#region - Importing data files +Write-Verbose "[$scriptName] - [data] - Processing folder" +$dataFolder = Join-Path $PSScriptRoot 'data' +Get-ChildItem -Path $dataFolder -Recurse -Force -Include '*.psd1' | ForEach-Object { + Write-Verbose "[$scriptName] - [data] - [$($_.Name)] - Importing data file" + New-Variable -Name $_.BaseName -Value (Import-PowerShellDataFile -Path $_.FullName) -Force + Write-Verbose "[$scriptName] - [data] - [$($_.Name)] - Done" +} +Write-Verbose "[$scriptName] - [data] - Done" +#endregion - Importing data files + +#region - Importing script files +$folders = 'init', 'classes', 'private', 'public' +foreach ($folder in $folders) { + Write-Verbose "[$scriptName] - [$folder] - Processing folder" + $folderPath = Join-Path $PSScriptRoot $folder + if (Test-Path -Path $folderPath) { + Get-ChildItem -Path $folderPath -Include '*.ps1', '*.psm1' -Recurse | + Sort-Object -Property FullName | ForEach-Object { + Write-Verbose "[$scriptName] - [$folder] - [$($_.Name)] - Importing script file" + Import-Module $_ -Verbose:$false + Write-Verbose "[$scriptName] - [$folder] - [$($_.Name)] - Done" + } + } + Write-Verbose "[$scriptName] - [$folder] - Done" +} +#endregion - Importing script files + +#region - Importing root script files +Write-Verbose "[$scriptName] - [PSModuleRoot] - Processing folder" +Get-ChildItem -Path $PSScriptRoot -Filter '*.ps1' | ForEach-Object { + Write-Verbose "[$scriptName] - [PSModuleRoot] - [$($_.Name)] - Importing root script files" + Import-Module $_ -Verbose:$false + Write-Verbose "[$scriptName] - [PSModuleRoot] - [$($_.Name)] - Done" +} +Write-Verbose "[$scriptName] - [Root] - Done" +#endregion - Importing root script files + +#region Export module members +$foldersToProcess = Get-ChildItem -Path $PSScriptRoot -Directory | Where-Object Name -In $folders +$moduleFiles = $foldersToProcess | Get-ChildItem -Include '*.ps1' -Recurse -File -Force +$functions = $moduleFiles.BaseName +$param = @{ + Function = $functions + Variable = '' + Cmdlet = '' + Alias = '*' +} + +Write-Verbose "[$scriptName] - Exporting module members" +Export-ModuleMember @param +Write-Verbose "[$scriptName] - Exporting module members - Done" +#endregion Export module members + +Write-Verbose "[$scriptName] - Done" diff --git a/src/GitHub/public/Meta/Get-GitHubAPIVersions.ps1 b/src/GitHub.Meta/public/Meta/Get-GitHubApiVersions.ps1 similarity index 71% rename from src/GitHub/public/Meta/Get-GitHubAPIVersions.ps1 rename to src/GitHub.Meta/public/Meta/Get-GitHubApiVersions.ps1 index c9bfe25f9..3ef3b3e31 100644 --- a/src/GitHub/public/Meta/Get-GitHubAPIVersions.ps1 +++ b/src/GitHub.Meta/public/Meta/Get-GitHubApiVersions.ps1 @@ -1,7 +1,9 @@ -function Get-GitHubApiVersions { +#Requires -Modules GitHub + +function Get-GitHubApiVersions { <# .SYNOPSIS - Get all supported GitHub API versions. + Get all API versions. .DESCRIPTION Get all supported GitHub API versions. @@ -12,7 +14,7 @@ Get all supported GitHub API versions. .NOTES - https://docs.github.com/en/rest/meta/meta?apiVersion=2022-11-28#get-all-api-versions + https://docs.github.com/rest/meta/meta#get-all-api-versions #> [OutputType([string[]])] [CmdletBinding()] diff --git a/src/GitHub.Meta/public/Meta/Get-GitHubMeta.ps1 b/src/GitHub.Meta/public/Meta/Get-GitHubMeta.ps1 new file mode 100644 index 000000000..2cda98f5b --- /dev/null +++ b/src/GitHub.Meta/public/Meta/Get-GitHubMeta.ps1 @@ -0,0 +1,37 @@ +#Requires -Modules GitHub + +function Get-GitHubMeta { + <# + .SYNOPSIS + Get GitHub meta information. + + .DESCRIPTION + Returns meta information about GitHub, including a list of GitHub's IP addresses. For more information, see "[About GitHub's IP addresses](https://docs.github.com/articles/about-github-s-ip-addresses/)." + + The API's response also includes a list of GitHub's domain names. + + The values shown in the documentation's response are example values. You must always query the API directly to get the latest values. + + **Note:** This endpoint returns both IPv4 and IPv6 addresses. However, not all features support IPv6. You should refer to the specific documentation for each feature to determine if IPv6 is supported. + + .EXAMPLE + Get-GitHubMeta + + Returns meta information about GitHub, including a list of GitHub's IP addresses. + + .NOTES + https://docs.github.com/rest/meta/meta#get-apiname-meta-information + #> + [OutputType([object])] + [CmdletBinding()] + param () + + $inputObject = @{ + ApiEndpoint = '/meta' + Method = 'GET' + } + + $response = Invoke-GitHubAPI @inputObject + + $response +} diff --git a/src/GitHub/public/Meta/Get-GitHubOctocat.ps1 b/src/GitHub.Meta/public/Meta/Get-GitHubOctocat.ps1 similarity index 83% rename from src/GitHub/public/Meta/Get-GitHubOctocat.ps1 rename to src/GitHub.Meta/public/Meta/Get-GitHubOctocat.ps1 index cbe675cd6..ceaab7085 100644 --- a/src/GitHub/public/Meta/Get-GitHubOctocat.ps1 +++ b/src/GitHub.Meta/public/Meta/Get-GitHubOctocat.ps1 @@ -1,10 +1,12 @@ -function Get-GitHubOctocat { +#Requires -Modules GitHub + +function Get-GitHubOctocat { <# .SYNOPSIS - Get the octocat as ASCII art + Get Octocat. .DESCRIPTION - Get the octocat as ASCII art + Get the octocat as ASCII art. .EXAMPLE Get-GitHubOctocat @@ -17,7 +19,7 @@ Get the octocat as ASCII art with a custom saying .NOTES - https://docs.github.com/en/rest/meta/meta?apiVersion=2022-11-28#get-octocat + https://docs.github.com/rest/meta/meta#get-octocat #> [OutputType([string])] [CmdletBinding()] @@ -33,7 +35,6 @@ # $query = [System.Web.HttpUtility]::UrlEncode($S) # $query = [System.Uri]::EscapeDataString($S) - $body = @{ s = $S } diff --git a/src/GitHub.Meta/public/Meta/Get-GitHubRoot.ps1 b/src/GitHub.Meta/public/Meta/Get-GitHubRoot.ps1 new file mode 100644 index 000000000..7f74c4605 --- /dev/null +++ b/src/GitHub.Meta/public/Meta/Get-GitHubRoot.ps1 @@ -0,0 +1,30 @@ +#Requires -Modules GitHub + +function Get-GitHubRoot { + <# + .SYNOPSIS + GitHub API Root. + + .DESCRIPTION + Get Hypermedia links to resources accessible in GitHub's REST API. + + .EXAMPLE + Get-GitHubRoot + + Get the root endpoint for the GitHub API. + + .NOTES + https://docs.github.com/rest/meta/meta#github-api-root + #> + [CmdletBinding()] + param () + + $InputObject = @{ + APIEndpoint = '/' + Method = 'GET' + } + + $response = Invoke-GitHubAPI @InputObject + + $response +} diff --git a/src/GitHub.Meta/public/Meta/Get-GitHubZen.ps1 b/src/GitHub.Meta/public/Meta/Get-GitHubZen.ps1 new file mode 100644 index 000000000..320f0c302 --- /dev/null +++ b/src/GitHub.Meta/public/Meta/Get-GitHubZen.ps1 @@ -0,0 +1,30 @@ +#Requires -Modules GitHub + +function Get-GitHubZen { + <# + .SYNOPSIS + Get the Zen of GitHub. + + .DESCRIPTION + Get a random sentence from the Zen of GitHub. + + .EXAMPLE + Get-GitHubZen + + Get a random sentence from the Zen of GitHub. + + .NOTES + https://docs.github.com/rest/meta/meta#get-the-zen-of-github + #> + [CmdletBinding()] + param () + + $InputObject = @{ + APIEndpoint = '/zen' + Method = 'GET' + } + + $Response = Invoke-GitHubAPI @InputObject + + $Response +} diff --git a/src/GitHub/public/Meta/Get-GitHubMeta.ps1 b/src/GitHub/public/Meta/Get-GitHubMeta.ps1 deleted file mode 100644 index 7837725a9..000000000 --- a/src/GitHub/public/Meta/Get-GitHubMeta.ps1 +++ /dev/null @@ -1,36 +0,0 @@ -function Get-GitHubMeta { - <# - .SYNOPSIS - Returns meta information about GitHub, including a list of GitHub's IP addresses. - - .DESCRIPTION - Returns meta information about GitHub, including a list of GitHub's IP addresses. - - The API's response also includes a list of GitHub's domain names. - - The values shown in the documentation's response are example values. You must always query the API directly to get the latest values. - - Note: This endpoint returns both IPv4 and IPv6 addresses. However, not all features support IPv6. You should refer to the specific documentation for each feature to determine if IPv6 is supported. - - .EXAMPLE - Get-GitHubMeta - - Returns meta information about GitHub, including a list of GitHub's IP addresses. - - .NOTES - https://docs.github.com/en/rest/meta/meta?apiVersion=2022-11-28#github-api-root - https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/about-githubs-ip-addresses - #> - [OutputType([object])] - [CmdletBinding()] - param () - - $inputObject = @{ - ApiEndpoint = '/meta' - Method = 'GET' - } - - $response = Invoke-GitHubAPI @inputObject - - $response -} diff --git a/src/GitHub/public/Meta/Get-GitHubRoot.ps1 b/src/GitHub/public/Meta/Get-GitHubRoot.ps1 deleted file mode 100644 index c56123230..000000000 --- a/src/GitHub/public/Meta/Get-GitHubRoot.ps1 +++ /dev/null @@ -1,17 +0,0 @@ -function Get-GitHubRoot { - <# - .NOTES - https://docs.github.com/en/rest/reference/meta#github-api-root - #> - [CmdletBinding()] - param () - - $InputObject = @{ - APIEndpoint = '/' - Method = 'GET' - } - - $response = Invoke-GitHubAPI @InputObject - - $response -} diff --git a/src/GitHub/public/Meta/Get-GitHubZen.ps1 b/src/GitHub/public/Meta/Get-GitHubZen.ps1 deleted file mode 100644 index fc92ce284..000000000 --- a/src/GitHub/public/Meta/Get-GitHubZen.ps1 +++ /dev/null @@ -1,17 +0,0 @@ -function Get-GitHubZen { - <# - .NOTES - https://docs.github.com/en/rest/reference/meta#github-api-root - #> - [CmdletBinding()] - param () - - $InputObject = @{ - APIEndpoint = '/zen' - Method = 'GET' - } - - $Response = Invoke-GitHubAPI @InputObject - - $Response -} diff --git a/tools/utilities/GitHubAPI.ps1 b/tools/utilities/GitHubAPI.ps1 new file mode 100644 index 000000000..9292af143 --- /dev/null +++ b/tools/utilities/GitHubAPI.ps1 @@ -0,0 +1,17 @@ +$APIDocURI = 'https://raw.githubusercontent.com/github/rest-api-description/main' +$Bundled = '/descriptions/api.github.com/api.github.com.json' +$Dereferenced = 'descriptions/api.github.com/dereferenced/api.github.com.deref.json' +$APIDocURI = $APIDocURI + $Bundled +$Response = Invoke-RestMethod -Uri $APIDocURI -Method Get + +# $Response.info # API name = GitHub REST API +# $Response.openapi # Spec version = 3.0.3 +# $Response.servers # API URL = api.github.com +# $Response.externalDocs # API docs URL = docs.github.com/rest +# $Response.components # Type specs +# $Response.paths # API endpoints +# $Response.tags # API categories +# $Response.'x-webhooks' # Webhooks/event docs + + +$Response.paths.'/meta'.get From 542b4be1cd59f90ae0c5cb58aa62bd62a30c0ecb Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Mon, 25 Sep 2023 23:16:05 +0200 Subject: [PATCH 44/45] Fix loader --- src/GitHub.Meta/GitHub.Meta.psm1 | 2 +- src/GitHub/GitHub.psm1 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/GitHub.Meta/GitHub.Meta.psm1 b/src/GitHub.Meta/GitHub.Meta.psm1 index eec39f2b3..20a13acca 100644 --- a/src/GitHub.Meta/GitHub.Meta.psm1 +++ b/src/GitHub.Meta/GitHub.Meta.psm1 @@ -7,7 +7,7 @@ Write-Verbose "[$scriptName] - Importing module" #region - Importing data files Write-Verbose "[$scriptName] - [data] - Processing folder" $dataFolder = Join-Path $PSScriptRoot 'data' -Get-ChildItem -Path $dataFolder -Recurse -Force -Include '*.psd1' | ForEach-Object { +Get-ChildItem -Path $dataFolder -Recurse -Force -Include '*.psd1' -ErrorAction SilentlyContinue | ForEach-Object { Write-Verbose "[$scriptName] - [data] - [$($_.Name)] - Importing data file" New-Variable -Name $_.BaseName -Value (Import-PowerShellDataFile -Path $_.FullName) -Force Write-Verbose "[$scriptName] - [data] - [$($_.Name)] - Done" diff --git a/src/GitHub/GitHub.psm1 b/src/GitHub/GitHub.psm1 index eec39f2b3..20a13acca 100644 --- a/src/GitHub/GitHub.psm1 +++ b/src/GitHub/GitHub.psm1 @@ -7,7 +7,7 @@ Write-Verbose "[$scriptName] - Importing module" #region - Importing data files Write-Verbose "[$scriptName] - [data] - Processing folder" $dataFolder = Join-Path $PSScriptRoot 'data' -Get-ChildItem -Path $dataFolder -Recurse -Force -Include '*.psd1' | ForEach-Object { +Get-ChildItem -Path $dataFolder -Recurse -Force -Include '*.psd1' -ErrorAction SilentlyContinue | ForEach-Object { Write-Verbose "[$scriptName] - [data] - [$($_.Name)] - Importing data file" New-Variable -Name $_.BaseName -Value (Import-PowerShellDataFile -Path $_.FullName) -Force Write-Verbose "[$scriptName] - [data] - [$($_.Name)] - Done" From 15725487fec9423e721c472dbf363111b04f1782 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Mon, 25 Sep 2023 23:22:28 +0200 Subject: [PATCH 45/45] Moved meta back in --- src/GitHub.Meta/GitHub.Meta.ps1 | 3 - src/GitHub.Meta/GitHub.Meta.psd1 | 29 --------- src/GitHub.Meta/GitHub.Meta.psm1 | 61 ------------------- .../public/Meta/Get-GitHubApiVersions.ps1 | 4 +- .../public/Meta/Get-GitHubMeta.ps1 | 4 +- .../public/Meta/Get-GitHubOctocat.ps1 | 4 +- .../public/Meta/Get-GitHubRoot.ps1 | 4 +- .../public/Meta/Get-GitHubZen.ps1 | 4 +- 8 files changed, 5 insertions(+), 108 deletions(-) delete mode 100644 src/GitHub.Meta/GitHub.Meta.ps1 delete mode 100644 src/GitHub.Meta/GitHub.Meta.psd1 delete mode 100644 src/GitHub.Meta/GitHub.Meta.psm1 rename src/{GitHub.Meta => GitHub}/public/Meta/Get-GitHubApiVersions.ps1 (89%) rename src/{GitHub.Meta => GitHub}/public/Meta/Get-GitHubMeta.ps1 (95%) rename src/{GitHub.Meta => GitHub}/public/Meta/Get-GitHubOctocat.ps1 (94%) rename src/{GitHub.Meta => GitHub}/public/Meta/Get-GitHubRoot.ps1 (90%) rename src/{GitHub.Meta => GitHub}/public/Meta/Get-GitHubZen.ps1 (89%) diff --git a/src/GitHub.Meta/GitHub.Meta.ps1 b/src/GitHub.Meta/GitHub.Meta.ps1 deleted file mode 100644 index da7dc2b46..000000000 --- a/src/GitHub.Meta/GitHub.Meta.ps1 +++ /dev/null @@ -1,3 +0,0 @@ -$scriptFilePath = $MyInvocation.MyCommand.Path - -Write-Verbose "[$scriptFilePath] - Initializing GitHub module..." -Verbose diff --git a/src/GitHub.Meta/GitHub.Meta.psd1 b/src/GitHub.Meta/GitHub.Meta.psd1 deleted file mode 100644 index 531b269f3..000000000 --- a/src/GitHub.Meta/GitHub.Meta.psd1 +++ /dev/null @@ -1,29 +0,0 @@ -@{ - # Author of this module - Author = 'Marius Storhaug' - - # Version number of this module - ModuleVersion = '0.0.1' - - # Description of the functionality provided by this module - Description = 'GitHub PowerShell Module' - - # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. - PrivateData = @{ - - PSData = @{ - - # Tags applied to this module. These help with module discovery in online galleries. - Tags = 'GitHub', 'PSModule' - - # A URL to the license for this module. - LicenseUri = 'https://github.com/PSModule/GitHub/blob/main/LICENSE.md' - - # A URL to the main website for this project. - ProjectUri = 'https://github.com/PSModule/GitHub' - - # A URL to an icon representing this module. - IconUri = 'https://raw.githubusercontent.com/PSModule/GitHub/main/media/icon.png' - } - } -} diff --git a/src/GitHub.Meta/GitHub.Meta.psm1 b/src/GitHub.Meta/GitHub.Meta.psm1 deleted file mode 100644 index 20a13acca..000000000 --- a/src/GitHub.Meta/GitHub.Meta.psm1 +++ /dev/null @@ -1,61 +0,0 @@ -[Cmdletbinding()] -param() - -$scriptName = $MyInvocation.MyCommand.Name -Write-Verbose "[$scriptName] - Importing module" - -#region - Importing data files -Write-Verbose "[$scriptName] - [data] - Processing folder" -$dataFolder = Join-Path $PSScriptRoot 'data' -Get-ChildItem -Path $dataFolder -Recurse -Force -Include '*.psd1' -ErrorAction SilentlyContinue | ForEach-Object { - Write-Verbose "[$scriptName] - [data] - [$($_.Name)] - Importing data file" - New-Variable -Name $_.BaseName -Value (Import-PowerShellDataFile -Path $_.FullName) -Force - Write-Verbose "[$scriptName] - [data] - [$($_.Name)] - Done" -} -Write-Verbose "[$scriptName] - [data] - Done" -#endregion - Importing data files - -#region - Importing script files -$folders = 'init', 'classes', 'private', 'public' -foreach ($folder in $folders) { - Write-Verbose "[$scriptName] - [$folder] - Processing folder" - $folderPath = Join-Path $PSScriptRoot $folder - if (Test-Path -Path $folderPath) { - Get-ChildItem -Path $folderPath -Include '*.ps1', '*.psm1' -Recurse | - Sort-Object -Property FullName | ForEach-Object { - Write-Verbose "[$scriptName] - [$folder] - [$($_.Name)] - Importing script file" - Import-Module $_ -Verbose:$false - Write-Verbose "[$scriptName] - [$folder] - [$($_.Name)] - Done" - } - } - Write-Verbose "[$scriptName] - [$folder] - Done" -} -#endregion - Importing script files - -#region - Importing root script files -Write-Verbose "[$scriptName] - [PSModuleRoot] - Processing folder" -Get-ChildItem -Path $PSScriptRoot -Filter '*.ps1' | ForEach-Object { - Write-Verbose "[$scriptName] - [PSModuleRoot] - [$($_.Name)] - Importing root script files" - Import-Module $_ -Verbose:$false - Write-Verbose "[$scriptName] - [PSModuleRoot] - [$($_.Name)] - Done" -} -Write-Verbose "[$scriptName] - [Root] - Done" -#endregion - Importing root script files - -#region Export module members -$foldersToProcess = Get-ChildItem -Path $PSScriptRoot -Directory | Where-Object Name -In $folders -$moduleFiles = $foldersToProcess | Get-ChildItem -Include '*.ps1' -Recurse -File -Force -$functions = $moduleFiles.BaseName -$param = @{ - Function = $functions - Variable = '' - Cmdlet = '' - Alias = '*' -} - -Write-Verbose "[$scriptName] - Exporting module members" -Export-ModuleMember @param -Write-Verbose "[$scriptName] - Exporting module members - Done" -#endregion Export module members - -Write-Verbose "[$scriptName] - Done" diff --git a/src/GitHub.Meta/public/Meta/Get-GitHubApiVersions.ps1 b/src/GitHub/public/Meta/Get-GitHubApiVersions.ps1 similarity index 89% rename from src/GitHub.Meta/public/Meta/Get-GitHubApiVersions.ps1 rename to src/GitHub/public/Meta/Get-GitHubApiVersions.ps1 index 3ef3b3e31..ed1443291 100644 --- a/src/GitHub.Meta/public/Meta/Get-GitHubApiVersions.ps1 +++ b/src/GitHub/public/Meta/Get-GitHubApiVersions.ps1 @@ -1,6 +1,4 @@ -#Requires -Modules GitHub - -function Get-GitHubApiVersions { +function Get-GitHubApiVersions { <# .SYNOPSIS Get all API versions. diff --git a/src/GitHub.Meta/public/Meta/Get-GitHubMeta.ps1 b/src/GitHub/public/Meta/Get-GitHubMeta.ps1 similarity index 95% rename from src/GitHub.Meta/public/Meta/Get-GitHubMeta.ps1 rename to src/GitHub/public/Meta/Get-GitHubMeta.ps1 index 2cda98f5b..cae684cf8 100644 --- a/src/GitHub.Meta/public/Meta/Get-GitHubMeta.ps1 +++ b/src/GitHub/public/Meta/Get-GitHubMeta.ps1 @@ -1,6 +1,4 @@ -#Requires -Modules GitHub - -function Get-GitHubMeta { +function Get-GitHubMeta { <# .SYNOPSIS Get GitHub meta information. diff --git a/src/GitHub.Meta/public/Meta/Get-GitHubOctocat.ps1 b/src/GitHub/public/Meta/Get-GitHubOctocat.ps1 similarity index 94% rename from src/GitHub.Meta/public/Meta/Get-GitHubOctocat.ps1 rename to src/GitHub/public/Meta/Get-GitHubOctocat.ps1 index ceaab7085..946bfe86c 100644 --- a/src/GitHub.Meta/public/Meta/Get-GitHubOctocat.ps1 +++ b/src/GitHub/public/Meta/Get-GitHubOctocat.ps1 @@ -1,6 +1,4 @@ -#Requires -Modules GitHub - -function Get-GitHubOctocat { +function Get-GitHubOctocat { <# .SYNOPSIS Get Octocat. diff --git a/src/GitHub.Meta/public/Meta/Get-GitHubRoot.ps1 b/src/GitHub/public/Meta/Get-GitHubRoot.ps1 similarity index 90% rename from src/GitHub.Meta/public/Meta/Get-GitHubRoot.ps1 rename to src/GitHub/public/Meta/Get-GitHubRoot.ps1 index 7f74c4605..a08b507df 100644 --- a/src/GitHub.Meta/public/Meta/Get-GitHubRoot.ps1 +++ b/src/GitHub/public/Meta/Get-GitHubRoot.ps1 @@ -1,6 +1,4 @@ -#Requires -Modules GitHub - -function Get-GitHubRoot { +function Get-GitHubRoot { <# .SYNOPSIS GitHub API Root. diff --git a/src/GitHub.Meta/public/Meta/Get-GitHubZen.ps1 b/src/GitHub/public/Meta/Get-GitHubZen.ps1 similarity index 89% rename from src/GitHub.Meta/public/Meta/Get-GitHubZen.ps1 rename to src/GitHub/public/Meta/Get-GitHubZen.ps1 index 320f0c302..866265a06 100644 --- a/src/GitHub.Meta/public/Meta/Get-GitHubZen.ps1 +++ b/src/GitHub/public/Meta/Get-GitHubZen.ps1 @@ -1,6 +1,4 @@ -#Requires -Modules GitHub - -function Get-GitHubZen { +function Get-GitHubZen { <# .SYNOPSIS Get the Zen of GitHub.