Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Feature/calendar api session caching issues 162 163 #164

Merged
merged 2 commits into from
Mar 20, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Changelog

* [Changelog](#changelog)
* [2.25.0](#2250)
* [2.24.0](#2240)
* [2.23.2](#2232)
* [2.23.1](#2231)
Expand Down Expand Up @@ -77,6 +78,22 @@

***

## 2.25.0

* [Issue #162](https://github.com/scrthq/PSGSuite/issues/162)
* Updated: `New-GoogleService` now caches Service objects created during the current session. This means that repeated calls will attempt to use an existing Service object from the cache if present, otherwise it will create the Service as usual.
* Updated: `New-GoogleService` Verbose output. To cut down on verbose noisiness, the following verbose output is set:
* New Service created = `Building ServiceAccountCredential from....`
* First use of existing Service = `Using matching cached service for user....`
* Re-use of existing Service = No verbose output (helps cut down on pipeline verbosity where service re-use is expected)
* Added: `Get-PSGSuiteServiceCache` to get the current Service Cache for inspection.
* [Issue #163](https://github.com/scrthq/PSGSuite/issues/163)
* Added: `Get-GSCalendar` to get the CalendarList of a user.
* Added: `Remove-GSCalendarAcl` to remove Access Control List rules from Google Calendars.
* Miscellaneous
* Improved pipeline support for Gmail `*Message` functions and Calendar functions.
* Added tab completion to `Switch-PSGSuiteConfig` for the ConfigName parameter.

## 2.24.0

* [Issue #159](https://github.com/scrthq/PSGSuite/issues/159)
Expand Down
2 changes: 1 addition & 1 deletion PSGSuite/PSGSuite.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
RootModule = 'PSGSuite.psm1'

# Version number of this module.
ModuleVersion = '2.24.0'
ModuleVersion = '2.25.0'

# ID used to uniquely identify this module
GUID = '9d751152-e83e-40bb-a6db-4c329092aaec'
Expand Down
14 changes: 14 additions & 0 deletions PSGSuite/Private/Get-PSGSuiteConfigNames.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
function Get-PSGSuiteConfigNames {
<#
.SYNOPSIS
Gets the current config names for tab completion on Switch-PSGSuiteConfig.

.DESCRIPTION
Gets the current config names for tab completion on Switch-PSGSuiteConfig.
#>
[cmdletbinding()]
Param ()
$script:ConfigScope = $Scope
$fullConf = Import-SpecificConfiguration -CompanyName 'SCRT HQ' -Name 'PSGSuite' -Scope $Script:ConfigScope
$fullConf.Keys | Where-Object {$_ -ne 'DefaultConfig'}
}
31 changes: 31 additions & 0 deletions PSGSuite/Public/Authentication/Get-PSGSuiteServiceCache.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
function Get-PSGSuiteServiceCache {
<#
.SYNOPSIS
Returns the dictionary of cached service objects created with New-GoogleService for inspection.

.DESCRIPTION
Returns the dictionary of cached service objects created with New-GoogleService for inspection.

The keys in the session cache dictionary are comprised of the following values which are added to the cache whenever a new session is created:

$SessionKey = @($User,$ServiceType,$(($Scope | Sort-Object) -join ";")) -join ";"

.EXAMPLE
Get-PSGSuiteServiceCache
#>
[CmdletBinding()]
Param (
[parameter(Mandatory = $false,Position = 0)]
[Switch]
$IncludeKeys
)
Begin{
if (-not $script:_PSGSuiteSessions) {
$script:_PSGSuiteSessions = @{}
}
}
Process {
Write-Verbose "Getting cached session list"
$script:_PSGSuiteSessions.Values
}
}
139 changes: 84 additions & 55 deletions PSGSuite/Public/Authentication/New-GoogleService.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -13,69 +13,98 @@ function New-GoogleService {
[String]
$User = $script:PSGSuite.AdminEmail
)
Begin {
if (-not $script:_PSGSuiteSessions) {
$script:_PSGSuiteSessions = @{}
}
$sessionKey = @($User,$ServiceType,$(($Scope | Sort-Object) -join ";")) -join ";"
}
Process {
if ($script:PSGSuite.P12KeyPath -or $script:PSGSuite.P12Key) {
try {
Write-Verbose "Building ServiceAccountCredential from P12Key as user '$User'"
if (-not $script:PSGSuite.P12Key) {
$script:PSGSuite.P12Key = ([System.IO.File]::ReadAllBytes($script:PSGSuite.P12KeyPath))
Set-PSGSuiteConfig -ConfigName $script:PSGSuite.ConfigName -P12Key $script:PSGSuite.P12Key -Verbose:$false
if ($script:_PSGSuiteSessions.ContainsKey($sessionKey)) {
if (-not $script:_PSGSuiteSessions[$sessionKey].Acknowledged) {
Write-Verbose "Using matching cached service for user '$User'"
$script:_PSGSuiteSessions[$sessionKey].Acknowledged = $true
}
$script:_PSGSuiteSessions[$sessionKey] | Select-Object -ExpandProperty Service
}
else {
if ($script:PSGSuite.P12KeyPath -or $script:PSGSuite.P12Key) {
try {
Write-Verbose "Building ServiceAccountCredential from P12Key as user '$User'"
if (-not $script:PSGSuite.P12Key) {
$script:PSGSuite.P12Key = ([System.IO.File]::ReadAllBytes($script:PSGSuite.P12KeyPath))
Set-PSGSuiteConfig -ConfigName $script:PSGSuite.ConfigName -P12Key $script:PSGSuite.P12Key -Verbose:$false
}
$certificate = New-Object 'System.Security.Cryptography.X509Certificates.X509Certificate2' -ArgumentList ([System.Byte[]]$script:PSGSuite.P12Key),"notasecret",([System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable)
$credential = New-Object 'Google.Apis.Auth.OAuth2.ServiceAccountCredential' (New-Object 'Google.Apis.Auth.OAuth2.ServiceAccountCredential+Initializer' $script:PSGSuite.AppEmail -Property @{
User = $User
Scopes = [string[]]$Scope
}
).FromCertificate($certificate)
}
$certificate = New-Object 'System.Security.Cryptography.X509Certificates.X509Certificate2' -ArgumentList ([System.Byte[]]$script:PSGSuite.P12Key),"notasecret",([System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable)
$credential = New-Object 'Google.Apis.Auth.OAuth2.ServiceAccountCredential' (New-Object 'Google.Apis.Auth.OAuth2.ServiceAccountCredential+Initializer' $script:PSGSuite.AppEmail -Property @{
User = $User
Scopes = [string[]]$Scope
catch {
$PSCmdlet.ThrowTerminatingError($_)
}
).FromCertificate($certificate)
}
catch {
$PSCmdlet.ThrowTerminatingError($_)
}
}
elseif ($script:PSGSuite.ClientSecretsPath -or $script:PSGSuite.ClientSecrets) {
try {
$ClientSecretsScopes = @(
'https://www.google.com/m8/feeds'
'https://mail.google.com'
'https://www.googleapis.com/auth/gmail.settings.basic'
'https://www.googleapis.com/auth/gmail.settings.sharing'
'https://www.googleapis.com/auth/calendar'
'https://www.googleapis.com/auth/drive'
'https://www.googleapis.com/auth/tasks'
'https://www.googleapis.com/auth/tasks.readonly'
)
if (-not $script:PSGSuite.ClientSecrets) {
$script:PSGSuite.ClientSecrets = ([System.IO.File]::ReadAllText($script:PSGSuite.ClientSecretsPath))
Set-PSGSuiteConfig -ConfigName $script:PSGSuite.ConfigName -ClientSecrets $script:PSGSuite.ClientSecrets -Verbose:$false
elseif ($script:PSGSuite.ClientSecretsPath -or $script:PSGSuite.ClientSecrets) {
try {
$ClientSecretsScopes = @(
'https://www.google.com/m8/feeds'
'https://mail.google.com'
'https://www.googleapis.com/auth/gmail.settings.basic'
'https://www.googleapis.com/auth/gmail.settings.sharing'
'https://www.googleapis.com/auth/calendar'
'https://www.googleapis.com/auth/drive'
'https://www.googleapis.com/auth/tasks'
'https://www.googleapis.com/auth/tasks.readonly'
)
if (-not $script:PSGSuite.ClientSecrets) {
$script:PSGSuite.ClientSecrets = ([System.IO.File]::ReadAllText($script:PSGSuite.ClientSecretsPath))
Set-PSGSuiteConfig -ConfigName $script:PSGSuite.ConfigName -ClientSecrets $script:PSGSuite.ClientSecrets -Verbose:$false
}
$credPath = Join-Path (Resolve-Path (Join-Path "~" ".scrthq")) "PSGSuite"
Write-Verbose "Building UserCredentials from ClientSecrets as user '$User' and prompting for authorization if necessary."
$stream = New-Object System.IO.MemoryStream $([System.Text.Encoding]::ASCII.GetBytes(($script:PSGSuite.ClientSecrets))),$null
$credential = [Google.Apis.Auth.OAuth2.GoogleWebAuthorizationBroker]::AuthorizeAsync(
[Google.Apis.Auth.OAuth2.GoogleClientSecrets]::Load($stream).Secrets,
[string[]]$ClientSecretsScopes,
$User,
[System.Threading.CancellationToken]::None,
[Google.Apis.Util.Store.FileDataStore]::new($credPath,$true),
$(if ($PSVersionTable.PSVersion.Major -gt 5) {
New-Object 'Google.Apis.Auth.OAuth2.PromptCodeReceiver'
}
else {
New-Object 'Google.Apis.Auth.OAuth2.LocalServerCodeReceiver'
})
).Result
}
catch {
$PSCmdlet.ThrowTerminatingError($_)
}
finally {
if ($stream) {
$stream.Close()
}
}
$credPath = Join-Path (Resolve-Path (Join-Path "~" ".scrthq")) "PSGSuite"
Write-Verbose "Building UserCredentials from ClientSecrets as user '$User' and prompting for authorization if necessary."
$stream = New-Object System.IO.MemoryStream $([System.Text.Encoding]::ASCII.GetBytes(($script:PSGSuite.ClientSecrets))),$null
$credential = [Google.Apis.Auth.OAuth2.GoogleWebAuthorizationBroker]::AuthorizeAsync(
[Google.Apis.Auth.OAuth2.GoogleClientSecrets]::Load($stream).Secrets,
[string[]]$ClientSecretsScopes,
$User,
[System.Threading.CancellationToken]::None,
[Google.Apis.Util.Store.FileDataStore]::new($credPath,$true),
$(if($PSVersionTable.PSVersion.Major -gt 5){New-Object 'Google.Apis.Auth.OAuth2.PromptCodeReceiver'}else{New-Object 'Google.Apis.Auth.OAuth2.LocalServerCodeReceiver'})
).Result
}
catch {
$PSCmdlet.ThrowTerminatingError($_)
else {
$PSCmdlet.ThrowTerminatingError((ThrowTerm "The current config '$($script:PSGSuite.ConfigName)' does not contain a P12KeyPath or a ClientSecretsPath! PSGSuite is unable to build a credential object for the service without a path to a credential file! Please update the configuration to include a path at least one of the two credential types."))
}
finally {
if ($stream) {
$stream.Close()
$svc = New-Object "$ServiceType" (New-Object 'Google.Apis.Services.BaseClientService+Initializer' -Property @{
HttpClientInitializer = $credential
ApplicationName = "PSGSuite - $env:USERNAME"
}
}
)
$issued = Get-Date
$script:_PSGSuiteSessions[$sessionKey] = ([PSCustomObject]@{
User = $User
Scope = $Scope
Service = $svc
Issued = $issued
Acknowledged = $false
})
return $svc
}
else {
$PSCmdlet.ThrowTerminatingError((ThrowTerm "The current config '$($script:PSGSuite.ConfigName)' does not contain a P12KeyPath or a ClientSecretsPath! PSGSuite is unable to build a credential object for the service without a path to a credential file! Please update the configuration to include a path at least one of the two credential types."))
}
New-Object "$ServiceType" (New-Object 'Google.Apis.Services.BaseClientService+Initializer' -Property @{
HttpClientInitializer = $credential
ApplicationName = "PSGSuite - $env:USERNAME"
}
)
}
}
139 changes: 139 additions & 0 deletions PSGSuite/Public/Calendar/Get-GSCalendar.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
function Get-GSCalendar {
<#
.SYNOPSIS
Gets the calendars for a user

.DESCRIPTION
Gets the calendars for a user

.PARAMETER CalendarId
The Id of the calendar you would like to get.

If excluded, returns the list of calendars for the user.

.PARAMETER User
The primary email or UserID of the user. You can exclude the '@domain.com' to insert the Domain in the config or use the special 'me' to indicate the AdminEmail in the config.

Defaults to the AdminEmail in the config

.PARAMETER MinAccessRole
The minimum access role for the user in the returned entries. Optional. The default is no restriction.

.PARAMETER PageSize
Maximum number of entries returned on one result page. The page size can never be larger than 250 entries.

.PARAMETER ShowDeleted
Whether to include deleted calendar list entries in the result. Optional. The default is False.

.PARAMETER ShowHidden
Whether to show hidden entries. Optional. The default is False.

.PARAMETER SyncToken
Token obtained from the nextSyncToken field returned on the last page of results from the previous list request. It makes the result of this list request contain only entries that have changed since then.

If only read-only fields such as calendar properties or ACLs have changed, the entry won't be returned. All entries deleted and hidden since the previous list request will always be in the result set and it is not allowed to set showDeleted neither showHidden to False.

To ensure client state consistency minAccessRole query parameter cannot be specified together with nextSyncToken. If the syncToken expires, the server will respond with a 410 GONE response code and the client should clear its storage and perform a full synchronization without any syncToken. Learn more about incremental synchronization.

Optional. The default is to return all entries.
#>
[OutputType('Google.Apis.Calendar.v3.Data.CalendarListEntry')]
[cmdletbinding(DefaultParameterSetName = "List")]
Param
(
[parameter(Mandatory = $true,Position = 0,ParameterSetName = "Get")]
[String[]]
$CalendarId,
[parameter(Mandatory = $false,ValueFromPipeline = $true,ValueFromPipelineByPropertyName = $true)]
[Alias("PrimaryEmail","UserKey","Mail")]
[ValidateNotNullOrEmpty()]
[String[]]
$User = $Script:PSGSuite.AdminEmail,
[parameter(Mandatory = $false,ParameterSetName = "List")]
[Google.Apis.Calendar.v3.CalendarListResource+ListRequest+MinAccessRoleEnum]
$MinAccessRole,
[parameter(Mandatory = $false,ParameterSetName = "List")]
[Alias('MaxResults')]
[ValidateRange(1,250)]
[Int]
$PageSize = 250,
[parameter(Mandatory = $false,ParameterSetName = "List")]
[switch]
$ShowDeleted,
[parameter(Mandatory = $false,ParameterSetName = "List")]
[switch]
$ShowHidden,
[parameter(Mandatory = $false,ParameterSetName = "List")]
[String]
$SyncToken
)
Process {
foreach ($U in $User) {
if ($U -ceq 'me') {
$U = $Script:PSGSuite.AdminEmail
}
elseif ($U -notlike "*@*.*") {
$U = "$($U)@$($Script:PSGSuite.Domain)"
}
$serviceParams = @{
Scope = 'https://www.googleapis.com/auth/calendar'
ServiceType = 'Google.Apis.Calendar.v3.CalendarService'
User = $U
}
$service = New-GoogleService @serviceParams
switch ($PSCmdlet.ParameterSetName) {
Get {
foreach ($calId in $CalendarId) {
try {
$request = $service.CalendarList.Get($calId)
Write-Verbose "Getting Calendar Id '$calId' for User '$U'"
$request.Execute() | Add-Member -MemberType NoteProperty -Name 'User' -Value $U -PassThru
}
catch {
if ($ErrorActionPreference -eq 'Stop') {
$PSCmdlet.ThrowTerminatingError($_)
}
else {
Write-Error $_
}
}
}
}
List {
try {
$request = $service.CalendarList.List()
foreach ($key in $PSBoundParameters.Keys | Where-Object {$_ -ne 'CalendarId'}) {
if ($request.PSObject.Properties.Name -contains $key) {
$request.$key = $PSBoundParameters[$key]
}
}
if ($PageSize) {
$request.MaxResults = $PageSize
}
Write-Verbose "Getting Calendar List for user '$U'"
[int]$i = 1
do {
$result = $request.Execute()
$result.Items | Add-Member -MemberType NoteProperty -Name 'User' -Value $U -PassThru
if ($result.NextPageToken) {
$request.PageToken = $result.NextPageToken
}
[int]$retrieved = ($i + $result.Items.Count) - 1
Write-Verbose "Retrieved $retrieved Calendars..."
[int]$i = $i + $result.Items.Count
}
until (!$result.NextPageToken)
}
catch {
if ($ErrorActionPreference -eq 'Stop') {
$PSCmdlet.ThrowTerminatingError($_)
}
else {
Write-Error $_
}
}
}
}
}
}
}
Loading