diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 7b346ad61..9122555d5 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -6,30 +6,34 @@ The following is a set of guidelines for contributing to Pode on GitHub. These a ## Table of Contents -* [Code of Conduct](#code-of-conduct) -* [I just have a Question](#i-just-have-a-question) -* [About Pode](#about-pode) -* [How to Contribute](#how-to-contribute) - * [Issues](#issues) - * [Branch Names](#branch-names) - * [Pull Requests](#pull-requests) - * [Building](#building) - * [Testing](#testing) - * [Documentation](#documentation) -* [Styleguide](#styleguide) - * [Code](#code) - * [Comments](#comments) - * [General](#general) - * [Help](#help) - * [PowerShell Commandlets](#powershell-commandlets) - * [Foreach-Object](#foreach-object) - * [Where-Object](#where-object) - * [Select-Object](#select-object) - * [Measure-Object](#measure-object) +- [Contributing to Pode](#contributing-to-pode) + - [Table of Contents](#table-of-contents) + - [Code of Conduct](#code-of-conduct) + - [I just have a Question](#i-just-have-a-question) + - [About Pode](#about-pode) + - [How to Contribute](#how-to-contribute) + - [Issues](#issues) + - [Branch Names](#branch-names) + - [Pull Requests](#pull-requests) + - [Building](#building) + - [Testing](#testing) + - [Documentation](#documentation) + - [Importing](#importing) + - [Styleguide](#styleguide) + - [Editor](#editor) + - [Code](#code) + - [Comments](#comments) + - [General](#general) + - [Help](#help) + - [PowerShell Commandlets](#powershell-commandlets) + - [Foreach-Object](#foreach-object) + - [Where-Object](#where-object) + - [Select-Object](#select-object) + - [Measure-Object](#measure-object) ## Code of Conduct -This project and everyone participating in it is governed by the Pode's [Code of Conduct](../.github/CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code. +This project, and everyone participating in it, is governed by the Pode's [Code of Conduct](../.github/CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code. ## I just have a Question @@ -111,22 +115,20 @@ When editing Pode and you need to import the local dev module for testing, you w ## Styleguide +### Editor + +You can use whatever editor you like, but it's recommended to use Visual Studio Code. To help with this style guide, specifically for PowerShell, Pode has code formatting workspace setting which will automatically format the files on save. + ### Code In general, observe the coding style used within the file/project and mimic that as best as you can. Some standards that are typical are: -* Bracers (`{}`) on the function header should be on a new line, such as: +* Bracers (`{}`) should be on the same line of the statement they following, such as `function`, `foreach`, `if`, etc. ```powershell -function Add-Something -{ - # logic -} -``` - -* Bracers (`{}`) should be on the same line of other calls, such as `foreach`, `if`, etc. -```powershell -foreach ($item in $items) { - # logic +function Add-Something { + foreach ($item in $items) { + # logic + } } ``` @@ -135,8 +137,7 @@ foreach ($item in $items) { * Ensure public functions always declare `[CmdletBinding()]` attribute. * Ensure parameter names, types, and attributes are declared on new lines - not all on one line. ```powershell -function Add-Something -{ +function Add-Something { [CmdletBinding()] param( [Parameter()] diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..0eada4b5d --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,24 @@ +{ + "editor.formatOnSave": true, + "editor.formatOnType": false, + "editor.minimap.enabled": false, + "powershell.codeFormatting.addWhitespaceAroundPipe": true, + "powershell.codeFormatting.alignPropertyValuePairs": true, + "powershell.codeFormatting.autoCorrectAliases": true, + "powershell.codeFormatting.avoidSemicolonsAsLineTerminators": true, + "powershell.codeFormatting.ignoreOneLineBlock": true, + "powershell.codeFormatting.pipelineIndentationStyle": "IncreaseIndentationForFirstPipeline", + "powershell.codeFormatting.trimWhitespaceAroundPipe": true, + "powershell.codeFormatting.useConstantStrings": true, + "powershell.codeFormatting.whitespaceBeforeOpenBrace": true, + "powershell.codeFormatting.whitespaceAroundOperator": true, + "powershell.codeFormatting.whitespaceAfterSeparator": true, + "powershell.codeFormatting.useCorrectCasing": false, + "powershell.codeFormatting.openBraceOnSameLine": true, + "powershell.codeFormatting.newLineAfterOpenBrace": true, + "powershell.codeFormatting.newLineAfterCloseBrace": true, + "powershell.codeFormatting.whitespaceBeforeOpenParen": true, + "powershell.codeFormatting.whitespaceBetweenParameters": false, + "powershell.codeFormatting.whitespaceInsideBrace": true, + "files.trimTrailingWhitespace": true +} \ No newline at end of file diff --git a/README.md b/README.md index c2d0fdb58..9ba22b6c6 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,7 @@ Then navigate to `http://127.0.0.1:8000` in your browser. * Basic rate limiting for IP addresses and subnets * Middleware and Sessions on web servers, with Flash message and CSRF support * Authentication on requests, such as Basic, Windows and Azure AD +* Authorisation support on requests, using Roles, Groups, Scopes, etc. * Support for dynamically building Routes from Functions and Modules * Generate/bind self-signed certificates * Secret management support to load secrets from vaults diff --git a/alpine.dockerfile b/alpine.dockerfile index dafff22aa..3880711d0 100644 --- a/alpine.dockerfile +++ b/alpine.dockerfile @@ -1,4 +1,4 @@ -FROM mcr.microsoft.com/powershell:7.3-alpine-3.15 +FROM mcr.microsoft.com/powershell:7.3-alpine-3.17 LABEL maintainer="Matthew Kelly (Badgerati)" RUN mkdir -p /usr/local/share/powershell/Modules/Pode COPY ./pkg/ /usr/local/share/powershell/Modules/Pode \ No newline at end of file diff --git a/arm32.dockerfile b/arm32.dockerfile index 1574f6e31..d3804fa55 100644 --- a/arm32.dockerfile +++ b/arm32.dockerfile @@ -1,4 +1,4 @@ -FROM mcr.microsoft.com/powershell:preview-7.3-arm32v7-ubuntu-18.04 +FROM mcr.microsoft.com/powershell:7.3-ubuntu-22.04-arm32 LABEL maintainer="Matthew Kelly (Badgerati)" RUN mkdir -p /usr/local/share/powershell/Modules/Pode COPY ./pkg/ /usr/local/share/powershell/Modules/Pode \ No newline at end of file diff --git a/docs/Getting-Started/Migrating/0X-to-1X.md b/docs/Getting-Started/Migrating/0X-to-1X.md index 36e9e3133..8760d6d78 100644 --- a/docs/Getting-Started/Migrating/0X-to-1X.md +++ b/docs/Getting-Started/Migrating/0X-to-1X.md @@ -186,7 +186,7 @@ There is a new [`New-PodeMiddleware`](../../../Functions/Middleware/New-PodeMidd ([Tutorial](../../../Tutorials/Middleware/Types/Sessions)) -The `session` function has now been replaced by the new [`Enable-PodeSessionMiddleware`](../../../Functions/Middleware/Enable-PodeSessionMiddleware) function. With the new function, not only will it automatically enabled session middleware for you, but the old `-Options` hashtable has now been converted into proper function parameters. +The `session` function has now been replaced by the new [`Enable-PodeSessionMiddleware`](../../../Functions/Sessions/Enable-PodeSessionMiddleware) function. With the new function, not only will it automatically enabled session middleware for you, but the old `-Options` hashtable has now been converted into proper function parameters. ### CSRF diff --git a/docs/Hosting/IIS.md b/docs/Hosting/IIS.md index 2620d8549..ef3c68247 100644 --- a/docs/Hosting/IIS.md +++ b/docs/Hosting/IIS.md @@ -129,6 +129,10 @@ This allows you to write a Pode server that works locally, but will also automat !!! note This does mean that Pode will force all endpoints to `127.0.0.1:PORT`. So if you had two different IPs before, they'll be merged into one. Something to be aware of if you assign routes to specific endpoints, as under IIS this won't work. +### Maximum Worker Processes + +Unless you're using an external data store for sessions, ensure the Maximum Worker Processes is 1. Each worker process will spawn a new instance of your Pode server, and if using Pode's inbuilt session storage you'll face authenticated/session timeout issues when one instance doesn't contain the right session. + ### Advanced/Domain/Kerberos The above IIS site setup works, but only for simple sites. If you require the use of the Active Directory module, or your site to be running as a different user then follow the steps below. diff --git a/docs/Tutorials/Authentication/Inbuilt/AzureAD.md b/docs/Tutorials/Authentication/Inbuilt/AzureAD.md index 167942037..0c43fd9d4 100644 --- a/docs/Tutorials/Authentication/Inbuilt/AzureAD.md +++ b/docs/Tutorials/Authentication/Inbuilt/AzureAD.md @@ -41,6 +41,8 @@ To setup and start using Azure AD authentication in Pode you use `New-PodeAuthAz ```powershell Start-PodeServer { + Enable-PodeSessionMiddleware -Duration 120 -Extend + $scheme = New-PodeAuthAzureADScheme -ClientID '' -ClientSecret '' -Tenant '' $scheme | Add-PodeAuth -Name 'Login' -FailureUrl '/login' -SuccessUrl '/' -ScriptBlock { @@ -63,6 +65,8 @@ To setup Azure AD authentcation, but using your own Form login, then you can use ```powershell Start-PodeServer { + Enable-PodeSessionMiddleware -Duration 120 -Extend + $form = New-PodeAuthScheme -Form $scheme = New-PodeAuthAzureADScheme -ClientID '' -ClientSecret '' -Tenant '' -InnerScheme $form @@ -97,7 +101,7 @@ The Pode side needs to be configured to allow basic authentication as well. This $form = New-PodeAuthScheme -Form $schemeForm = New-PodeAuthAzureADScheme -ClientID '' -ClientSecret '' -Tenant '' -InnerScheme $form -$basic = New-PodeAuthSceme -Basic +$basic = New-PodeAuthScheme -Basic $schemeBasic = New-PodeAuthAzureADScheme -ClientID '' -ClientSecret '' -Tenant '' -InnerScheme $basic $authLogin = { diff --git a/docs/Tutorials/Authentication/Inbuilt/Session.md b/docs/Tutorials/Authentication/Inbuilt/Session.md new file mode 100644 index 000000000..809a8a05e --- /dev/null +++ b/docs/Tutorials/Authentication/Inbuilt/Session.md @@ -0,0 +1,59 @@ +# Sessions + +Pode has support for Sessions when using Authentication, by default if you call a Route with authentication and you already have a session on the request then you're "authenticated". If there's no session, then the authentication logic is invoked, and if the details are invalid you're redirected to a login screen. + +If you have a need to use multiple authentication methods for login, and the user can chose the one they want, then on Routes there's no simple way of say which authentication is required. However, under the hood they all create a session object which can be used as a "shared" authentication method. + +This sessions authenticator can be used to pass authentication if a valid session in on the request, or to automatically redirect to a login page if there is no valid session. Useful for if you're using multiple authentication methods the user can choose from. + +## Usage + +To add sessions authentication you can use [`Add-PodeAuthSession`](../../../../Functions/Authentication/Add-PodeAuthSession). The following example will validate a user's credentials on login using Form authentication, but the home page uses session authentication to just verify there's a valid session: + +```powershell +Start-PodeServer { + # endpoint and view engine + Add-PodeEndpoint -Address * -Port 8085 -Protocol Http + Set-PodeViewEngine -Type Pode + + # enable sessions + Enable-PodeSessionMiddleware -Duration 120 -Extend + + # setup form auth for login + New-PodeAuthScheme -Form | Add-PodeAuth -Name 'FormAuth' -FailureUrl '/login' -SuccessUrl '/' -ScriptBlock { + param($username, $password) + + # here you'd check a real user storage, this is just for example + if ($username -eq 'morty' -and $password -eq 'pickle') { + return @{ User = @{ Name = 'Morty' } } + } + + return @{ Message = 'Invalid details supplied' } + } + + # setup session auth for routes and logout + Add-PodeAuthSession -Name 'SessionAuth' -FailureUrl '/login' + + # home page: use session auth, and redirect to login if no valid session + Add-PodeRoute -Method Get -Path '/' -Authentication SessionAuth -ScriptBlock { + Write-PodeViewResponse -Path 'auth-home' + } + + # login page: use form auth here to actually verify the user's credentials + Add-PodeRoute -Method Get -Path '/login' -Authentication FormAuth -Login -ScriptBlock { + Write-PodeViewResponse -Path 'auth-login' -FlashMessages + } + + # login check: again, use form auth + Add-PodeRoute -Method Post -Path '/login' -Authentication FormAuth -Login + + # logout - use session auth here to purge the session + Add-PodeRoute -Method Post -Path '/logout' -Authentication SessionAuth -Logout +} +``` + +### User Object + +If a valid session is found on the request, then the user object set at `$WebEvent.Auth.User` will take the form of which ever authentication method using for login. + +The user object will simply be loaded from the session. diff --git a/docs/Tutorials/Authentication/Inbuilt/UserFile.md b/docs/Tutorials/Authentication/Inbuilt/UserFile.md index 2a28456b7..564c54dbc 100644 --- a/docs/Tutorials/Authentication/Inbuilt/UserFile.md +++ b/docs/Tutorials/Authentication/Inbuilt/UserFile.md @@ -62,6 +62,7 @@ Start-PodeServer { Regardless of whether the password is a standard SHA256 hash or HMAC hash, the hashed output should be a base64 string. The following functions will return the hashed value in the expected format: **SHA256 HASH**: + ```powershell function ConvertTo-SHA256([string]$String) { @@ -73,6 +74,7 @@ function ConvertTo-SHA256([string]$String) ``` **HMAC HASH:** + ```powershell function ConvertTo-HMACSHA256([string]$String, [string]$Secret) { $HMACSHA256 = New-Object System.Security.Cryptography.HMACSHA256 diff --git a/docs/Tutorials/Authentication/Inbuilt/WindowsAD.md b/docs/Tutorials/Authentication/Inbuilt/WindowsAD.md index 7300ea855..e6a2c77b2 100644 --- a/docs/Tutorials/Authentication/Inbuilt/WindowsAD.md +++ b/docs/Tutorials/Authentication/Inbuilt/WindowsAD.md @@ -149,3 +149,7 @@ New-PodeAuthScheme -Form | Add-PodeAuthWindowsAd -Name 'Login' -ScriptBlock { return @{ Message = 'Authorisation failed' } } ``` + +## Protected Users + +In Windows AD there is a "Protected Users" group that you can assign users into. If users in this group are trying to use your site, then they will fail authentication. Unfortunately, this is just a secure feature of Windows AD, and the only way around this is to take the affected users out of the Protected Users group. diff --git a/docs/Tutorials/Authentication/Methods/Bearer.md b/docs/Tutorials/Authentication/Methods/Bearer.md index 73cb7bc25..77f92abbe 100644 --- a/docs/Tutorials/Authentication/Methods/Bearer.md +++ b/docs/Tutorials/Authentication/Methods/Bearer.md @@ -12,7 +12,7 @@ To start using Bearer authentication in Pode you can use `New-PodeAuthScheme -Be ```powershell Start-PodeServer { - New-PodeAuthScheme -Bearer | Add-PodeAuth -Name 'Authenticate' -ScriptBlock { + New-PodeAuthScheme -Bearer | Add-PodeAuth -Name 'Authenticate' -Sessionless -ScriptBlock { param($token) # check if the token is valid, and get user diff --git a/docs/Tutorials/Authentication/Overview.md b/docs/Tutorials/Authentication/Overview.md index 31562f89c..a38b3ba7f 100644 --- a/docs/Tutorials/Authentication/Overview.md +++ b/docs/Tutorials/Authentication/Overview.md @@ -7,9 +7,9 @@ Authentication can either be sessionless (requiring validation on every request) To setup and use authentication in Pode you need to use the [`New-PodeAuthScheme`](../../../Functions/Authentication/New-PodeAuthScheme) and [`Add-PodeAuth`](../../../Functions/Authentication/Add-PodeAuth) functions. -## Usage +You can also setup [Authorisation](../../Authorisation/Overview) for use with Authentication as well. -### Schemes +## Schemes The [`New-PodeAuthScheme`](../../../Functions/Authentication/New-PodeAuthScheme) function allows you to create and configure authentication schemes, or you can create your own Custom authentication schemes. These schemes can then be piped into [`Add-PodeAuth`](../../../Functions/Authentication/Add-PodeAuth). The role of a scheme is to parse the request for any user credentials, or other information, that is required for a user to be authenticated. @@ -29,7 +29,7 @@ Or you can define a custom scheme: * [Custom](../Methods/Custom) -### Validators +## Validators The [`Add-PodeAuth`](../../../Functions/Authentication/Add-PodeAuth) function allows you to add authentication validators to your server. You can have many methods configured, defining which one to validate against using the `-Authentication` parameter on Routes. Their job is to validate the information parsed from the supplied scheme to ensure a user is valid. @@ -49,7 +49,7 @@ The `-Name` of the authentication method must be unique. The `-Scheme` comes fro The `-ScriptBlock` is used to validate a user, checking if they exist and the password is correct (or checking if they exist in some data store). If the ScriptBlock succeeds, then a `User` object needs to be returned from the script as `@{ User = $user }`. If `$null`, or a null user, is returned then the script is assumed to have failed - meaning the user will have failed authentication, and a 401 response is returned. -#### Custom Status and Headers +### Custom Status and Headers When authenticating a user in Pode, any failures will return a 401 response with a generic message. You can inform Pode to return a custom message/status from [`Add-PodeAuth`](../../../Functions/Authentication/Add-PodeAuth) by returning the relevant hashtable values. @@ -93,7 +93,7 @@ return @{ } ``` -#### Authenticate Type/Realm +### Authenticate Type/Realm When authentication fails, and a 401 response is returned, then Pode will also attempt to Response back to the client with a `WWW-Authenticate` header (if you've manually set this header using the custom headers from above, then the custom header will be used instead). For the inbuilt types, such as Basic, this Header will always be returned on a 401 response. @@ -114,7 +114,7 @@ WWW-Authenticate: Basic realm="Enter creds to access site" !!! note If no Realm was set then it would just look as follows: `WWW-Authenticate: Basic` -#### Redirecting +### Redirecting When building custom authenticators, it might be required that you have to redirect mid-auth and stop processing the current request. To achieve this you can return the following from the scriptblock of `New-PodeAuthScheme` or `Add-PodeAuth`: @@ -122,7 +122,9 @@ When building custom authenticators, it might be required that you have to redir return @{ IsRedirected = $true } ``` -### Routes/Middleware +An example of this could be OAuth2, where the authentication needs to redirect to the Provider. + +## Routes/Middleware To use an authentication on a specific route, you can use the `-Authentication` parameter on the [`Add-PodeRoute`](../../../Functions/Routes/Add-PodeRoute) function; this takes the Name supplied to the `-Name` parameter on [`Add-PodeAuth`](../../../Functions/Authentication/Add-PodeAuth). This will set the authentication up to run before other route middleware. @@ -161,6 +163,8 @@ The `Auth` object will also contain: | User | Details about the authenticated user | | IsAuthenticated | States if the request is for an authenticated user, can be `$true`, `$false` or `$null` | | Store | States whether the authentication is for a session, and will be stored as a cookie | +| IsAuthorised | If using [Authorisation](../../Authorisation/Overview), this value will be `$true` or `$false` depending on whether or not the authenticated user is authorised to access the Route. If not using Authorisation this value will just be `$true` | +| Name | The name(s) of the Authentication methods which passed - useful if you're using merged Authentications and you want to know which one(s) passed | The following example get the user's name from the `Auth` object: @@ -172,6 +176,141 @@ Add-PodeRoute -Method Get -Path '/' -Authentication 'Login' -Login -ScriptBlock } ``` +## Merging + +For advanced authentication scenarios, you can merge multiple authentication methods together using [`Merge-PodeAuth`](../../../Functions/Authentication/Merge-PodeAuth). This allows you to have an authentication strategy where multiple authentications are required to pass for a user to be fully authenticated, or you could have fallback authentications should the primary authentication fail. + +When you merge authentication methods together, it becomes a new authentication method which you can supply to `-Authentication` on Routes. By default the merged authentications expect just one to pass, but you can state that you require all to pass via the `-Valid` parameter on [`Merge-PodeAuth`](../../../Functions/Authentication/Merge-PodeAuth). + +### All + +For example, you might require an API Key and Basic authentication for a user to view a Route, in which case you would set something up as follows: + +```powershell +# setup apikey auth +New-PodeAuthScheme -ApiKey -Location Header | Add-PodeAuth -Name 'ApiKey' -Sessionless -ScriptBlock { + param($key) + + # here you'd check a real user storage, this is just for example + if ($key -ieq 'test-api-key') { + return @{ User = @{ Name = 'Morty' } } + } + + return $null +} + +# setup basic auth +New-PodeAuthScheme -Basic | Add-PodeAuth -Name 'Basic' -Sessionless -ScriptBlock { + param($username, $password) + + # here you'd check a real user storage, this is just for example + if ($username -eq 'morty' -and $password -eq 'pickle') { + return @{ User = @{ Name = 'Morty' } } + } + + return @{ Message = 'Invalid details supplied' } +} + +# merge the authentications together, and require all to pass +Merge-PodeAuth -Name 'MergedAuth' -Authentication 'ApiKey', 'Basic' -Valid All + +# use the merged auth in a route +Add-PodeRoute -Method Get -Path '/users' -Authentication 'MergedAuth' -ScriptBlock { + Write-PodeJsonResponse -Value @{ + Users = $WebEvent.Auth.User + } +} +``` + +### One + +Or, you might want to check for a JWT Bearer token, but if one isn't present default to Azure AD authentication so we can store the returned access token and utilise the first Bearer auth later on: + +```powershell +# setup jwt bearer auth +New-PodeAuthScheme -Bearer -AsJWT | Add-PodeAuth -Name 'Bearer' -Sessionless -ScriptBlock { + param($payload) + # check payload + return @{ User = @{ Name = 'Morty' } } +} + +# setup basic azure-ad auth +$basic = New-PodeAuthScheme -Basic +$scheme = New-PodeAuthAzureADScheme -ClientID '' -ClientSecret '' -Tenant '' -InnerScheme $basic +$scheme | Add-PodeAuth -Name 'AzureAD' -Sessionless -ScriptBlock { + param($user, $accessToken, $refreshToken, $response) + # check if the user is valid + return @{ User = $user } +} + +# merge the authentications together, and require just one to pass +Merge-PodeAuth -Name 'MergedAuth' -Authentication 'Bearer', 'AzureAD' -Valid One + +# use the merged auth in a route +Add-PodeRoute -Method Get -Path '/users' -Authentication 'MergedAuth' -ScriptBlock { + Write-PodeJsonResponse -Value @{ + Users = $WebEvent.Auth.User + } +} +``` + +### Advanced + +You can also merge together other merged authentication methods. This lets you build scenarios where you require an API key, and then need either a JWT Bearer token or OAuth2 to pass. As a very brief example: + +```powershell +# setup apikey auth +New-PodeAuthScheme -ApiKey -Location Header | Add-PodeAuth -Name 'ApiKey' -Sessionless -ScriptBlock { + param($key) + + # here you'd check a real user storage, this is just for example + if ($key -ieq 'test-api-key') { + return @{ User = @{ Name = 'Morty' } } + } + + return $null +} + +# setup jwt bearer auth +New-PodeAuthScheme -Bearer -AsJWT | Add-PodeAuth -Name 'Bearer' -Sessionless -ScriptBlock { + param($payload) + # check payload + return @{ User = @{ Name = 'Morty' } } +} + +# setup basic azure-ad auth +$basic = New-PodeAuthScheme -Basic +$scheme = New-PodeAuthAzureADScheme -ClientID '' -ClientSecret '' -Tenant '' -InnerScheme $basic +$scheme | Add-PodeAuth -Name 'AzureAD' -Sessionless -ScriptBlock { + param($user, $accessToken, $refreshToken, $response) + # check if the user is valid + return @{ User = $user } +} + +# merge the authentications together, and require just one to pass +Merge-PodeAuth -Name 'JwtMergedAuth' -Authentication 'Bearer', 'AzureAD' -Valid One + +# merge the above merged auth with the apikey auth, and require both to pass +Merge-PodeAuth -Name 'ApiMergedAuth' -Authentication 'ApiKey', 'JwtMergedAuth' -Valid All + +# use the merged auth in a route +Add-PodeRoute -Method Get -Path '/users' -Authentication 'ApiMergedAuth' -ScriptBlock { + Write-PodeJsonResponse -Value @{ + Users = $WebEvent.Auth.User + } +} +``` + +### Users + +When using a single authentication method, the authenticated user's details will be accessible at `$WebEvent.Auth.User`. However, when you're using a merged authentication method you could end up with 2 or more user objects being returned from each authentication method. + +Because of this, when using a merged authentication, the user object will instead be found at `$WebEvent.Auth.User[]`. For example, if you have a authentication method with name "BearerAuth", then the user's details would be at `$WebEvent.Auth.User['BearerAuth']` - not `$WebEvent.Auth.User`. + +### Parameters + +Similar to [`Add-PodeAuth`](../../../Functions/Authentication/Add-PodeAuth) the [`Merge-PodeAuth`](../../../Functions/Authentication/Merge-PodeAuth) function also supports the `-FailureUrl`, `-SuccessUrl`, etc. parameters. When set on the merged authentication method, these fails will be used as a fallback if the initial authentication methods don't have them set. This means you can setup 2 authentication methods without Failure URLs, and then merge them together with a default one of `/login` on the merged authentication. + ## Inbuilt Authenticators Overtime Pode will start to support inbuilt authentication methods - such as [Windows Active Directory](../Inbuilt/WindowsAD). More information can be found in the Inbuilt section. diff --git a/docs/Tutorials/Authorisation/Overview.md b/docs/Tutorials/Authorisation/Overview.md new file mode 100644 index 000000000..fb59aa807 --- /dev/null +++ b/docs/Tutorials/Authorisation/Overview.md @@ -0,0 +1,335 @@ +# Overview + +Authorisation can either be used in conjunction with [Authentication](../../Authentication/Overview) and [Routes](../../Routes/Overview), or on it's own for custom scenarios. + +When used with Authentication, Pode can automatically authorise access to Routes based on Roles; Groups; Scopes; Users; or custom validation logic for you, using the currently authenticated User's details. When authorisation fails Pode will respond with an HTTP 403 status code. + +With authentication, Pode will set the following properties on the `$WebEvent.Auth` object: + +| Name | Description | +| ---- | ----------- | +| IsAuthorised | This value will be `$true` or `$false` depending on whether or not the authenticated user is authorised to access the Route | + +## Create an Access Method + +To validate authorisation in Pode you'll first need to create an Access scheme using [`New-PodeAccessScheme`](../../../Functions/Access/New-PodeAccessScheme), and then an Access method using [`Add-PodeAccess`](../../../Functions/Authentication/Add-PodeAccess). At its most simple you'll just need a Name, Type and possibly a Match type. + +For example, you can create a simple Access method for any of the inbuilt types as follows: + +```powershell +New-PodeAccessScheme -Type Role | Add-PodeAccess -Name 'RoleExample' +New-PodeAccessScheme -Type Group | Add-PodeAccess -Name 'GroupExample' +New-PodeAccessScheme -Type Scope | Add-PodeAccess -Name 'ScopeExample' +New-PodeAccessScheme -Type User | Add-PodeAccess -Name 'UserExample' +``` + +### Match Type + +Pode supports 3 inbuilt "Match" types for validating access to resources: One, All and None. The default Match type is One; each of them are applied as follows: + +| Type | Description | +| ---- | ----------- | +| One | If the Source's (ie: User's) access values contain at least one of the Destination's (ie: Route's) access values, then authorisation is granted. | +| All | The Source's access values must contain all of the Destination's access values for authorisation to be granted. | +| None | The Source's access values must contain none of the Destination's access values for authorisation to be granted. | + +For example, to setup an Access method where a User must be in every Group that a Route specifies: + +```powershell +New-PodeAccessScheme -Type Group | Add-PodeAccess -Name 'GroupExample' -Match All +``` + +### User Access Lookup + +When using Access methods with Authentication and Routes, Pode will lookup the User's "access values" from the `$WebEvent.Auth.User` object. The property within this object that Pode uses depends on the `-Type` supplied to [`New-PodeAccessScheme`](../../../Functions/Access/New-PodeAccessScheme): + +| Type | Property | +| ---- | -------- | +| Role | Roles | +| Group | Groups | +| Scope | Scopes | +| User | Username | +| Custom | n/a - you must supply a `-Path` or `-ScriptBlock` to [`New-PodeAccessScheme`](../../../Functions/Access/New-PodeAccessScheme) | + +You can override this default lookup in one of two ways, by either supplying a custom property `-Path` or a `-ScriptBlock` for more a more advanced lookup (ie: external sources). + +!!! note + If you're using Access methods in a more adhoc manner via [`Test-PodeAccess`](../../../Functions/Authentication/Test-PodeAccess), the `-Path` property does nothing. However, if you don't supply a `-Source` to this function then the `-ScriptBlock` will be invoked. + +#### Lookup Path + +The `-Path` property on [`New-PodeAccessScheme`](../../../Functions/Access/New-PodeAccessScheme) allows you to specify a custom property path within the `$WebEvent.Auth.User` object, which will be used to retrieve the access values for the User. + +For example, if you have Roles for the User set in a `Roles` property within a `Metadata` property, then you'd use: + +```powershell +New-PodeAccessScheme -Type Role -Path 'Metadata.Roles' | Add-PodeAccess -Name 'RoleExample' + +<# +$User = @{ + Username = 'joe.bloggs' + Metadata = @{ + Roles = @('Developer') + } +} +#> +``` + +And Pode will retrieve the appropriate data for you. + +#### Lookup ScriptBlock + +If the source access values you require are not stored in the `$WebEvent.Auth.User` object but else where (ie: external source), then you can supply a `-ScriptBlock` on [`New-PodeAccessScheme`](../../../Functions/Access/New-PodeAccessScheme). When Pode attempts to retrieve access values for the User, or another Source, this scriptblock will be invoked. + +!!! note + When using this scriptblock with Authentication the currently authenticated User will be supplied as the first parameter, followed by the `-ArgumentList` values. When using the Access methods in a more adhoc manner via [`Test-PodeAccess`](../../../Functions/Authentication/Test-PodeAccess), just the `-ArgumentList` values are supplied. + +For example, if the Role values you need to retrieve are stored in some SQL database: + +```powershell +$scheme = New-PodeAccessScheme -Type Role -ScriptBlock { + param($user) + return Invoke-Sqlcmd -Query "SELECT Roles FROM UserRoles WHERE Username = '$($user.Username)'" -ServerInstance '(local)' +} + +$scheme | Add-PodeAccess -Name 'RoleExample' +``` + +Or if you need to get the Groups from AD: + +```powershell +$scheme = New-PodeAccessScheme -Type Group -ScriptBlock { + param($user) + return Get-ADPrincipalGroupMembership $user.Username | select name +} + +$scheme | Add-PodeAccess -Name 'GroupExample' +``` + +### Custom Validator + +By default Pode will perform basic array contains checks, to see if the Source/Destination access values meet the `-Match` type required which was set on [`Add-PodeAccess`](../../../Functions/Access/Add-PodeAccess). + +For example, if the User has just the Role value `Developer`, and Route has `-Role` values of `Developer` and `QA` supplied, and the `-Match` type is left as `One`, then "if the User Role is contained within the Routes Roles" access is authorised. + +However, if you require a more custom/advanced validation logic to be applied, you can supply a `-ScriptBlock` to [`Add-PodeAccess`](../../../Functions/Authentication/Add-PodeAccess). The scriptblock will be supplied with the "Source" access values as the first parameter; the "Destination" access values as the second parameter; and then followed by the `-ArgumentList` values. This scriptblock should return a boolean value: true if authorisation granted, or false otherwise. + +!!! note + Supplying a `-ScriptBlock` will override the `-Match` type supplied, as this scriptblock will be used for validation instead of Pode's inbuilt Match logic. + +For example, if you want to validate that the User's Scopes definitely contains a Route's first Scope value and then at least any 1 of the other Scope values: + +```powershell +New-PodeAccessScheme -Type Scope | Add-PodeAccess -Name 'ScopeExample' -ScriptBlock { + param($userScopes, $routeScopes) + + if ($routeScopes[0] -inotin $userScopes) { + return $false + } + + foreach ($scope in $routeScopes[1..($routeScopes.Length - 1)]) { + if ($scope -iin $userScopes) { + return $true + } + } + + return $false +} +``` + +## Using with Routes + +The Access methods will most commonly be used in conjunction with [Authentication](../../Authentication/Overview) and [Routes](../../Routes/Overview). When used together, Pode will automatically validate Route Authorisation for after the Authentication flow. If authorisation fails, an HTTP 403 status code will be returned. + +After creating an Access method as outlined above, you can supply the Access method Name to [`Add-PodeRoute`](../../../Functions/Routes/Add-PodeRoute), and other Route functions, using the `-Access` parameter. + +On [`Add-PodeRoute`](../../../Functions/Routes/Add-PodeRoute) and [`Add-PodeRouteGroup`](../../../Functions/Routes/Add-PodeRouteGroup) there are also the following parameters: `-Role`, `-Group`, `-Scope`, and `-User`. You can supply one ore more string values to these parameters, depending on which Access method type you're using. + +For example, to verify access to a Route to authorise only Developer role users: + +```powershell +Start-PodeServer { + Add-PodeEndpoint -Address * -Port 8080 -Protocol Http + + # create a simple role access method + New-PodeAccessScheme -Type Role | Add-PodeAccess -Name 'RoleExample' + + # setup Basic authentication + New-PodeAuthScheme -Basic | Add-PodeAuth -Name 'AuthExample' -Sessionless -ScriptBlock { + param($username, $password) + + # here you'd check a real user storage, this is just for example + if (($username -eq 'morty') -and ($password -eq 'pickle')) { + return @{ + User = @{ + Username = 'Morty' + Roles = @('Developer') + } + } + } + + # authentication failed + return $null + } + + # create a route which only developers can access + Add-PodeRoute -Method Get -Path '/route1' -Role 'Developer' -Authentication 'AuthExample' -Access 'RoleExample' -ScriptBlock { + Write-PodeJsonResponse -Value @{ 'Value' = 'Hello!' } + } + + # create a route which only admins can access + Add-PodeRoute -Method Get -Path '/route2' -Role 'Admin' -Authentication 'AuthExample' -Access 'RoleExample' -ScriptBlock { + Write-PodeJsonResponse -Value @{ 'Value' = 'Hi!' } + } +} +``` + +Calling the following will succeed: + +```powershell +Invoke-RestMethod -Uri http://localhost:8080/route1 -Method Get -Headers @{ Authorization = 'Basic bW9ydHk6cGlja2xl' } +``` + +But calling the following will fail with a 403: + +```powershell +Invoke-RestMethod -Uri http://localhost:8080/route2 -Method Get -Headers @{ Authorization = 'Basic bW9ydHk6cGlja2xl' } +``` + +## Merging + +Similar to Authentication methods, you can also merge Access methods using [`Merge-PodeAccess`](../../../Functions/Authentication/Merge-PodeAccess). This allows you to have an access strategy where multiple authorisations are required to pass for a user to be fully authorised, or just one of several possible methods. + +When you merge access methods together, it becomes a new access method which you can supply to `-Access` on [`Add-PodeRoute`](../../../Functions/Routes/Add-PodeRoute). By default the merged access method expects just one to pass, but you can state that you require all to pass via the `-Valid` parameter on [`Merge-PodeAccess`](../../../Functions/Authentication/Merge-PodeAccess). + +Using the same example above, we could add Group authorisation to this as well so the Developers have to be in a Software Group, and the Admins in a Operations Group: + +```powershell +Start-PodeServer { + Add-PodeEndpoint -Address * -Port 8080 -Protocol Http + + # create simple role and group access methods + New-PodeAccessScheme -Type Role | Add-PodeAccess -Name 'RoleExample' + New-PodeAccessScheme -Type Group | Add-PodeAccess -Name 'GroupExample' + + # setup a merged access + Merge-PodeAccess -Name 'MergedExample' -Access 'RoleExample', 'GroupExample' -Valid All + + # setup Basic authentication + New-PodeAuthScheme -Basic | Add-PodeAuth -Name 'AuthExample' -Sessionless -ScriptBlock { + param($username, $password) + + # here you'd check a real user storage, this is just for example + if (($username -eq 'morty') -and ($password -eq 'pickle')) { + return @{ + User = @{ + Username = 'Morty' + Roles = @('Developer') + Groups = @('Software') + } + } + } + + # authentication failed + return $null + } + + # create a route which only developers can access + Add-PodeRoute -Method Get -Path '/route1' -Role 'Developer' -Group 'Software' -Authentication 'AuthExample' -Access 'MergedExample' -ScriptBlock { + Write-PodeJsonResponse -Value @{ 'Value' = 'Hello!' } + } + + # create a route which only admins can access + Add-PodeRoute -Method Get -Path '/route2' -Role 'Admin' -Group 'Operations' -Authentication 'AuthExample' -Access 'MergedExample' -ScriptBlock { + Write-PodeJsonResponse -Value @{ 'Value' = 'Hi!' } + } +} +``` + +## Custom Access + +Pode has inbuilt support for Roles, Groups, Scopes, and Users authorisation on Routes. However, if you need to setup a more Custom authorisation policy on Routes you can create a custom Access scheme by supplying `-Custom` to [`New-PodeAccessScheme`](../../../Functions/Access/New-PodeAccessScheme), and add custom access values to a Route using [`Add-PodeAccessCustom`](../../../Functions/Authentication/Add-PodeAccessCustom). + +Custom access values for a User won't be automatically loaded from the authenticated User object, and a `-Path` or `-ScriptBlock` on [`New-PodeAccessScheme`](../../../Functions/Access/New-PodeAccessScheme) will be required. + +For example, if you wanted to authorise access from a set of user attributes, and based on favourite colour, you could do the following: + +```powershell +Start-PodeServer { + Add-PodeEndpoint -Address * -Port 8080 -Protocol Http + + # create a simple role access method + New-PodeAccessScheme -Custom -Path 'Metadata.Attributes' | Add-PodeAccess -Name 'CustomExample' -ScriptBlock { + param($userAttrs, $routeAttrs) + return ($userAttrs.Colour -ieq $routeAttrs.Colour) + } + + # setup Basic authentication + New-PodeAuthScheme -Basic | Add-PodeAuth -Name 'AuthExample' -Sessionless -ScriptBlock { + param($username, $password) + + # here you'd check a real user storage, this is just for example + if (($username -eq 'morty') -and ($password -eq 'pickle')) { + return @{ + User = @{ + Username = 'Morty' + Metadata = @{ + Attributes = @{ + Country = 'UK' + Colour = 'Blue' + } + } + } + } + } + + # authentication failed + return $null + } + + # create a route which only users who like the colour blue can access + Add-PodeRoute -Method Get -Path '/blue' -Authentication 'AuthExample' -Access 'CustomExample' -ScriptBlock { + Write-PodeJsonResponse -Value @{ 'Value' = 'Hello!' } + } -PassThru | + Add-PodeAccessCustom -Name 'CustomExample' -Value @{ Colour = 'Blue' } + + # create a route which only users who like the colour red can access + Add-PodeRoute -Method Get -Path '/red' -Authentication 'AuthExample' -Access 'CustomExample' -ScriptBlock { + Write-PodeJsonResponse -Value @{ 'Value' = 'Hi!' } + } -PassThru | + Add-PodeAccessCustom -Name 'CustomExample' -Value @{ Colour = 'Red' } +} +``` + +## Using Adhoc + +It is possible to invoke the Access method validation in an adhoc manner, without (or while) using Authentication, using [`Test-PodeAccess`](../../../Functions/Authentication/Test-PodeAccess). + +When using the Access methods outside of Authentication/Routes, the `-Type` doesn't really have any bearing. + +For example, you could create a Roles Access method and verify some Users Roles within a TCP Verb: + +```powershell +Start-PodeServer { + Add-PodeEndpoint -Address * -Port 9000 -Protocol Tcp -CRLFMessageEnd + + # create a role access method get retrieves roles from a database + $scheme = New-PodeAccessScheme -Type Role -ScriptBlock { + param($username) + return Invoke-Sqlcmd -Query "SELECT Roles FROM UserRoles WHERE Username = '$($username)'" -ServerInstance '(local)' + } + $scheme | Add-PodeAccess -Name 'RoleExample' + + # setup a Verb that only allows Developers + Add-PodeVerb -Verb 'EXAMPLE :username' -ScriptBlock { + if (!(Test-PodeAccess -Name 'RoleExample' -Destination 'Developer' -ArgumentList $TcpEvent.Parameters.username)) { + Write-PodeTcpClient -Message "Forbidden Access" + return + } + + Write-PodeTcpClient -Message "Hello, there!" + } +} +``` + +The `-ArgumentList`, on [`Test-PodeAccess`](../../../Functions/Authentication/Test-PodeAccess), will supply values as the first set of parameters to the `-ScriptBlock` defined on [`New-PodeAccessScheme`](../../../Functions/Access/New-PodeAccessScheme). diff --git a/docs/Tutorials/Endpoints/Basics.md b/docs/Tutorials/Endpoints/Basics.md index a2167a411..9bd89a81d 100644 --- a/docs/Tutorials/Endpoints/Basics.md +++ b/docs/Tutorials/Endpoints/Basics.md @@ -79,6 +79,13 @@ The below example will create a local self-signed HTTPS endpoint: Add-PodeEndpoint -Address * -Port 8443 -Protocol Https -SelfSigned ``` +### SSL Protocols + +By default Pode will use the SSL3 or TLS12 protocols - or just TLS12 if on MacOS. You can override this default in one of two ways: + +1. Update the global default in Pode's configuration file, as [described here](../../Certificates#ssl-protocols). +2. Specify specific SSL Protocols to use per Endpoints using the `-SslProtocol` parameter on [`Add-PodeEndpoint`](../../../Functions/Core/Add-PodeEndpoint). + ## Endpoint Names You can give endpoints unique names by supplying the `-EndpointName` parameter. This name can then be passed to [`Add-PodeRoute`](../../../Functions/Routes/Add-PodeRoute) or [`Add-PodeStaticRoute`](../../../Functions/Routes/Add-PodeStaticRoute) to bind these routes to that endpoint only. @@ -128,7 +135,8 @@ The following is the structure of the Endpoint object internally, as well as the | Hostname | string | The hostname of the Endpoint | | FriendlyName | string | A user friendly hostname to use when generating internal URLs | | Url | string | The full base URL of the Endpoint | -| Ssl | bool | Whether or not this Endpoint support support SSL | +| Ssl.Enabled | bool | Whether or not this Endpoint uses SSL | +| Ssl.Protocols | SslProtocols | An aggregated integer which specifies the SSL protocols this endpoints supports | | Protocol | string | The protocol of the Endpoint. Such as: HTTP, HTTPS, WS, etc. | | Type | string | The type of the Endpoint. Such as: HTTP, WS, SMTP, TCP | | Certificate | hashtable | Details about the certificate that will be used for SSL Endpoints | diff --git a/docs/Tutorials/Events.md b/docs/Tutorials/Events.md index 85e4eabb6..bd284a7f9 100644 --- a/docs/Tutorials/Events.md +++ b/docs/Tutorials/Events.md @@ -8,6 +8,11 @@ Pode lets you register scripts to be run when certain server events are triggere * Browser * Crash * Stop +* Running + +And these events are triggered in the following order: + +![event_flow](../../images/event-flow.png) ## Overview @@ -33,28 +38,32 @@ $evt = Get-PodeEvent -Type Start -Name '' ### Start -Scripts registered to the `Start` event will all be invoked just after the server's main scriptblock has been invoked - ie: the `-ScriptBlock` supplied to [`Start-PodeServer`](../../Functions/Core/Start-PodeServer). +Scripts registered to the `Start` event will all be invoked just after the `-ScriptBlock` supplied to [`Start-PodeServer`](../../Functions/Core/Start-PodeServer) has been invoked, and just before the runspaces for Pode have been opened. + +If you need the runspaces to be opened, you'll want to look at the `Running` event below. These scripts will also be re-invoked after a server restart has occurred. ### Terminate -Scripts registered to the `Terminate` event will all be invoked just before the server terminates. Ie, when the `Terminating...` message usually appears in the terminal, the script will run just after this and just before the `Done` message. - -These script *will not* run when a Restart is triggered. +Scripts registered to the `Terminate` event will all be invoked just before the server terminates. Ie, when the `Terminating...` message usually appears in the terminal, the script will run just after this and just before the `Done` message. Runspaces at this point will still be open. ### Restart -Scripts registered to the `Restart` event will all be invoked whenever an internal server restart occurs. This could be due to file monitoring, auto-restarting, `Ctrl+R`, or [`Restart-PodeServer`](../../Functions/Core/Restart-PodeServer). They will be invoked just after the `Restarting...` message appears in the terminal, and just before the `Done` message. +Scripts registered to the `Restart` event will all be invoked whenever an internal server restart occurs. This could be due to file monitoring, auto-restarting, `Ctrl+R`, or [`Restart-PodeServer`](../../Functions/Core/Restart-PodeServer). They will be invoked just after the `Restarting...` message appears in the terminal, and just before the `Done` message. Runspaces at this point will still be open. ### Browser -Scripts registered to the `Browser` event will all be invoked whenever the server is told to open a browser, ie: when `Ctrl+B` is pressed. +Scripts registered to the `Browser` event will all be invoked whenever the server is told to open a browser, ie: when `Ctrl+B` is pressed. Runspaces at this point will still be open. ### Crash -Scripts registered to the `Crash` event will all be invoked if the server ever terminates due to an exception being thrown. If a Crash event it triggered, then Terminate will not be triggered. +Scripts registered to the `Crash` event will all be invoked if the server ever terminates due to an exception being thrown. If a Crash event it triggered, then Terminate will not be triggered. Runspaces at this point will still be open, but there could be a chance not all of them will be available as the crash could have occurred from a runspace error. ### Stop -Scripts registered to the `Stop` event will all be invoked when the server stops and closes. This event will be fired after either the Terminate or Crash events - which ever one causes the server to ultimately stop. +Scripts registered to the `Stop` event will all be invoked when the server stops and closes. This event will be fired after either the Terminate or Crash events - which ever one causes the server to ultimately stop. Runspaces at this point will still be open. + +### Running + +Scripts registered to the `Running` event will all be run soon after the `Start` event, even after a `Restart`. At this point all of the runspaces will have been opened and available for use. diff --git a/docs/Tutorials/Logging/Types/Requests.md b/docs/Tutorials/Logging/Types/Requests.md index 627123c4c..3f31ffbaa 100644 --- a/docs/Tutorials/Logging/Types/Requests.md +++ b/docs/Tutorials/Logging/Types/Requests.md @@ -31,6 +31,22 @@ $method = New-PodeLoggingMethod -Custom -ScriptBlock { $method | Enable-PodeRequestLogging -Raw ``` +### Username + +If you're not using any Authentication then the "user" field in the log will always be "-". However, if you're using Authentication, and it passes, then the Username of the user accessing the Route will attempt to be retrieved from `$WebEvent.Auth.User`. The property within the authenticated user object by default is `Username`, but you can customise this using `-UsernameProperty`. + +For example, if the username was actually user "ID": + +```powershell +Enable-PodeRequestLogging -UsernameProperty 'ID' +``` + +Or if the username was inside another "Meta" property, and then within a "Username" property inside the Meta object: + +```powershell +Enable-PodeRequestLogging -UsernameProperty 'Meta.Username' +``` + ## Raw Request The raw Request hashtable that will be supplied to any Custom logging methods will look as follows: diff --git a/docs/Tutorials/Middleware/Types/Security.md b/docs/Tutorials/Middleware/Types/Security.md index 57efc8419..69f3d60f7 100644 --- a/docs/Tutorials/Middleware/Types/Security.md +++ b/docs/Tutorials/Middleware/Types/Security.md @@ -19,6 +19,8 @@ The following headers are currently supported, but you can add custom header val * X-Content-Type-Options * Referrer-Policy +You can also set the "Server" header to be hidden on responses if required. + ## Types Pode has an inbuilt wrapper to easily toggle all headers with default values: [`Set-PodeSecurity`](../../../../Functions/Security/Set-PodeSecurity). This function lets you specify a `-Type` of either `Simple` or `Strict`. The specified value will setup the headers with the default values defined below. You can also force `X-XSS-Protection` to use blocking mode if you want to support older browsers, or enable `Strict-Transport-Security` via `-UseHsts`. @@ -51,6 +53,8 @@ The following values are used for each header when the `Simple` type is supplied | X-Content-Type-Options | nosniff | | Referred-Policy | strict-origin | +The Server header is also hidden. + ### Strict The following values are used for each header when the `Strict` type is supplied: @@ -72,6 +76,8 @@ The following values are used for each header when the `Strict` type is supplied | X-Content-Type-Options | nosniff | | Referred-Policy | no-referrer | +The Server header is also hidden. + ## Headers You can setup the values of headers individually by using their relevant functions. @@ -205,6 +211,13 @@ The `Referrer-Policy` header tells the browser how much information to include i Set-PodeSecurityReferrerPolicy -Type Strict-Origin ``` +### Server + +You can hide or show the Server header on responses using the following functions, by default the Server header is visible: + +* [`Hide-PodeSecurityServer`](../../../../Functions/Security/Hide-PodeSecurityServer) +* [`Show-PodeSecurityServer`](../../../../Functions/Security/Show-PodeSecurityServer) + ## Custom There could be some headers for security that Pode doesn't support, but that you need. In this case you can use [`Add-PodeSecurityHeader`](../../../../Functions/Security/Add-PodeSecurityHeader) to specify a custom header and value that will be added: diff --git a/docs/Tutorials/Middleware/Types/Sessions.md b/docs/Tutorials/Middleware/Types/Sessions.md index 958a7c87c..fc1c04e5d 100644 --- a/docs/Tutorials/Middleware/Types/Sessions.md +++ b/docs/Tutorials/Middleware/Types/Sessions.md @@ -12,7 +12,7 @@ The duration of the session cookie/header can be specified, as well as whether t ## Usage -To initialise sessions in Pode you'll need to call [`Enable-PodeSessionMiddleware`](../../../../Functions/Middleware/Enable-PodeSessionMiddleware). This function will configure and automatically create the Middleware needed to enable sessions. By default sessions are set to use cookies, but support is also available for headers. +To initialise sessions in Pode you'll need to call [`Enable-PodeSessionMiddleware`](../../../../Functions/Sessions/Enable-PodeSessionMiddleware). This function will configure and automatically create the Middleware needed to enable sessions. By default sessions are set to use cookies, but support is also available for headers. Sessions are automatically signed using a random GUID. For Pode running on a single server using the default in-memory storage this is OK, however if you're running Pode on multiple servers, or if you're defining a custom storage then a `-Secret` is required - this is so that sessions from different servers, or after a server restart, don't become corrupt and unusable. @@ -46,7 +46,7 @@ The inbuilt SessionId generator used for sessions is a GUID, but you can supply If supplied, the `-Generator` is a scriptblock that must return a valid string. The string itself should be a random unique value, that can be used as a unique session identifier. -Within a route, or middleware, you can get the currently authenticated session'd ID using [`Get-PodeSessionId`](../../../../Functions/Middleware/Get-PodeSessionId). If there is no session, or the session is not authenticated, then `$null` is returned. This function can also returned the fully signed sessionId as well. If you want the sessionId even if it's not authenticated, then you can supply `-Force` to get the current SessionId back. +Within a route, or middleware, you can get the currently authenticated session'd ID using [`Get-PodeSessionId`](../../../../Functions/Sessions/Get-PodeSessionId). If there is no session, or the session is not authenticated, then `$null` is returned. This function can also returned the fully signed sessionId as well. If you want the sessionId even if it's not authenticated, then you can supply `-Force` to get the current SessionId back. ### Strict diff --git a/docs/Tutorials/Misc/DesktopApp.md b/docs/Tutorials/Misc/DesktopApp.md index debc03e5e..4f64f8c4b 100644 --- a/docs/Tutorials/Misc/DesktopApp.md +++ b/docs/Tutorials/Misc/DesktopApp.md @@ -5,7 +5,6 @@ Normally in Pode you define a server and run it, however if you use the [`Show-P !!! warning Currently only supported in Windows PowerShell, and PowerShell 7 on Windows due to using WPF. - ## Setting Server to run as Application To serve up you server as a desktop application you can just write you Pode server script as normal. The only difference is you can use the [`Show-PodeGui`](../../../Functions/Core/Show-PodeGui) function to display the application. @@ -61,11 +60,11 @@ In order to switch to [`CefSharp`](http://cefsharp.github.io/), either compile o Pode will automatically switch to CefSharp, if the binaries are loaded into the Powershell Session before the Pode Module itself gets initialized. The required packages are the following and can be compiled from scratch or downloaded from the Nuget Repository: + - [`cef.redist.x64`](https://www.nuget.org/packages/cef.redist.x64/) - [`cefsharp.common`](https://www.nuget.org/packages/cefsharp.common) - [`cefsharp.wpf`](https://www.nuget.org/packages/cefsharp.wpf) - This example shows how to load them: ```Powershell diff --git a/docs/Tutorials/Misc/UploadFiles.md b/docs/Tutorials/Misc/UploadFiles.md index af830dcc0..530a1fc59 100644 --- a/docs/Tutorials/Misc/UploadFiles.md +++ b/docs/Tutorials/Misc/UploadFiles.md @@ -39,6 +39,7 @@ The following HTML is an example of a `
` for a simple sign-up flow. Here t The inputs will be POSTed to the server, and accessible via the [web event](../../WebEvent)'s `.Data` and `.Files`. For the `.Data`: + ```powershell $WebEvent.Data['username'] # the username entered $WebEvent.Data['password'] # the password entered @@ -46,6 +47,7 @@ $WebEvent.Data['avatar'] # the name of the file (assume image.png) ``` For the `.Files`: + ```powershell $WebEvent.Files['image.png'] # the bytes of the uploaded file ``` @@ -60,7 +62,6 @@ If you use the `multiple` property then all the file names will be available und You can upload files from the CLI by using `Invoke-WebRequest` (or `Invoke-RestMethod`), and to do so you'll need to pass the `-Form` parameter. Assuming you have the following Route to save some "avatar" file: - ```powershell Start-PodeServer { Add-PodeEndpoint -Address * -Port 8085 -Protocol Http diff --git a/docs/Tutorials/Routes/Examples/AnonymousAccess.md b/docs/Tutorials/Routes/Examples/AnonymousAccess.md index 0fc9744f7..834c327e4 100644 --- a/docs/Tutorials/Routes/Examples/AnonymousAccess.md +++ b/docs/Tutorials/Routes/Examples/AnonymousAccess.md @@ -47,6 +47,10 @@ Add-PodeRoute -Method Get -Path '/' -Authentication 'Login' -AllowAnon -ScriptBl Now, when an authenticated user hits the page, they're shown the original personal greeting page with view counter. However, when an unauthenticated user hits the page they are shown a generic greeting with a login button. +The [`Test-PodeAuthUser`](../../../../Functions/Authentication/Test-PodeAuthUser) will check both the `$WebEvent.Auth` and `$WebEvent.Session` objects for an authenticated user. You can force the function to only check the former by supplying `-IgnoreSession`. + +You can also retrieve the user object using [`Get-PodeAuthUser`](../../../../Functions/Authentication/Get-PodeAuthUser). Under most circumstances you'll be able to access the authenticated user at `$WebEvent.Auth.User`, however if you're relying on Sessions and a Route without Authentication configured then you'll ave to use this function. Similar to above, you can supply `-IgnoreSession` here as well. + ## Example Code This is the full code for the server above: diff --git a/docs/Tutorials/Routes/Examples/LoginPage.md b/docs/Tutorials/Routes/Examples/LoginPage.md index 438566b8a..eaaee628a 100644 --- a/docs/Tutorials/Routes/Examples/LoginPage.md +++ b/docs/Tutorials/Routes/Examples/LoginPage.md @@ -35,7 +35,7 @@ Add-PodeEndpoint -Address * -Port 8080 -Protocol Http Set-PodeViewEngine -Type Pode ``` -To use sessions for our authentication (so we can stay logged in), we need to setup Session Middleware using the [`Enable-PodeSessionMiddleware`](../../../../Functions/Middleware/Enable-PodeSessionMiddleware) function. Here our sessions will last for 2 minutes, and will be extended on each request: +To use sessions for our authentication (so we can stay logged in), we need to setup Session Middleware using the [`Enable-PodeSessionMiddleware`](../../../../Functions/Sessions/Enable-PodeSessionMiddleware) function. Here our sessions will last for 2 minutes, and will be extended on each request: ```powershell Enable-PodeSessionMiddleware -Duration 120 -Extend diff --git a/docs/Tutorials/Routes/Examples/RestApiSessions.md b/docs/Tutorials/Routes/Examples/RestApiSessions.md index d63fab54e..f8b4dad2f 100644 --- a/docs/Tutorials/Routes/Examples/RestApiSessions.md +++ b/docs/Tutorials/Routes/Examples/RestApiSessions.md @@ -23,7 +23,7 @@ Add-PodeEndpoint -Address * -Port 8080 -Protocol Http ## Enabling Sessions -To use sessions with headers for our authentication, we need to setup Session Middleware using the [`Enable-PodeSessionMiddleware`](../../../../Functions/Middleware/Enable-PodeSessionMiddleware) function. Here our sessions will last for 2 minutes, and will be extended on each request: +To use sessions with headers for our authentication, we need to setup Session Middleware using the [`Enable-PodeSessionMiddleware`](../../../../Functions/Sessions/Enable-PodeSessionMiddleware/) function. Here our sessions will last for 2 minutes, and will be extended on each request: ```powershell Enable-PodeSessionMiddleware -Duration 120 -Extend -UseHeaders diff --git a/docs/Tutorials/Secrets/Overview.md b/docs/Tutorials/Secrets/Overview.md index e652d9e85..2aa9ae7d5 100644 --- a/docs/Tutorials/Secrets/Overview.md +++ b/docs/Tutorials/Secrets/Overview.md @@ -36,6 +36,9 @@ At present there are just two registration types implemented for registering sec * [Secret Management](../Types/SecretManagement) (powershell module) * [Custom](../Types/Custom) +!!! tip + For the SecretManagement module you can read a "getting started" [guide for it here](https://learn.microsoft.com/en-us/powershell/utility-modules/secretmanagement/how-to/using-secrets-in-automation?view=ps-modules) when using the module in automated scenarios. + ### Initialise If there is any logic that needs to be invoked before a vault is registered, such as connecting to a cloud provider first (ie: `Connect-AzAccount`), this can be achieved via the `-InitScriptBlock` parameter on [`Register-PodeSecretVault`](../../../Functions/Secrets/Register-PodeSecretVault). @@ -191,7 +194,6 @@ Mount-PodeSecret -Name 'SecretName2' -Vault 'VaultName' -Key 'SecretKeyNameInVau There is also support for creating/updating, retrieving, and removing secrets in an adhoc manor from registered vaults - without having to mount them. - ### Create To create a new secret, and well as update an existing ones value, you can use [`Set-PodeSecret`](../../../Functions/Secrets/Set-PodeSecret): diff --git a/docs/Tutorials/SharedState.md b/docs/Tutorials/SharedState.md index f391e5bcb..b34ba137e 100644 --- a/docs/Tutorials/SharedState.md +++ b/docs/Tutorials/SharedState.md @@ -4,13 +4,13 @@ Most things in Pode run in isolated runspaces: routes, middleware, schedules - t You also have the option of saving the current state to a file, and then restoring the state back on server start. This way you won't lose state between server restarts. -You can also use the State in combination with [`Lock-PodeObject`](../../Functions/Utilities/Lock-PodeObject) to ensure thread safety - if needed. +You can also use the State in combination with [`Lock-PodeObject`](../../Functions/Threading/Lock-PodeObject) to ensure thread safety - if needed. !!! tip - It's wise to use the State in conjunction with [`Lock-PodeObject`](../../Functions/Utilities/Lock-PodeObject), to ensure thread safety between runspaces. + It's wise to use the State in conjunction with [`Lock-PodeObject`](../../Functions/Threading/Lock-PodeObject), to ensure thread safety between runspaces. !!! warning - If you omit the use of [`Lock-PodeObject`](../../Functions/Utilities/Lock-PodeObject), you might run into errors due to multi-threading. Only omit if you are *absolutely confident* you do not need locking. (ie: you set in state once and then only ever retrieve, never updating the variable). + If you omit the use of [`Lock-PodeObject`](../../Functions/Threading/Lock-PodeObject), you might run into errors due to multi-threading. Only omit if you are *absolutely confident* you do not need locking. (ie: you set in state once and then only ever retrieve, never updating the variable). ## Usage @@ -98,7 +98,7 @@ Start-PodeServer { ### Save -The [`Save-PodeState`](../../Functions/State/Save-PodeState) function will save the current state, as JSON, to the specified file. The file path can either be relative, or literal. When saving the state, it's recommended to wrap the function within [`Lock-PodeObject`](../../Functions/Utilities/Lock-PodeObject). +The [`Save-PodeState`](../../Functions/State/Save-PodeState) function will save the current state, as JSON, to the specified file. The file path can either be relative, or literal. When saving the state, it's recommended to wrap the function within [`Lock-PodeObject`](../../Functions/Threading/Lock-PodeObject). An example of saving the current state every hour is as follows: @@ -120,7 +120,7 @@ By default the JSON will be saved expanded, but you can saved the JSON as compre ### Restore -The [`Restore-PodeState`](../../Functions/State/Restore-PodeState) function will restore the current state from the specified file. The file path can either be relative, or a literal path. if you're restoring the state immediately on server start, you don't need to use [`Lock-PodeObject`](../../Functions/Utilities/Lock-PodeObject). +The [`Restore-PodeState`](../../Functions/State/Restore-PodeState) function will restore the current state from the specified file. The file path can either be relative, or a literal path. if you're restoring the state immediately on server start, you don't need to use [`Lock-PodeObject`](../../Functions/Threading/Lock-PodeObject). An example of restore the current state on server start is as follows: diff --git a/docs/images/event-flow.png b/docs/images/event-flow.png new file mode 100644 index 000000000..47e8051ed Binary files /dev/null and b/docs/images/event-flow.png differ diff --git a/docs/index.md b/docs/index.md index 1060fb5c7..c5d175bd9 100644 --- a/docs/index.md +++ b/docs/index.md @@ -39,6 +39,7 @@ Pode is a Cross-Platform framework to create web servers that host REST APIs, We * Basic rate limiting for IP addresses and subnets * Middleware and Sessions on web servers, with Flash message and CSRF support * Authentication on requests, such as Basic, Windows and Azure AD +* Authorisation support on requests, using Roles, Groups, Scopes, etc. * Support for dynamically building Routes from Functions and Modules * Generate/bind self-signed certificates * Secret management support to load secrets from vaults diff --git a/docs/release-notes.md b/docs/release-notes.md index 3a8fe9f11..eec9e4468 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -1,7 +1,50 @@ # Release Notes +## v2.9.0 + +Date: 30th October 2023 + +```plain +### Features +* #992: Introduces new Authorisation middleware support + +### Enhancements +* #588: Adds support for merging Authentication methods into a new Authentication method +* #1082, #1107: Adds a new "Running" event type, which will be triggered once all Runspaces have started +* #1101: Adds a new `-SslProtocol` parameter to `Add-PodeEndpoint`, to allow setting SSL Protocols per endpoint +* #1106: Adds two new Security functions to control the hiding/showing of the Server header in responses +* #1142: The `Test-PodeJwt` function is now public (thanks @alan-null!) +* #1163: Adds a new "Session" authentication method, useful if you need multiple authentication methods and the user can choose one + +### Bugs +* #1030: Fixes an issue with some Authentication methods when `-AsCredential` was supplied on the scheme +* #1081: Don't attempt to parse the query string if there is no query string supplied +* #1083: Fixes a time-zone issue when verifying JWT "exp" and "nbf" properties (thanks @avin3sh!) +* #1087: Fixes an SMTP body parsing issue when multiple headers are in the request +* #1093: Allow greater JSON depths to be used when saving/restoring State (thanks @plk!) +* #1125: Fixes an issue where Verbs weren't being cleared down appropriately on server restart +* #1130: When request logging is enabled, and an authenticated user is available, the username will now be used and not "-" +* #1137: Fixes the loading of AutoImport configuration - it was being ignored! + +### Documentation +* #1078: Adds release dates to the releases notes page +* #1099: Adds a reference to the "Protected Users" group in the AD Authentication page +* #1115, #1116: Fixes incorrect ports in Example scripts (thanks @ArieHein!) +* #1117, #1118, #1119: Fixes markdown syntax in pages (thanks @ArieHein!) +* #1123: Adds reference link about using the SecretManagement module in automation scenarios +* #1133: Fixes broken links to functions (thanks @Chris--A!) +* #1141: Updates the IIS page to reference the use of Maximum Worker Processes, and Sessions being stored in-memory + +### Packaging +* #1169: Adds a `.vscode` workspace settings file, with PowerShell code formatting settings +* #1170: Bumps the Alpine version to 3.17, and Ubuntu to 22.04 in Dockerfiles +* #1171: Bumps the versions of MkDocs and the Material theme +``` + ## v2.8.0 +Date: 2nd February 2023 + ```plain ### Features * #980: Adds support for Secret Management, either via the SecretManagement module or using custom logic @@ -36,6 +79,8 @@ ## v2.7.2 +Date: 18th October 2022 + ```plain ### Enhancements * #1002: Adds a `-KeepCredential` switch for `Add-PodeAuthWindowsAd` (thanks @TheBakaBandit!) @@ -52,6 +97,8 @@ ## v2.7.1 +Date: 21st July 2022 + ```plain ### Bugs * #990: Fix SMTP attachment name parsing, when the name contains a space @@ -62,6 +109,8 @@ ## v2.7.0 +Date: 22nd June 2022 + ```plain ### Features * #895: Add support for server to be able to connect to external WebSockets @@ -91,6 +140,8 @@ ## v2.6.2 +Date: 2nd March 2022 + ```plain ### Bugs * #948: Hotfix to resolve issue with importing ActiveDirectory module into runspaces @@ -98,6 +149,8 @@ ## v2.6.1 +Date: 21st February 2022 + ```plain ### Bugs * #915: Fix regex issue preventing Pode listening on IPv6 addresses @@ -112,6 +165,8 @@ ## v2.6.0 +Date: 10th February 2022 + ```plain ### Features * #893: Add async/sync Task support @@ -138,6 +193,8 @@ ## v2.5.2 +Date: 4th January 2022 + ```plain ### Bugs * #892: Fixes a bug with importing modules, where the wrong file was being used @@ -145,6 +202,8 @@ ## v2.5.1 +Date: 21st December 2021 + ```plain ### Bugs * #877: Fix for `ConvertFrom-PodeJwt` expecting string not byte[] @@ -161,6 +220,8 @@ ## v2.5.0 +Date: 13th November 2021 + ```plain ### Enhancements * #771: Adds more `Use-PodeXYZ` functions for auto-loading scripts @@ -199,6 +260,8 @@ ## v2.4.2 +Date: 13th September 2021 + ```plain ### Bugs * #810: Fixes a Local/UTC datetime issue on Cookies, expiring sessions early @@ -214,6 +277,8 @@ ## v2.4.1 +Date: 9th August 2021 + ```plain ### Enhancements * #801: Add new `-SearchBase` parameter to `Add-PodeAuthWindowsAD` for OpenLDAP @@ -231,6 +296,8 @@ ## v2.4.0 +Date: 21st July 2021 + ```plain ### Features * #766: Add support for server Event Hooks, to run scripts on events like terminating the server @@ -255,6 +322,8 @@ ## v2.3.0 +Date: 1st June 2021 + ```plain ### Features * #723: Add support for logging to Windows Event Viewer @@ -277,6 +346,8 @@ ## v2.2.3 +Date: 10th April 2021 + ```plain ### Bugs * #736: Fix issue with v2.2.2 PowerShell Gallery packaging @@ -284,6 +355,8 @@ ## v2.2.2 +Date: 9th April 2021 + ```plain ### Enhancements * #727: Allow referencing an OpenAPI component schema from another component schema (thanks @glatzert!) @@ -295,6 +368,8 @@ ## v2.2.1 +Date: 27th March 2021 + ```plain ### Bugs * #716: Fix bug with `$TimerEvent` object within Timers @@ -304,6 +379,8 @@ ## v2.2.0 +Date: 21st March 2021 + ```plain ### Features * #682: Add support for the HTTP Range request header @@ -325,6 +402,8 @@ ## v2.1.1 +Date: 19th February 2021 + ```plain ### Enhancements * #693: Add OperationId OpenAPI support on routes (thanks @glatzert) @@ -341,6 +420,8 @@ ## v2.1.0 +Date: 3rd February 2021 + ```plain ### Enhancements * #655: Update the Socket Listener to handle larger request payloads, and fix receiving SSL requests @@ -368,6 +449,8 @@ ## v2.0.3 +Date: 21st December 2020 + ```plain ### Bugs * #641: Fix an issue with Invalid Request Lines being received when running via SSL and using a Proxy @@ -379,6 +462,8 @@ ## v2.0.2 +Date: 5th December 2020 + ```plain ### Bugs * #636: Fixes bug with OAuth2 RedirectUrl when behind IIS @@ -386,6 +471,8 @@ ## v2.0.1 +Date: 29th November 2020 + ```plain ### Bugs * #631: Parse username during Windows AD authentication @@ -394,6 +481,8 @@ ## v2.0.0 +Date: 14th September 2020 + ```plain ### Features * #472: Adds support for client certificate authentication @@ -425,6 +514,8 @@ ## v1.8.4 +Date: 16th October 2020 + ```plain ### Bugs * #615: Fixes a bug with Azure Functions V3, where the sys property has now been removed @@ -432,6 +523,8 @@ ## v1.8.3 +Date: 20th September 2020 + ```plain ### Enhancements * #602: Adds a new `Remove-PodeOAResponse` function to allow removing of default responses @@ -440,6 +533,8 @@ ## v1.8.2 +Date: 31st July 2020 + ```plain ### Bugs * #594: Add `Import-PodeSnapIn` to FunctionsToExport list @@ -447,6 +542,8 @@ ## v1.8.1 +Date: 26th June 2020 + ```plain ### Bugs * #578: Fixes OpenAPI functions with rogue "=" on returning a value @@ -455,6 +552,8 @@ ## v1.8.0 +Date: 24th May 2020 + ```plain ### Enhancements * #533: Support on states for inclusion/exlcusions when saving, and scopes on states @@ -481,6 +580,8 @@ ## v1.7.3 +Date: 10th May 2020 + ```plain ### Bugs * #554: Fixes an issue where HTML static files would be treated as dynamic files @@ -488,6 +589,8 @@ ## v1.7.2 +Date: 27th April 2020 + ```plain ### Bugs * #543: Fixes an internal issue that was causing errors in the SMTP server @@ -495,6 +598,8 @@ ## v1.7.1 +Date: 17th April 2020 + ```plain ### Bugs * #534: Fixes an issue with IIS Windows Authentication when using foreign trusted domains (thanks @RobinBeismann!) @@ -502,6 +607,8 @@ ## v1.7.0 +Date: 10th April 2020 + ```plain ### Features * #504: Support for GZip and Deflate compression on Requests @@ -525,6 +632,8 @@ ## v1.6.1 +Date: 7th March 2020 + ```plain ### Bugs * 495: Fix issue with parsing query strings when using the Pode server type @@ -536,6 +645,8 @@ ## v1.6.0 +Date: 3rd March 2020 + ```plain ### Features * #464: Request metrics for routes for the number of requests @@ -556,6 +667,8 @@ ## v1.5.0 +Date: 2nd February 2020 + ```plain ### Features * #218: Adds OpenAPI with Swagger and ReDoc support @@ -574,6 +687,8 @@ ## v1.4.0 +Date: 10th January 2020 + ```plain ### Enhancements * #447: Sessions can now be used via Headers for better CLI support @@ -589,6 +704,8 @@ ## v1.3.0 +Date: 27th December 2019 + ```plain ### Enhancements * #421: Adds a new `-FilePath` parameter to the `Add-PodeTimer` and `Add-PodeSchedule` functions @@ -606,6 +723,8 @@ ## v1.2.1 +Date: 2nd December 2019 + ```plain ### Enhancements * #415: New functions for invoking Timer and Schedules adhoc @@ -619,6 +738,8 @@ ## v1.2.0 +Date: 13th November 2019 + ```plain ### Features * #395: Built-in support for using Server-to-Client websockets @@ -640,6 +761,8 @@ ## v1.1.0 +Date: 28th September 2019 + ```plain ### Features * #376: *Experimental* support for cross-platform HTTPS! @@ -655,6 +778,8 @@ ## v1.0.1 +Date: 4th September 2019 + ```plain ### Bugs * #367: If a "server.psd1" file is not present, Logging will not work @@ -663,6 +788,8 @@ ## v1.0.0 +Date: 2nd September 2019 + ```plain ### Features * #228: Support for rendering Markdown as HTML (Fully supported in PowerShell 7+) @@ -702,6 +829,8 @@ ## v0.32.0 +Date: 28th June 2019 + ```plain ### Enhancements * #270: Support on `gui` to specify the width and height of the window @@ -717,6 +846,8 @@ ## v0.31.0 +Date: 11th June 2019 + ```plain ### Features * #264: Support for Azure Functions and AWS Lambda @@ -732,6 +863,8 @@ ## v0.30.0 +Date: 26th May 2019 + ```plain ### Enhancements * #245: Support for Windows AD group validation on the inbuilt 'windows-ad' authentication validator @@ -748,6 +881,8 @@ ## v0.29.0 +Date: 10th May 2019 + ```plain ### Enhancements * #216: Multi-content-type support on Error Pages @@ -765,6 +900,8 @@ ## v0.28.1 +Date: 16th April 2019 + ```plain ### Bugs * #226: Adds the "gui" function to export list @@ -772,6 +909,8 @@ ## v0.28.0 +Date: 13th April 2019 + ```plain ### Features * #210: New "cookie" function added, to support setting/getting cookies - including signing them @@ -790,6 +929,8 @@ ## v0.27.3 +Date: 4th April 2019 + ```plain ### Bugs * #217: Binding to hostname throws error @@ -797,6 +938,8 @@ ## v0.27.2 +Date: 27th March 2019 + ```plain ### Bugs * #212: Incorrect variable name used in html, csv, xml and json functions when referencing files @@ -804,6 +947,8 @@ ## v0.27.1 +Date: 16th March 2019 + ```plain ### Bugs * #199: Fix issues with relative paths when running server as a service @@ -812,6 +957,8 @@ ## v0.27.0 +Date: 14th March 2019 + ```plain ### Features * #185: Support for Server Restarts either Periodically or at specific Times, with support for cron expressions @@ -835,6 +982,8 @@ ## v0.26.0 +Date: 17th February 2019 + ```plain ### Features * #162: Basic support for local modules in "package.json" on "pode install" @@ -846,6 +995,8 @@ ## v0.25.0 +Date: 5th February 2019 + ```plain ### Features * #170: Support for Static Content Caching, with ability to include/exclude routes/extensions @@ -864,6 +1015,8 @@ ## v0.24.0 +Date: 18th January 2019 + ```plain ### Features * #125: Helper support function for uploading files from a web form @@ -879,6 +1032,8 @@ ## v0.23.0 +Date: 24th December 2018 + ```plain ### Features * #77: Ability to run a web server, and view it through a Desktop Application (Windows only) @@ -893,6 +1048,8 @@ ## v0.22.0 +Date: 7th December 2018 + ```plain ### Enhancements * #123: Ability to remove a `route` @@ -904,6 +1061,8 @@ ## v0.21.0 +Date: 2nd November 2018 + ```plain ### Enhancements * #110: Return a 401 for inaccessible files @@ -922,6 +1081,8 @@ ## v0.20.0 +Date: 20th October 2018 + ```plain ### Documentation * Extended documentation for third-party template engines @@ -940,6 +1101,8 @@ ## v0.19.1 +Date: 9th October 2018 + ```plain ### Documentation * #91: This release contains far better documentation for Pode: https://badgerati.github.io/Pode @@ -955,6 +1118,8 @@ ## v0.19.0 +Date: 14th September 2018 + ```plain ### Features * #84: Session cookie support, with in-mem/custom data storage @@ -966,6 +1131,8 @@ ## v0.18.0 +Date: 25th August 2018 + ```plain ### Features * #78: Middleware support for web servers, allowing custom logic and extension modules on web request/responses @@ -976,6 +1143,8 @@ ## v0.17.0 +Date: 19th August 2018 + ```plain ### Features * #43: Ability to generate self-signed certificates, and bind those certs - or pre-installed certs - when using HTTPS @@ -988,6 +1157,8 @@ ## v0.16.0 +Date: 8th August 2018 + ```plain ### Features * #66: Support for basic rate limiting of requests per x seconds from IPs @@ -1000,6 +1171,8 @@ ## v0.15.0 +Date: 13th July 2018 + ```plain ### Features @@ -1013,6 +1186,8 @@ ## v0.14.0 +Date: 6th July 2018 + ```plain ### Features * #21: Ability for Pode to Internally Restart when a File Change is Detected @@ -1028,6 +1203,8 @@ ## v0.13.0 +Date: 23rd June 2018 + ```plain ### Features * #40: Ability to add variables to a shared state, so you can re-use variables in timers, loggers, and routes @@ -1035,6 +1212,8 @@ ## v0.12.0 +Date: 15th June 2018 + ```plain ### Features * #33: Support for logging to the terminal, files, and custom loggers for LogStash/Fluentd/etc @@ -1047,6 +1226,8 @@ ## v0.11.3 +Date: 10th June 2018 + ```plain ### Bugs and Enhancements * #22: Proper fix for high CPU usage, by using `Task.Wait` with `CancellationTokens`; A Runspace is setup to monitor for key presses, and on `Ctrl+C` will `Cancel()` the token and terminate Pode @@ -1054,6 +1235,8 @@ ## v0.11.2 +Date: 8th June 2018 + ```plain ### Bugs * #22: Hot fix patch for reducing high CPU usage when idle @@ -1061,6 +1244,8 @@ ## v0.11.1 +Date: 1st June 2018 + ```plain ### Bugs * #16: Status and Include functions were missing from module export list @@ -1068,6 +1253,8 @@ ## v0.11.0 +Date: 30th May 2018 + ```plain ### Features * #5: Async timers to run tasks and processes in a separate thread (see timers sections in README) diff --git a/docs/roadmap.md b/docs/roadmap.md index 0a37bcfef..426035f3e 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -29,17 +29,17 @@ Sometimes there could be more, if patch releases are needed. But sometimes there - [ ] gRPC support - [ ] HTTP/2.0 support - [ ] HTTP/3.0 support -- [ ] Inbuilt authorization support, on top the current authentications support [#992](https://github.com/Badgerati/Pode/issues/992) +- [x] Inbuilt authorization support, on top the current authentications support [#992](https://github.com/Badgerati/Pode/issues/992) - [x] Secret management support [#980](https://github.com/Badgerati/Pode/issues/980) -- [ ] Some way of being able to merge authentication types [588](https://github.com/Badgerati/Pode/issues/588) +- [x] Some way of being able to merge authentication types [588](https://github.com/Badgerati/Pode/issues/588) - [ ] Improved garbage collection in runspaces, to help free up memory - [ ] A Session Pool that can be used to port/re-use PSSessions in Pode more easily - [ ] Further improvements to OIDC, such as HMAC and refresh token support - [ ] Implement an inbuilt FTP(S) server - [ ] Is it possible to implement an inbuilt SFTP server? - [ ] Inbuilt connectors for connecting to message brokers, like Kafka, RabbitMQ, etc. -- [ ] Would is be possible to create an inbuilt pub/sub server? -- [x] An inbuilt FIM server, so we can fun logic on FIM events +- [ ] Would it be possible to create an inbuilt pub/sub server? +- [x] An inbuilt FIM server, so we can run logic on FIM events ### Misc diff --git a/examples/mail-server.ps1 b/examples/mail-server.ps1 index 336ac6f03..f08e52b3f 100644 --- a/examples/mail-server.ps1 +++ b/examples/mail-server.ps1 @@ -42,6 +42,7 @@ Start-PodeServer -Threads 2 { Write-Host '|' $SmtpEvent.Email | Out-Default $SmtpEvent.Request | out-default + $SmtpEvent.Email.Headers | out-default Write-Host '- - - - - - - - - - - - - - - - - -' } diff --git a/examples/public/styles/simple.css b/examples/public/styles/simple.css index 7cf2d40c0..22ff031a3 100644 --- a/examples/public/styles/simple.css +++ b/examples/public/styles/simple.css @@ -1,3 +1,11 @@ body { background-color: rebeccapurple; +} + +a { + color: white; +} + +a:visited { + color: yellow; } \ No newline at end of file diff --git a/examples/rest-api.ps1 b/examples/rest-api.ps1 index 9599c37bc..1f0793544 100644 --- a/examples/rest-api.ps1 +++ b/examples/rest-api.ps1 @@ -9,6 +9,9 @@ Start-PodeServer { Add-PodeEndpoint -Address * -Port 8086 -Protocol Http + # request logging + New-PodeLoggingMethod -Terminal -Batch 10 -BatchTimeout 10 | Enable-PodeRequestLogging + # can be hit by sending a GET request to "localhost:8086/api/test" Add-PodeRoute -Method Get -Path '/api/test' -ScriptBlock { Write-PodeJsonResponse -Value @{ 'hello' = 'world'; } diff --git a/examples/schedules-routes.ps1 b/examples/schedules-routes.ps1 index 1fecdeb53..cfe3ef0fa 100644 --- a/examples/schedules-routes.ps1 +++ b/examples/schedules-routes.ps1 @@ -7,7 +7,7 @@ Import-Module "$($path)/src/Pode.psm1" -Force -ErrorAction Stop # create a server, and start listening on port 8085 Start-PodeServer -EnablePool Schedules { - Add-PodeEndpoint -Address * -Port 8081 -Protocol Http + Add-PodeEndpoint -Address * -Port 8085 -Protocol Http # create a new schdule via a route Add-PodeRoute -Method Get -Path '/api/schedule' -ScriptBlock { diff --git a/examples/tcp-server-auth.ps1 b/examples/tcp-server-auth.ps1 new file mode 100644 index 000000000..daea3c39e --- /dev/null +++ b/examples/tcp-server-auth.ps1 @@ -0,0 +1,37 @@ +$path = Split-Path -Parent -Path (Split-Path -Parent -Path $MyInvocation.MyCommand.Path) +Import-Module "$($path)/src/Pode.psm1" -Force -ErrorAction Stop + +# or just: +# Import-Module Pode + +# create a server, and start listening on port 9000 +Start-PodeServer -Threads 2 { + + # add endpoint + Add-PodeEndpoint -Address * -Port 9000 -Protocol Tcp -CRLFMessageEnd + + # enable logging + New-PodeLoggingMethod -Terminal | Enable-PodeErrorLogging + + # create a role access method get retrieves roles from a database + Add-PodeAccess -Name 'RoleExample' -Type Role -ScriptBlock { + param($username) + if ($username -ieq 'morty') { + return @('Developer') + } + + return 'QA' + } + + # setup a Verb that only allows Developers + Add-PodeVerb -Verb 'EXAMPLE :username' -ScriptBlock { + if (!(Test-PodeAccess -Name 'RoleExample' -Destination 'Developer' -ArgumentList $TcpEvent.Parameters.username)) { + Write-PodeTcpClient -Message "Forbidden Access" + 'Forbidden!' | Out-Default + return + } + + Write-PodeTcpClient -Message "Hello, there!" + 'Hello!' | Out-Default + } +} \ No newline at end of file diff --git a/examples/tcp-server.ps1 b/examples/tcp-server.ps1 index 388f301d5..c28677b46 100644 --- a/examples/tcp-server.ps1 +++ b/examples/tcp-server.ps1 @@ -17,6 +17,7 @@ Start-PodeServer -Threads 2 { Add-PodeVerb -Verb 'HELLO' -ScriptBlock { Write-PodeTcpClient -Message "HI" + 'here' | Out-Default } Add-PodeVerb -Verb 'HELLO2 :username' -ScriptBlock { diff --git a/examples/views/auth-about.pode b/examples/views/auth-about.pode new file mode 100644 index 000000000..1df74729c --- /dev/null +++ b/examples/views/auth-about.pode @@ -0,0 +1,23 @@ + + + Auth About + + + + + Hello, this is an about page! +
+ +

+ Home + Register +

+ + +
+ +
+
+ + + \ No newline at end of file diff --git a/examples/views/auth-home.pode b/examples/views/auth-home.pode index 34c3c01e1..39af1f672 100644 --- a/examples/views/auth-home.pode +++ b/examples/views/auth-home.pode @@ -10,6 +10,11 @@ Your session will expire on $($data.Expiry)
+

+ About + Register +

+
diff --git a/examples/views/auth-register.pode b/examples/views/auth-register.pode new file mode 100644 index 000000000..d13719c68 --- /dev/null +++ b/examples/views/auth-register.pode @@ -0,0 +1,23 @@ + + + Auth Register + + + + + Hello, this is a register page! +
+ +

+ Home + About +

+ + +
+ +
+ + + + \ No newline at end of file diff --git a/examples/web-auth-apikey.ps1 b/examples/web-auth-apikey.ps1 index c75001b85..580b6c4cc 100644 --- a/examples/web-auth-apikey.ps1 +++ b/examples/web-auth-apikey.ps1 @@ -11,6 +11,8 @@ Import-Module "$($path)/src/Pode.psm1" -Force -ErrorAction Stop # or just: # Import-Module Pode +# Invoke-RestMethod -Method Get -Uri 'http://localhost:8085/users' -Headers @{ 'X-API-KEY' = 'test-api-key' } + # create a server, and start listening on port 8085 Start-PodeServer -Threads 2 { diff --git a/examples/web-auth-basic-access.ps1 b/examples/web-auth-basic-access.ps1 new file mode 100644 index 000000000..ca6ce591f --- /dev/null +++ b/examples/web-auth-basic-access.ps1 @@ -0,0 +1,145 @@ +$path = Split-Path -Parent -Path (Split-Path -Parent -Path $MyInvocation.MyCommand.Path) +Import-Module "$($path)/src/Pode.psm1" -Force -ErrorAction Stop + +# or just: +# Import-Module Pode + +<# +This example shows how to use sessionless authentication, which will mostly be for +REST APIs. The example used here is Basic authentication. + +Calling the '[POST] http://localhost:8085/users-all' endpoint, with an Authorization +header of 'Basic bW9ydHk6cGlja2xl' will display the uesrs. Anything else and +you'll get a 401 status code back. + +Success: +Invoke-RestMethod -Uri http://localhost:8085/users-all -Method Post -Headers @{ Authorization = 'Basic bW9ydHk6cGlja2xl' } + +Failure: +Invoke-RestMethod -Uri http://localhost:8085/users-all -Method Post -Headers @{ Authorization = 'Basic bW9ydHk6cmljaw==' } +#> + +# create a server, and start listening on port 8085 +Start-PodeServer -Threads 2 { + + # listen on localhost:8085 + Add-PodeEndpoint -Address * -Port 8085 -Protocol Http + + # setup RBAC + # Add-PodeAccess -Type Role -Name 'TestRbac' + # Add-PodeAccess -Type Group -Name 'TestGbac' + New-PodeAccessScheme -Type Role | Add-PodeAccess -Name 'TestRbac' + New-PodeAccessScheme -Type Group | Add-PodeAccess -Name 'TestGbac' + # Add-PodeAccess -Type Custom -Name 'TestRbac' -Path 'CustomAccess' -Validator { + # param($userRoles, $customValues) + # return $userRoles.Example -iin $customValues.Example + # } + + Merge-PodeAccess -Name 'TestMergedAll' -Access 'TestRbac', 'TestGbac' -Valid All + Merge-PodeAccess -Name 'TestMergedOne' -Access 'TestRbac', 'TestGbac' -Valid One + + # setup basic auth (base64> username:password in header) + New-PodeAuthScheme -Basic -Realm 'Pode Example Page' | Add-PodeAuth -Name 'Validate' -Sessionless -ScriptBlock { + param($username, $password) + + # here you'd check a real user storage, this is just for example + if ($username -eq 'morty' -and $password -eq 'pickle') { + return @{ + User = @{ + ID ='M0R7Y302' + Name = 'Morty' + Type = 'Human' + Username = 'm.orty' + Roles = @('Developer') + Groups = @('Software', 'Admins') + CustomAccess = @{ Example = 'test-val-1' } + } + } + } + + return @{ Message = 'Invalid details supplied' } + } + + # Endware to output user auth state + Add-PodeEndware -ScriptBlock { + $WebEvent.Auth | Out-Default + } + + # POST request to get list of users - there's no Access, so any auth'd user can access + Add-PodeRoute -Method Post -Path '/users-all' -Authentication 'Validate' -ScriptBlock { + Write-PodeJsonResponse -Value @{ + Users = @( + @{ + Name = 'Deep Thought' + } + ) + } + } + + # POST request to get list of users - only Developer roles can access + Add-PodeRoute -Method Post -Path '/users-dev' -Authentication 'Validate' -Access 'TestRbac' -Role Developer -ScriptBlock { + Write-PodeJsonResponse -Value @{ + Users = @( + @{ + Name = 'Leeroy Jenkins' + } + ) + } + } + + # POST request to get list of users - only QA roles can access + Add-PodeRoute -Method Post -Path '/users-qa' -Authentication 'Validate' -Access 'TestRbac' -Role QA -ScriptBlock { + Write-PodeJsonResponse -Value @{ + Users = @( + @{ + Name = 'Nikola Tesla' + } + ) + } + } + + # POST request to get list of users - only users in the SOftware group can access + Add-PodeRoute -Method Post -Path '/users-soft' -Authentication 'Validate' -Access 'TestGbac' -Group Software -ScriptBlock { + Write-PodeJsonResponse -Value @{ + Users = @( + @{ + Name = 'Smooth McGroove' + } + ) + } + } + + # POST request to get list of users - only Developer role in the Admins group can access + Add-PodeRoute -Method Post -Path '/users-dev-admin' -Authentication 'Validate' -Access 'TestMergedAll' -Role Developer -Group Admins -ScriptBlock { + Write-PodeJsonResponse -Value @{ + Users = @( + @{ + Name = 'Arthur Dent' + } + ) + } + } + + # POST request to get list of users - either DevOps role or Admins group can access + Add-PodeRoute -Method Post -Path '/users-devop-admin' -Authentication 'Validate' -Access 'TestMergedOne' -Role DevOps -Group Admins -ScriptBlock { + Write-PodeJsonResponse -Value @{ + Users = @( + @{ + Name = 'Monkey D. Luffy' + } + ) + } + } + + # POST request to get list of users - either QA role or Support group can access + Add-PodeRoute -Method Post -Path '/users-qa-support' -Authentication 'Validate' -Access 'TestMergedOne' -Role QA -Group Support -ScriptBlock { + Write-PodeJsonResponse -Value @{ + Users = @( + @{ + Name = 'Donald Duck' + } + ) + } + } + +} \ No newline at end of file diff --git a/examples/web-auth-basic-adhoc.ps1 b/examples/web-auth-basic-adhoc.ps1 new file mode 100644 index 000000000..ebcb9bb7e --- /dev/null +++ b/examples/web-auth-basic-adhoc.ps1 @@ -0,0 +1,67 @@ +$path = Split-Path -Parent -Path (Split-Path -Parent -Path $MyInvocation.MyCommand.Path) +Import-Module "$($path)/src/Pode.psm1" -Force -ErrorAction Stop + +# or just: +# Import-Module Pode + +<# +This example shows how to use sessionless authentication, which will mostly be for +REST APIs. The example used here is adhoc Basic authentication. + +Calling the '[POST] http://localhost:8085/users' endpoint, with an Authorization +header of 'Basic bW9ydHk6cGlja2xl' will display the users. Anything else and +you'll get a 401 status code back. + +Success: +Invoke-RestMethod -Uri http://localhost:8085/users -Method Post -Headers @{ Authorization = 'Basic bW9ydHk6cGlja2xl' } + +Failure: +Invoke-RestMethod -Uri http://localhost:8085/users -Method Post -Headers @{ Authorization = 'Basic bW9ydHk6cmljaw==' } +#> + +# create a server, and start listening on port 8085 +Start-PodeServer -Threads 2 { + + # listen on localhost:8085 + Add-PodeEndpoint -Address * -Port 8085 -Protocol Http + + # setup basic auth (base64> username:password in header) + New-PodeAuthScheme -Basic -Realm 'Pode Example Page' | Add-PodeAuth -Name 'Validate' -Sessionless -ScriptBlock { + param($username, $password) + + # here you'd check a real user storage, this is just for example + if ($username -eq 'morty' -and $password -eq 'pickle') { + return @{ + User = @{ + ID ='M0R7Y302' + Name = 'Morty' + Type = 'Human' + } + } + } + + return @{ Message = 'Invalid details supplied' } + } + + # POST request to get list of users (authentication is done adhoc, and not directly using -Authentication on the Route) + Add-PodeRoute -Method Post -Path '/users' -ScriptBlock { + if (!(Test-PodeAuth -Name Validate)) { + Set-PodeResponseStatus -Code 401 + return + } + + Write-PodeJsonResponse -Value @{ + User = @( + @{ + Name = 'Deep Thought' + Age = 42 + }, + @{ + Name = 'Leeroy Jenkins' + Age = 1337 + } + ) + } + } + +} \ No newline at end of file diff --git a/examples/web-auth-basic-anon.ps1 b/examples/web-auth-basic-anon.ps1 index c3133b26e..024e50d3b 100644 --- a/examples/web-auth-basic-anon.ps1 +++ b/examples/web-auth-basic-anon.ps1 @@ -44,7 +44,7 @@ Start-PodeServer -Threads 2 { } # GET request to get list of users (since there's no session, authentication will always happen, but, we're allowing anon access) - Add-PodeRoute -Method Get -Path '/users' -Authentication 'Validate' -Anon -ScriptBlock { + Add-PodeRoute -Method Get -Path '/users' -Authentication 'Validate' -AllowAnon -ScriptBlock { if (Test-PodeAuthUser) { Write-PodeJsonResponse -Value @{ Users = @( diff --git a/examples/web-auth-basic.ps1 b/examples/web-auth-basic.ps1 index df739269d..fed990bee 100644 --- a/examples/web-auth-basic.ps1 +++ b/examples/web-auth-basic.ps1 @@ -25,6 +25,9 @@ Start-PodeServer -Threads 2 { # listen on localhost:8085 Add-PodeEndpoint -Address * -Port 8085 -Protocol Http + # request logging + New-PodeLoggingMethod -Terminal -Batch 10 -BatchTimeout 10 | Enable-PodeRequestLogging + # setup basic auth (base64> username:password in header) New-PodeAuthScheme -Basic -Realm 'Pode Example Page' | Add-PodeAuth -Name 'Validate' -Sessionless -ScriptBlock { param($username, $password) @@ -33,6 +36,7 @@ Start-PodeServer -Threads 2 { if ($username -eq 'morty' -and $password -eq 'pickle') { return @{ User = @{ + Username = 'morty' ID ='M0R7Y302' Name = 'Morty' Type = 'Human' @@ -43,35 +47,10 @@ Start-PodeServer -Threads 2 { return @{ Message = 'Invalid details supplied' } } - # POST request to get list of users (since there's no session, authentication will always happen) + # POST request to get current user (since there's no session, authentication will always happen) Add-PodeRoute -Method Post -Path '/users' -Authentication 'Validate' -ScriptBlock { Write-PodeJsonResponse -Value @{ - Users = @( - @{ - Name = 'Deep Thought' - Age = 42 - }, - @{ - Name = 'Leeroy Jenkins' - Age = 1337 - } - ) - } - } - - # GET request to get list of users (since there's no session, authentication will always happen) - Add-PodeRoute -Method Get -Path '/users' -Authentication 'Validate' -ScriptBlock { - Write-PodeJsonResponse -Value @{ - Users = @( - @{ - Name = 'Deep Thought' - Age = 42 - }, - @{ - Name = 'Leeroy Jenkins' - Age = 1337 - } - ) + User = (Get-PodeAuthUser) } } diff --git a/examples/web-auth-bearer.ps1 b/examples/web-auth-bearer.ps1 index e32187bd4..3e8ca4e74 100644 --- a/examples/web-auth-bearer.ps1 +++ b/examples/web-auth-bearer.ps1 @@ -4,6 +4,8 @@ Import-Module "$($path)/src/Pode.psm1" -Force -ErrorAction Stop # or just: # Import-Module Pode +# Invoke-RestMethod -Method Get -Uri 'http://localhost:8085/users' -Headers @{ Authorization = 'Bearer test-token' } + # create a server, and start listening on port 8085 Start-PodeServer -Threads 2 { diff --git a/examples/web-auth-form-access.ps1 b/examples/web-auth-form-access.ps1 new file mode 100644 index 000000000..a7551dd30 --- /dev/null +++ b/examples/web-auth-form-access.ps1 @@ -0,0 +1,103 @@ +$path = Split-Path -Parent -Path (Split-Path -Parent -Path $MyInvocation.MyCommand.Path) +Import-Module "$($path)/src/Pode.psm1" -Force -ErrorAction Stop + +# or just: +# Import-Module Pode + +<# +This examples shows how to use session persistant authentication with access. +The example used here is Form authentication and RBAC access on pages, sent from the
in HTML. + +Navigating to the 'http://localhost:8085' endpoint in your browser will auto-rediect you to the '/login' +page. Here, you can type the username (morty) and the password (pickle); clicking 'Login' will take you +back to the home page with a greeting and a view counter. Clicking 'Logout' will purge the session and +take you back to the login page. + +- The Home and Login pages are accessible by all. +- The About page is only accessible by Developers (for morty it will load) +- The Register page is only accessible by QAs (for morty this will 403) +#> + +# create a server, and start listening on port 8085 +Start-PodeServer -Threads 2 { + + # listen on localhost:8085 + Add-PodeEndpoint -Address * -Port 8085 -Protocol Http + + # set the view engine + Set-PodeViewEngine -Type Pode + + # enable error logging + New-PodeLoggingMethod -Terminal | Enable-PodeErrorLogging + + # setup session details + Enable-PodeSessionMiddleware -Duration 120 -Extend + + # setup form auth ( in HTML) + New-PodeAuthScheme -Form | Add-PodeAuth -Name 'Login' -FailureUrl '/login' -SuccessUrl '/' -ScriptBlock { + param($username, $password) + + # here you'd check a real user storage, this is just for example + if ($username -eq 'morty' -and $password -eq 'pickle') { + return @{ + User = @{ + Name = 'Morty' + Roles = @('Developer') + } + } + } + + return @{ Message = 'Invalid details supplied' } + } + + # set RBAC access + New-PodeAccessScheme -Type Role | Add-PodeAccess -Name 'Rbac' -Match One + + + # home page: + # redirects to login page if not authenticated + Add-PodeRoute -Method Get -Path '/' -Authentication Login -ScriptBlock { + $session:Views++ + + Write-PodeViewResponse -Path 'auth-home' -Data @{ + Username = $WebEvent.Auth.User.Name + Views = $session:Views + Expiry = Get-PodeSessionExpiry + } + } + + + # about page: + # only Developers can access this page + Add-PodeRoute -Method Get -Path '/about' -Authentication Login -Access Rbac -Role Developer -ScriptBlock { + Write-PodeViewResponse -Path 'auth-about' + } + + + # register page: + # only QAs can access this page + Add-PodeRoute -Method Get -Path '/register' -Authentication Login -Access Rbac -Role QA -ScriptBlock { + Write-PodeViewResponse -Path 'auth-register' + } + + + # login page: + # the login flag set below checks if there is already an authenticated session cookie. If there is, then + # the user is redirected to the home page. If there is no session then the login page will load without + # checking user authetication (to prevent a 401 status) + Add-PodeRoute -Method Get -Path '/login' -Authentication Login -Login -ScriptBlock { + Write-PodeViewResponse -Path 'auth-login' -FlashMessages + } + + + # login check: + # this is the endpoint the 's action will invoke. If the user validates then they are set against + # the session as authenticated, and redirect to the home page. If they fail, then the login page reloads + Add-PodeRoute -Method Post -Path '/login' -Authentication Login -Login + + + # logout check: + # when the logout button is click, this endpoint is invoked. The logout flag set below informs this call + # to purge the currently authenticated session, and then redirect back to the login page + Add-PodeRoute -Method Post -Path '/logout' -Authentication Login -Logout +} \ No newline at end of file diff --git a/examples/web-auth-form-anon.ps1 b/examples/web-auth-form-anon.ps1 index ab86a6a54..18d25c151 100644 --- a/examples/web-auth-form-anon.ps1 +++ b/examples/web-auth-form-anon.ps1 @@ -50,7 +50,7 @@ Start-PodeServer -Threads 2 { # home page: # redirects to login page if not authenticated - Add-PodeRoute -Method Get -Path '/' -Authentication Login -Anon -ScriptBlock { + Add-PodeRoute -Method Get -Path '/' -Authentication Login -AllowAnon -ScriptBlock { if (Test-PodeAuthUser) { $session:Views++ diff --git a/examples/web-auth-form-merged.ps1 b/examples/web-auth-form-merged.ps1 new file mode 100644 index 000000000..9f180fbcf --- /dev/null +++ b/examples/web-auth-form-merged.ps1 @@ -0,0 +1,102 @@ +$path = Split-Path -Parent -Path (Split-Path -Parent -Path $MyInvocation.MyCommand.Path) +Import-Module "$($path)/src/Pode.psm1" -Force -ErrorAction Stop + +# or just: +# Import-Module Pode + +<# +This examples shows how to use session persistant authentication, for things like logins on websites. +The example used here is Form authentication, sent from the in HTML. + +Navigating to the 'http://localhost:8085' endpoint in your browser will auto-rediect you to the '/login' +page. Here, you can type the username (morty) and the password (pickle); clicking 'Login' will take you +back to the home page with a greeting and a view counter. Clicking 'Logout' will purge the session and +take you back to the login page. +#> + +# create a server, and start listening on port 8085 +Start-PodeServer -Threads 2 { + + # listen on localhost:8085 + Add-PodeEndpoint -Address * -Port 8085 -Protocol Http + + # set the view engine + Set-PodeViewEngine -Type Pode + + # enable error logging + New-PodeLoggingMethod -Terminal | Enable-PodeErrorLogging + + # setup session details + Enable-PodeSessionMiddleware -Duration 120 -Extend + + # setup form auth ( in HTML) + New-PodeAuthScheme -Form | Add-PodeAuth -Name 'Login1' -FailureUrl '/login' -SuccessUrl '/' -ScriptBlock { + param($username, $password) + + # here you'd check a real user storage, this is just for example + if ($username -eq 'morty' -and $password -eq 'pickle') { + return @{ + User = @{ + ID ='M0R7Y302' + Name = 'Morty' + Type = 'Human' + } + } + } + + return @{ Message = 'Invalid details supplied' } + } + + New-PodeAuthScheme -Form | Add-PodeAuth -Name 'Login2' -FailureUrl '/login' -SuccessUrl '/' -ScriptBlock { + param($username, $password) + + # here you'd check a real user storage, this is just for example + if ($username -eq 'morty' -and $password -eq 'bob') { + return @{ + User = @{ + ID ='M0R7Y302' + Name = 'Morty' + Type = 'Human' + } + } + } + + return @{ Message = 'Invalid details supplied' } + } + + Merge-PodeAuth -Name 'Login' -Authentication Login1, Login2 + + + # home page: + # redirects to login page if not authenticated + Add-PodeRoute -Method Get -Path '/' -Authentication Login -ScriptBlock { + $session:Views++ + + Write-PodeViewResponse -Path 'auth-home' -Data @{ + Username = $WebEvent.Auth.User.Name + Views = $session:Views + Expiry = Get-PodeSessionExpiry + } + } + + + # login page: + # the login flag set below checks if there is already an authenticated session cookie. If there is, then + # the user is redirected to the home page. If there is no session then the login page will load without + # checking user authetication (to prevent a 401 status) + Add-PodeRoute -Method Get -Path '/login' -Authentication Login -Login -ScriptBlock { + Write-PodeViewResponse -Path 'auth-login' -FlashMessages + } + + + # login check: + # this is the endpoint the 's action will invoke. If the user validates then they are set against + # the session as authenticated, and redirect to the home page. If they fail, then the login page reloads + Add-PodeRoute -Method Post -Path '/login' -Authentication Login -Login + + + # logout check: + # when the logout button is click, this endpoint is invoked. The logout flag set below informs this call + # to purge the currently authenticated session, and then redirect back to the login page + Add-PodeRoute -Method Post -Path '/logout' -Authentication Login -Logout +} \ No newline at end of file diff --git a/examples/web-auth-form-session-auth.ps1 b/examples/web-auth-form-session-auth.ps1 new file mode 100644 index 000000000..aee830686 --- /dev/null +++ b/examples/web-auth-form-session-auth.ps1 @@ -0,0 +1,87 @@ +$path = Split-Path -Parent -Path (Split-Path -Parent -Path $MyInvocation.MyCommand.Path) +Import-Module "$($path)/src/Pode.psm1" -Force -ErrorAction Stop + +# or just: +# Import-Module Pode + +<# +This examples shows how to use session persistant authentication, for things like logins on websites. +The example used here is Form authentication, sent from the in HTML. But also used is Session Authentication +on the main home page route and Form Auth on Login. + +Navigating to the 'http://localhost:8085' endpoint in your browser will auto-rediect you to the '/login' +page. Here, you can type the username (morty) and the password (pickle); clicking 'Login' will take you +back to the home page with a greeting and a view counter. Clicking 'Logout' will purge the session and +take you back to the login page. +#> + +# create a server, and start listening on port 8085 +Start-PodeServer -Threads 2 { + + # listen on localhost:8085 + Add-PodeEndpoint -Address * -Port 8085 -Protocol Http + + # set the view engine + Set-PodeViewEngine -Type Pode + + # enable error logging + New-PodeLoggingMethod -Terminal | Enable-PodeErrorLogging + + # setup session details + Enable-PodeSessionMiddleware -Duration 120 -Extend + + # setup form auth ( in HTML) + New-PodeAuthScheme -Form | Add-PodeAuth -Name 'Login' -FailureUrl '/login' -SuccessUrl '/' -ScriptBlock { + param($username, $password) + + # here you'd check a real user storage, this is just for example + if ($username -eq 'morty' -and $password -eq 'pickle') { + return @{ + User = @{ + ID ='M0R7Y302' + Name = 'Morty' + Type = 'Human' + } + } + } + + return @{ Message = 'Invalid details supplied' } + } + + # setup session auth + Add-PodeAuthSession -Name 'SessionAuth' -FailureUrl '/login' + + + # home page: + # redirects to login page if not authenticated + Add-PodeRoute -Method Get -Path '/' -Authentication SessionAuth -ScriptBlock { + $session:Views++ + + Write-PodeViewResponse -Path 'auth-home' -Data @{ + Username = $WebEvent.Auth.User.Name + Views = $session:Views + Expiry = Get-PodeSessionExpiry + } + } + + + # login page: + # the login flag set below checks if there is already an authenticated session cookie. If there is, then + # the user is redirected to the home page. If there is no session then the login page will load without + # checking user authetication (to prevent a 401 status) + Add-PodeRoute -Method Get -Path '/login' -Authentication Login -Login -ScriptBlock { + Write-PodeViewResponse -Path 'auth-login' -FlashMessages + } + + + # login check: + # this is the endpoint the 's action will invoke. If the user validates then they are set against + # the session as authenticated, and redirect to the home page. If they fail, then the login page reloads + Add-PodeRoute -Method Post -Path '/login' -Authentication Login -Login + + + # logout check: + # when the logout button is click, this endpoint is invoked. The logout flag set below informs this call + # to purge the currently authenticated session, and then redirect back to the login page + Add-PodeRoute -Method Post -Path '/logout' -Authentication SessionAuth -Logout +} \ No newline at end of file diff --git a/examples/web-auth-merged.ps1 b/examples/web-auth-merged.ps1 new file mode 100644 index 000000000..7dd84c36a --- /dev/null +++ b/examples/web-auth-merged.ps1 @@ -0,0 +1,80 @@ +$path = Split-Path -Parent -Path (Split-Path -Parent -Path $MyInvocation.MyCommand.Path) +Import-Module "$($path)/src/Pode.psm1" -Force -ErrorAction Stop + +# or just: +# Import-Module Pode + +# Success +# Invoke-RestMethod -Method Get -Uri 'http://localhost:8085/users' -Headers @{ 'X-API-KEY' = 'test-api-key'; Authorization = 'Basic bW9ydHk6cGlja2xl' } + +# Failure +# Invoke-RestMethod -Method Get -Uri 'http://localhost:8085/users' -Headers @{ 'X-API-KEY' = 'test-api-key'; Authorization = 'Basic bW9ydHk6cmljaw==' } + +# create a server, and start listening on port 8085 +Start-PodeServer -Threads 2 { + + # listen on localhost:8085 + Add-PodeEndpoint -Address * -Port 8085 -Protocol Http + New-PodeLoggingMethod -Terminal | Enable-PodeErrorLogging + + # request logging + New-PodeLoggingMethod -Terminal -Batch 10 -BatchTimeout 10 | Enable-PodeRequestLogging + + # setup access + Add-PodeAccess -Type Role -Name 'Rbac' + Add-PodeAccess -Type Group -Name 'Gbac' + + # setup a merged access + Merge-PodeAccess -Name 'MergedAccess' -Access 'Rbac', 'Gbac' -Valid All + + # setup apikey auth + New-PodeAuthScheme -ApiKey -Location Header | Add-PodeAuth -Name 'ApiKey' -Access 'Gbac' -Sessionless -ScriptBlock { + param($key) + + # here you'd check a real user storage, this is just for example + if ($key -ieq 'test-api-key') { + return @{ + User = @{ + ID ='M0R7Y302' + Name = 'Morty' + Type = 'Human' + Groups = @('Software') + } + } + } + + return $null + } + + # setup basic auth (base64> username:password in header) + New-PodeAuthScheme -Basic | Add-PodeAuth -Name 'Basic' -Access 'MergedAccess' -Sessionless -ScriptBlock { + param($username, $password) + + # here you'd check a real user storage, this is just for example + if ($username -eq 'morty' -and $password -eq 'pickle') { + return @{ + User = @{ + Username = 'morty' + ID ='M0R7Y302' + Name = 'Morty' + Type = 'Human' + Roles = @('Developer') + Groups = @('Software') + } + } + } + + return @{ Message = 'Invalid details supplied' } + } + + # merge the auths together + Merge-PodeAuth -Name 'MergedAuth' -Authentication 'ApiKey', 'Basic' -Valid All + + # GET request to get list of users (since there's no session, authentication will always happen) + Add-PodeRoute -Method Get -Path '/users' -Authentication 'MergedAuth' -Role Developer -Group Software -ScriptBlock { + Write-PodeJsonResponse -Value @{ + Users = $WebEvent.Auth.User + } + } + +} \ No newline at end of file diff --git a/examples/web-csrf.ps1 b/examples/web-csrf.ps1 index 85561a927..e6c8b78f9 100644 --- a/examples/web-csrf.ps1 +++ b/examples/web-csrf.ps1 @@ -1,4 +1,4 @@ -param ( +param( [Parameter()] [ValidateSet('Cookie', 'Session')] [string] diff --git a/examples/web-funcs-to-routes.ps1 b/examples/web-funcs-to-routes.ps1 index 8816fb690..dc21f4784 100644 --- a/examples/web-funcs-to-routes.ps1 +++ b/examples/web-funcs-to-routes.ps1 @@ -22,7 +22,7 @@ Start-PodeServer -Threads 2 { return @{ Message = 'Invalid details supplied' } } - # listen on localhost:8085 + # listen on localhost:8090 Add-PodeEndpoint -Address localhost -Port 8090 -Protocol Http New-PodeLoggingMethod -Terminal | Enable-PodeErrorLogging @@ -32,4 +32,4 @@ Start-PodeServer -Threads 2 { # make routes for every exported command in Pester # ConvertTo-PodeRoute -Module Pester -Verbose -} \ No newline at end of file +} diff --git a/examples/web-hostname-kestrel.ps1 b/examples/web-hostname-kestrel.ps1 index e872a1348..0003a355d 100644 --- a/examples/web-hostname-kestrel.ps1 +++ b/examples/web-hostname-kestrel.ps1 @@ -1,4 +1,4 @@ -param ( +param( [int] $Port = 8085 ) diff --git a/examples/web-hostname.ps1 b/examples/web-hostname.ps1 index 64f953c5c..920dfd2d5 100644 --- a/examples/web-hostname.ps1 +++ b/examples/web-hostname.ps1 @@ -1,4 +1,4 @@ -param ( +param( [int] $Port = 8085 ) diff --git a/examples/web-imports.ps1 b/examples/web-imports.ps1 index 4fe9c0cf8..e6017a3cc 100644 --- a/examples/web-imports.ps1 +++ b/examples/web-imports.ps1 @@ -1,4 +1,4 @@ -param ( +param( [int] $Port = 8085 ) diff --git a/examples/web-pages-kestrel.ps1 b/examples/web-pages-kestrel.ps1 index 632f1e5c1..aa73f7801 100644 --- a/examples/web-pages-kestrel.ps1 +++ b/examples/web-pages-kestrel.ps1 @@ -1,4 +1,4 @@ -param ( +param( [int] $Port = 8085 ) diff --git a/examples/web-pages-simple.ps1 b/examples/web-pages-simple.ps1 index e7f846c01..893812910 100644 --- a/examples/web-pages-simple.ps1 +++ b/examples/web-pages-simple.ps1 @@ -1,4 +1,4 @@ -param ( +param( [int] $Port = 8085 ) diff --git a/examples/web-pages.ps1 b/examples/web-pages.ps1 index 90fbf799e..2d1dcb53b 100644 --- a/examples/web-pages.ps1 +++ b/examples/web-pages.ps1 @@ -1,4 +1,4 @@ -param ( +param( [int] $Port = 8085 ) diff --git a/examples/web-static.ps1 b/examples/web-static.ps1 index 3f0bf2472..c6b4a0cc0 100644 --- a/examples/web-static.ps1 +++ b/examples/web-static.ps1 @@ -1,4 +1,4 @@ -param ( +param( [int] $Port = 8085 ) diff --git a/examples/web-upload-kestrel.ps1 b/examples/web-upload-kestrel.ps1 index f021ed274..61560dae2 100644 --- a/examples/web-upload-kestrel.ps1 +++ b/examples/web-upload-kestrel.ps1 @@ -1,4 +1,4 @@ -param ( +param( [int] $Port = 8085 ) diff --git a/examples/web-upload.ps1 b/examples/web-upload.ps1 index 8b0fc7e7a..e652c3ffb 100644 --- a/examples/web-upload.ps1 +++ b/examples/web-upload.ps1 @@ -1,4 +1,4 @@ -param ( +param( [int] $Port = 8085 ) diff --git a/packers/choco/tools/ChocolateyInstall.ps1 b/packers/choco/tools/ChocolateyInstall.ps1 index 70babe97c..574ed5853 100644 --- a/packers/choco/tools/ChocolateyInstall.ps1 +++ b/packers/choco/tools/ChocolateyInstall.ps1 @@ -24,7 +24,7 @@ function Install-PodeModule($path, $version) try { - Push-Location (Join-Path $env:ChocolateyPackageFolder 'src') + Push-Location (Join-Path $toolsDir 'src') # which folders do we need? $folders = @('Private', 'Public', 'Misc', 'Libs') @@ -52,6 +52,9 @@ function Install-PodeModule($path, $version) # Determine which Program Files path to use $progFiles = [string]$env:ProgramFiles +# determine the path to choco tools +$toolsDir = Split-Path -Path (Split-Path -Parent $MyInvocation.MyCommand.Definition) + # Install PS Module # Set the module path $modulePath = Join-Path $progFiles (Join-Path 'WindowsPowerShell' 'Modules') diff --git a/pode.build.ps1 b/pode.build.ps1 index 3b59e8f82..a0366fd10 100644 --- a/pode.build.ps1 +++ b/pode.build.ps1 @@ -1,4 +1,4 @@ -param ( +param( [string] $Version = '' ) @@ -8,43 +8,38 @@ param ( #> $Versions = @{ - Pester = '4.8.0' - MkDocs = '1.4.2' + Pester = '4.8.0' + MkDocs = '1.5.3' PSCoveralls = '1.0.0' - SevenZip = '18.5.0.20180730' - DotNet = '7.0.1' - Checksum = '0.2.0' - MkDocsTheme = '9.0.2' - PlatyPS = '0.14.2' + SevenZip = '18.5.0.20180730' + DotNet = '7.0.1' + Checksum = '0.2.0' + MkDocsTheme = '9.4.6' + PlatyPS = '0.14.2' } <# # Helper Functions #> -function Test-PodeBuildIsWindows -{ +function Test-PodeBuildIsWindows { $v = $PSVersionTable return ($v.Platform -ilike '*win*' -or ($null -eq $v.Platform -and $v.PSEdition -ieq 'desktop')) } -function Test-PodeBuildIsGitHub -{ +function Test-PodeBuildIsGitHub { return (![string]::IsNullOrWhiteSpace($env:GITHUB_REF)) } -function Test-PodeBuildCanCodeCoverage -{ +function Test-PodeBuildCanCodeCoverage { return (@('1', 'true') -icontains $env:PODE_RUN_CODE_COVERAGE) } -function Get-PodeBuildService -{ +function Get-PodeBuildService { return 'github-actions' } -function Test-PodeBuildCommand($cmd) -{ +function Test-PodeBuildCommand($cmd) { $path = $null if (Test-PodeBuildIsWindows) { @@ -57,13 +52,11 @@ function Test-PodeBuildCommand($cmd) return (![string]::IsNullOrWhiteSpace($path)) } -function Get-PodeBuildBranch -{ +function Get-PodeBuildBranch { return ($env:GITHUB_REF -ireplace 'refs\/heads\/', '') } -function Invoke-PodeBuildInstall($name, $version) -{ +function Invoke-PodeBuildInstall($name, $version) { [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 if (Test-PodeBuildIsWindows) { @@ -84,8 +77,7 @@ function Invoke-PodeBuildInstall($name, $version) } } -function Install-PodeBuildModule($name) -{ +function Install-PodeBuildModule($name) { if ($null -ne ((Get-Module -ListAvailable $name) | Where-Object { $_.Version -ieq $Versions[$name] })) { return } @@ -95,8 +87,7 @@ function Install-PodeBuildModule($name) Install-Module -Name "$($name)" -Scope CurrentUser -RequiredVersion "$($Versions[$name])" -Force -SkipPublisherCheck } -function Invoke-PodeBuildDotnetBuild($target) -{ +function Invoke-PodeBuildDotnetBuild($target) { dotnet build --configuration Release --self-contained --framework $target if (!$?) { throw "dotnet build failed for $($target)" @@ -354,17 +345,17 @@ task DocsHelpBuild DocsDeps, { $updated = $false $content = (Get-Content -Path $_.FullName | ForEach-Object { - $line = $_ + $line = $_ - while ($line -imatch '\[`(?[a-z]+\-pode[a-z]+)`\](?([^(]|$))') { - $updated = $true - $name = $Matches['name'] - $char = $Matches['char'] - $line = ($line -ireplace "\[``$($name)``\]([^(]|$)", "[``$($name)``]($('../' * $depth)Functions/$($map[$name])/$($name))$($char)") - } + while ($line -imatch '\[`(?[a-z]+\-pode[a-z]+)`\](?([^(]|$))') { + $updated = $true + $name = $Matches['name'] + $char = $Matches['char'] + $line = ($line -ireplace "\[``$($name)``\]([^(]|$)", "[``$($name)``]($('../' * $depth)Functions/$($map[$name])/$($name))$($char)") + } - $line - }) + $line + }) if ($updated) { $content | Out-File -FilePath $_.FullName -Force -Encoding ascii diff --git a/src/Listener/PodeHttpRequest.cs b/src/Listener/PodeHttpRequest.cs index 2f9411949..a78fcdb79 100644 --- a/src/Listener/PodeHttpRequest.cs +++ b/src/Listener/PodeHttpRequest.cs @@ -205,15 +205,11 @@ private int ParseHeaders(string[] reqLines, string newline) // query string var reqQuery = reqMeta[1].Trim(); - if (!string.IsNullOrWhiteSpace(reqQuery)) - { - var qmIndex = reqQuery.IndexOf("?"); - QueryString = HttpUtility.ParseQueryString(qmIndex > 0 ? reqQuery.Substring(qmIndex) : reqQuery); - } - else - { - QueryString = default(NameValueCollection); - } + var qmIndex = string.IsNullOrEmpty(reqQuery) ? 0 : reqQuery.IndexOf("?"); + + QueryString = qmIndex > 0 + ? HttpUtility.ParseQueryString(reqQuery.Substring(qmIndex)) + : default(NameValueCollection); // http protocol version Protocol = (reqMeta[2] ?? "HTTP/1.1").Trim(); diff --git a/src/Listener/PodeListener.cs b/src/Listener/PodeListener.cs index c87636401..9ad432b0f 100644 --- a/src/Listener/PodeListener.cs +++ b/src/Listener/PodeListener.cs @@ -35,6 +35,16 @@ public int RequestBodySize } } + private bool _showServerDetails = true; + public bool ShowServerDetails + { + get => _showServerDetails; + set + { + _showServerDetails = value; + } + } + public PodeListener(CancellationToken cancellationToken = default(CancellationToken)) : base(cancellationToken) { diff --git a/src/Listener/PodeResponse.cs b/src/Listener/PodeResponse.cs index 3fe8a7b96..a56963c6b 100644 --- a/src/Listener/PodeResponse.cs +++ b/src/Listener/PodeResponse.cs @@ -298,10 +298,20 @@ private void SetDefaultHeaders() Headers.Add("Date", DateTime.UtcNow.ToString("r", CultureInfo.InvariantCulture)); - // set the server - if (!Headers.ContainsKey("Server")) + // set the server if allowed + if (Context.Listener.ShowServerDetails) { - Headers.Add("Server", "Pode"); + if (!Headers.ContainsKey("Server")) + { + Headers.Add("Server", "Pode"); + } + } + else + { + if (Headers.ContainsKey("Server")) + { + Headers.Remove("Server"); + } } // set context/socket ID diff --git a/src/Listener/PodeSmtpRequest.cs b/src/Listener/PodeSmtpRequest.cs index 56582c47d..a2b49eadc 100644 --- a/src/Listener/PodeSmtpRequest.cs +++ b/src/Listener/PodeSmtpRequest.cs @@ -320,14 +320,34 @@ private Hashtable ParseHeaders(string value) if (match.Success) { previousHeader = match.Groups["name"].Value; - headers.Add(match.Groups["name"].Value, match.Groups["value"].Value); + if (headers.ContainsKey(previousHeader)) + { + if (!(headers[previousHeader] is List)) + { + headers[previousHeader] = new List() { headers[previousHeader] }; + } + + ((List)headers[previousHeader]).Add(match.Groups["value"].Value); + } + else + { + headers.Add(previousHeader, match.Groups["value"].Value); + } } else { match = Regex.Match(line, "^(?.*?)\\:\\s+"); if (!match.Success) { - headers[previousHeader] += line; + if (headers[previousHeader] is List) + { + var values = (List)headers[previousHeader]; + values[values.Count - 1] += line; + } + else + { + headers[previousHeader] += line; + } } } diff --git a/src/Pode.Internal.psd1 b/src/Pode.Internal.psd1 index 1e295a37f..a7bb4dee5 100644 --- a/src/Pode.Internal.psd1 +++ b/src/Pode.Internal.psd1 @@ -8,16 +8,16 @@ @{ # Script module or binary module file associated with this manifest. - RootModule = 'Pode.Internal.psm1' + RootModule = 'Pode.Internal.psm1' # Version number of this module. - ModuleVersion = '$version$' + ModuleVersion = '$version$' # ID used to uniquely identify this module - GUID = '86b48c1c-8b59-4f3c-80bb-936d6b3218f6' + GUID = '86b48c1c-8b59-4f3c-80bb-936d6b3218f6' # Author of this module - Author = 'Matthew Kelly (Badgerati)' + Author = 'Matthew Kelly (Badgerati)' # Minimum version of the Windows PowerShell engine required by this module PowerShellVersion = '5.0' diff --git a/src/Pode.psd1 b/src/Pode.psd1 index 10cc580da..cd0ba8d01 100644 --- a/src/Pode.psd1 +++ b/src/Pode.psd1 @@ -8,22 +8,22 @@ @{ # Script module or binary module file associated with this manifest. - RootModule = 'Pode.psm1' + RootModule = 'Pode.psm1' # Version number of this module. - ModuleVersion = '$version$' + ModuleVersion = '$version$' # ID used to uniquely identify this module - GUID = 'e3ea217c-fc3d-406b-95d5-4304ab06c6af' + GUID = 'e3ea217c-fc3d-406b-95d5-4304ab06c6af' # Author of this module - Author = 'Matthew Kelly (Badgerati)' + Author = 'Matthew Kelly (Badgerati)' # Copyright statement for this module - Copyright = 'Copyright (c) 2017-2023 Matthew Kelly (Badgerati), licensed under the MIT License.' + Copyright = 'Copyright (c) 2017-2023 Matthew Kelly (Badgerati), licensed under the MIT License.' # Description of the functionality provided by this module - Description = 'A Cross-Platform PowerShell framework for creating web servers to host REST APIs and Websites. Pode also has support for being used in Azure Functions and AWS Lambda.' + Description = 'A Cross-Platform PowerShell framework for creating web servers to host REST APIs and Websites. Pode also has support for being used in Azure Functions and AWS Lambda.' # Minimum version of the Windows PowerShell engine required by this module PowerShellVersion = '5.0' @@ -116,6 +116,7 @@ 'Out-PodeVariable', 'Test-PodeIsHosted', 'New-PodeCron', + 'Test-PodeInRunspace', # routes 'Add-PodeRoute', @@ -218,9 +219,30 @@ 'Add-PodeAuthUserFile', 'ConvertTo-PodeJwt', 'ConvertFrom-PodeJwt', + 'Test-PodeJwt' 'Use-PodeAuth', 'ConvertFrom-PodeOIDCDiscovery', 'Test-PodeAuthUser', + 'Merge-PodeAuth', + 'Test-PodeAuth', + 'Test-PodeAuthExists', + 'Get-PodeAuthUser', + 'Add-PodeAuthSession', + + # access + 'New-PodeAccessScheme', + 'Add-PodeAccess', + 'Add-PodeAccessCustom', + 'Get-PodeAccess', + 'Test-PodeAccessExists', + 'Test-PodeAccess', + 'Test-PodeAccessUser', + 'Test-PodeAccessRoute', + 'Merge-PodeAccess', + 'Remove-PodeAccess', + 'Clear-PodeAccess', + 'Add-PodeAccessMiddleware', + 'Use-PodeAccess', # logging 'New-PodeLoggingMethod', @@ -312,6 +334,8 @@ 'Set-PodeSecurityPermissionsPolicy', 'Set-PodeSecurityReferrerPolicy', 'Set-PodeSecurityStrictTransportSecurity', + 'Hide-PodeSecurityServer', + 'Show-PodeSecurityServer', # Verbs 'Add-PodeVerb', @@ -380,24 +404,24 @@ ) # 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 = @{ + PrivateData = @{ PSData = @{ # Tags applied to this module. These help with module discovery in online galleries. - Tags = @('powershell', 'web', 'server', 'http', 'listener', 'rest', 'api', 'tcp', 'smtp', 'websites', + Tags = @('powershell', 'web', 'server', 'http', 'listener', 'rest', 'api', 'tcp', 'smtp', 'websites', 'powershell-core', 'windows', 'unix', 'linux', 'pode', 'PSEdition_Core', 'cross-platform', 'file-monitoring', 'multithreaded', 'schedule', 'middleware', 'session', - 'authentication', 'arm', 'raspberry-pi', 'aws-lambda', + 'authentication', 'authorisation', 'arm', 'raspberry-pi', 'aws-lambda', 'azure-functions', 'websockets', 'swagger', 'openapi', 'webserver', 'secrets', 'fim') # A URL to the license for this module. - LicenseUri = 'https://raw.githubusercontent.com/Badgerati/Pode/master/LICENSE.txt' + LicenseUri = 'https://raw.githubusercontent.com/Badgerati/Pode/master/LICENSE.txt' # A URL to the main website for this project. - ProjectUri = 'https://github.com/Badgerati/Pode' + ProjectUri = 'https://github.com/Badgerati/Pode' # A URL to an icon representing this module. - IconUri = 'https://raw.githubusercontent.com/Badgerati/Pode/master/images/icon.png' + IconUri = 'https://raw.githubusercontent.com/Badgerati/Pode/master/images/icon.png' # Release notes for this particular version of the module ReleaseNotes = 'https://github.com/Badgerati/Pode/releases/tag/v$version$' diff --git a/src/Private/Access.ps1 b/src/Private/Access.ps1 new file mode 100644 index 000000000..ddbc6c344 --- /dev/null +++ b/src/Private/Access.ps1 @@ -0,0 +1,65 @@ +function Get-PodeAccessMiddlewareScript { + return { + param($opts) + + if ($null -eq $WebEvent.Auth) { + Set-PodeResponseStatus -Code 403 + return $false + } + + # test access + $WebEvent.Auth.IsAuthorised = Invoke-PodeAccessValidation -Name $opts.Name + + # 403 if unauthorised + if (!$WebEvent.Auth.IsAuthorised) { + Set-PodeResponseStatus -Code 403 + } + + # run next middleware or stop? + return $WebEvent.Auth.IsAuthorised + } +} + +function Invoke-PodeAccessValidation { + param( + [Parameter(Mandatory = $true)] + [string] + $Name + ) + + # get the access method + $access = $PodeContext.Server.Authorisations.Methods[$Name] + + # if it's a merged access, re-call this function and check against "succeed" value + if ($access.Merged) { + foreach ($accName in $access.Access) { + $result = Invoke-PodeAccessValidation -Name $accName + + # if the access passed, and we only need one access to pass, return true + if ($result -and $access.PassOne) { + return $true + } + + # if the access failed, but we need all to pass, return false + if (!$result -and !$access.PassOne) { + return $false + } + } + + # if the last access failed, and we only need one access to pass, return false + if (!$result -and $access.PassOne) { + return $false + } + + # if the last access succeeded, and we need all to pass, return true + if ($result -and !$access.PassOne) { + return $true + } + + # default failure + return $false + } + + # main access validation logic + return (Test-PodeAccessRoute -Name $Name) +} \ No newline at end of file diff --git a/src/Private/Authentication.ps1 b/src/Private/Authentication.ps1 index fbd3f7514..b681d5b0a 100644 --- a/src/Private/Authentication.ps1 +++ b/src/Private/Authentication.ps1 @@ -1,5 +1,4 @@ -function Get-PodeAuthBasicType -{ +function Get-PodeAuthBasicType { return { param($options) @@ -8,7 +7,7 @@ function Get-PodeAuthBasicType if ($null -eq $header) { return @{ Message = 'No Authorization header found' - Code = 401 + Code = 401 } } @@ -17,14 +16,14 @@ function Get-PodeAuthBasicType if ($atoms.Length -lt 2) { return @{ Message = 'Invalid Authorization header' - Code = 400 + Code = 400 } } if ($atoms[0] -ine $options.HeaderTag) { return @{ Message = "Header is not for $($options.HeaderTag) Authorization" - Code = 400 + Code = 400 } } @@ -35,7 +34,7 @@ function Get-PodeAuthBasicType catch { return @{ Message = 'Invalid encoding specified for Authorization' - Code = 400 + Code = 400 } } @@ -45,7 +44,7 @@ function Get-PodeAuthBasicType catch { return @{ Message = 'Invalid Base64 string found in Authorization header' - Code = 400 + Code = 400 } } @@ -69,8 +68,7 @@ function Get-PodeAuthBasicType } } -function Get-PodeAuthOAuth2Type -{ +function Get-PodeAuthOAuth2Type { return { param($options, $schemes) @@ -84,8 +82,8 @@ function Get-PodeAuthOAuth2Type # if there's an error, fail if (![string]::IsNullOrWhiteSpace($WebEvent.Query['error'])) { return @{ - Message = $WebEvent.Query['error'] - Code = 401 + Message = $WebEvent.Query['error'] + Code = 401 IsErrored = $true } } @@ -103,8 +101,8 @@ function Get-PodeAuthOAuth2Type # ensure the state is valid if ((Test-PodeSessionsInUse) -and ($WebEvent.Query['state'] -ne $WebEvent.Session.Data['__pode_oauth_state__'])) { return @{ - Message = "OAuth2 state returned is invalid" - Code = 401 + Message = 'OAuth2 state returned is invalid' + Code = 401 IsErrored = $true } } @@ -148,8 +146,8 @@ function Get-PodeAuthOAuth2Type # was there an error? if (![string]::IsNullOrWhiteSpace($result.error)) { return @{ - Message = "$($result.error): $($result.error_description)" - Code = 401 + Message = "$($result.error): $($result.error_description)" + Code = 401 IsErrored = $true } } @@ -166,8 +164,8 @@ function Get-PodeAuthOAuth2Type if (![string]::IsNullOrWhiteSpace($user.error)) { return @{ - Message = "$($user.error): $($user.error_description)" - Code = 401 + Message = "$($user.error): $($user.error_description)" + Code = 401 IsErrored = $true } } @@ -207,9 +205,9 @@ function Get-PodeAuthOAuth2Type # add authUrl query params $query = "client_id=$($options.Client.ID)" - $query += "&response_type=code" + $query += '&response_type=code' $query += "&redirect_uri=$([System.Web.HttpUtility]::UrlEncode($redirectUrl))" - $query += "&response_mode=query" + $query += '&response_mode=query' $query += "&scope=$([System.Web.HttpUtility]::UrlEncode($scopes))" # add csrf state @@ -250,15 +248,14 @@ function Get-PodeAuthOAuth2Type # hmm, this is unexpected return @{ - Message = 'Well, this is awkward...' - Code = 500 + Message = 'Well, this is awkward...' + Code = 500 IsErrored = $true } } } -function Get-PodeOAuth2RedirectHost -{ +function Get-PodeOAuth2RedirectHost { param( [Parameter()] [string] @@ -284,8 +281,7 @@ function Get-PodeOAuth2RedirectHost return $RedirectUrl } -function Get-PodeAuthClientCertificateType -{ +function Get-PodeAuthClientCertificateType { return { param($options) $cert = $WebEvent.Request.ClientCertificate @@ -294,7 +290,7 @@ function Get-PodeAuthClientCertificateType if ($null -eq $cert) { return @{ Message = 'No client certificate supplied' - Code = 401 + Code = 401 } } @@ -302,7 +298,7 @@ function Get-PodeAuthClientCertificateType if ([string]::IsNullOrWhiteSpace($cert.Thumbprint)) { return @{ Message = 'Invalid client certificate supplied' - Code = 401 + Code = 401 } } @@ -311,7 +307,7 @@ function Get-PodeAuthClientCertificateType if (($cert.NotAfter -lt $now) -or ($cert.NotBefore -gt $now)) { return @{ Message = 'Invalid client certificate supplied' - Code = 401 + Code = 401 } } @@ -320,8 +316,7 @@ function Get-PodeAuthClientCertificateType } } -function Get-PodeAuthApiKeyType -{ +function Get-PodeAuthApiKeyType { return { param($options) @@ -346,7 +341,7 @@ function Get-PodeAuthApiKeyType if ([string]::IsNullOrWhiteSpace($apiKey)) { return @{ Message = "No $($options.LocationName) $($options.Location) found" - Code = 400 + Code = 400 } } @@ -364,7 +359,7 @@ function Get-PodeAuthApiKeyType if ($_.Exception.Message -ilike '*jwt*') { return @{ Message = $_.Exception.Message - Code = 400 + Code = 400 } } @@ -379,8 +374,7 @@ function Get-PodeAuthApiKeyType } } -function Get-PodeAuthBearerType -{ +function Get-PodeAuthBearerType { return { param($options) @@ -388,9 +382,9 @@ function Get-PodeAuthBearerType $header = (Get-PodeHeader -Name 'Authorization') if ($null -eq $header) { return @{ - Message = 'No Authorization header found' + Message = 'No Authorization header found' Challenge = (New-PodeAuthBearerChallenge -Scopes $options.Scopes -ErrorType invalid_request) - Code = 400 + Code = 400 } } @@ -398,17 +392,17 @@ function Get-PodeAuthBearerType $atoms = $header -isplit '\s+' if ($atoms.Length -lt 2) { return @{ - Message = 'Invalid Authorization header' + Message = 'Invalid Authorization header' Challenge = (New-PodeAuthBearerChallenge -Scopes $options.Scopes -ErrorType invalid_request) - Code = 400 + Code = 400 } } if ($atoms[0] -ine $options.HeaderTag) { return @{ - Message = "Authorization header is not $($options.HeaderTag)" + Message = "Authorization header is not $($options.HeaderTag)" Challenge = (New-PodeAuthBearerChallenge -Scopes $options.Scopes -ErrorType invalid_request) - Code = 400 + Code = 400 } } @@ -416,8 +410,8 @@ function Get-PodeAuthBearerType $token = $atoms[1] if ([string]::IsNullOrWhiteSpace($token)) { return @{ - Message = "No Bearer token found" - Code = 400 + Message = 'No Bearer token found' + Code = 400 } } @@ -435,7 +429,7 @@ function Get-PodeAuthBearerType if ($_.Exception.Message -ilike '*jwt*') { return @{ Message = $_.Exception.Message - Code = 400 + Code = 400 } } @@ -450,26 +444,25 @@ function Get-PodeAuthBearerType } } -function Get-PodeAuthBearerPostValidator -{ +function Get-PodeAuthBearerPostValidator { return { param($token, $result, $options) # if there's no user, fail with challenge if (($null -eq $result) -or ($null -eq $result.User)) { return @{ - Message = 'User not found' + Message = 'User not found' Challenge = (New-PodeAuthBearerChallenge -Scopes $options.Scopes -ErrorType invalid_token) - Code = 401 + Code = 401 } } # check for an error and description if (![string]::IsNullOrWhiteSpace($result.Error)) { return @{ - Message = 'Authorization failed' + Message = 'Authorization failed' Challenge = (New-PodeAuthBearerChallenge -Scopes $options.Scopes -ErrorType $result.Error -ErrorDescription $result.ErrorDescription) - Code = 401 + Code = 401 } } @@ -480,18 +473,18 @@ function Get-PodeAuthBearerPostValidator # 403 if we have auth scopes but no token scope if ($hasAuthScopes -and !$hasTokenScope) { return @{ - Message = 'Invalid Scope' + Message = 'Invalid Scope' Challenge = (New-PodeAuthBearerChallenge -Scopes $options.Scopes -ErrorType insufficient_scope) - Code = 403 + Code = 403 } } # 403 if we have both, but token not in auth scope if ($hasAuthScopes -and $hasTokenScope -and ($options.Scopes -notcontains $result.Scope)) { return @{ - Message = 'Invalid Scope' + Message = 'Invalid Scope' Challenge = (New-PodeAuthBearerChallenge -Scopes $options.Scopes -ErrorType insufficient_scope) - Code = 403 + Code = 403 } } @@ -500,8 +493,7 @@ function Get-PodeAuthBearerPostValidator } } -function New-PodeAuthBearerChallenge -{ +function New-PodeAuthBearerChallenge { param( [Parameter()] [string[]] @@ -533,8 +525,7 @@ function New-PodeAuthBearerChallenge return ($items -join ', ') } -function Get-PodeAuthDigestType -{ +function Get-PodeAuthDigestType { return { param($options) @@ -542,9 +533,9 @@ function Get-PodeAuthDigestType $header = (Get-PodeHeader -Name 'Authorization') if ($null -eq $header) { return @{ - Message = 'No Authorization header found' + Message = 'No Authorization header found' Challenge = (New-PodeAuthDigestChallenge) - Code = 401 + Code = 401 } } @@ -553,15 +544,15 @@ function Get-PodeAuthDigestType if ($atoms.Length -lt 2) { return @{ Message = 'Invalid Authorization header' - Code = 400 + Code = 400 } } if ($atoms[0] -ine $options.HeaderTag) { return @{ - Message = "Authorization header is not $($options.HeaderTag)" + Message = "Authorization header is not $($options.HeaderTag)" Challenge = (New-PodeAuthDigestChallenge) - Code = 401 + Code = 401 } } @@ -570,16 +561,16 @@ function Get-PodeAuthDigestType if ($params.Count -eq 0) { return @{ Message = 'Invalid Authorization header' - Code = 400 + Code = 400 } } # if no username then 401 and challenge if ([string]::IsNullOrWhiteSpace($params.username)) { return @{ - Message = 'Authorization header is missing username' + Message = 'Authorization header is missing username' Challenge = (New-PodeAuthDigestChallenge) - Code = 401 + Code = 401 } } @@ -587,7 +578,7 @@ function Get-PodeAuthDigestType if ($WebEvent.Path -ine $params.uri) { return @{ Message = 'Invalid Authorization header' - Code = 400 + Code = 400 } } @@ -596,17 +587,16 @@ function Get-PodeAuthDigestType } } -function Get-PodeAuthDigestPostValidator -{ +function Get-PodeAuthDigestPostValidator { return { param($username, $params, $result, $options) # if there's no user or password, fail with challenge if (($null -eq $result) -or ($null -eq $result.User) -or [string]::IsNullOrWhiteSpace($result.Password)) { return @{ - Message = 'User not found' + Message = 'User not found' Challenge = (New-PodeAuthDigestChallenge) - Code = 401 + Code = 401 } } @@ -622,9 +612,9 @@ function Get-PodeAuthDigestPostValidator # compare final hash to client response if ($final -ne $params.response) { return @{ - Message = 'Hashes failed to match' + Message = 'Hashes failed to match' Challenge = (New-PodeAuthDigestChallenge) - Code = 401 + Code = 401 } } @@ -634,8 +624,7 @@ function Get-PodeAuthDigestPostValidator } } -function ConvertFrom-PodeAuthDigestHeader -{ +function ConvertFrom-PodeAuthDigestHeader { param( [Parameter()] [string[]] @@ -658,14 +647,12 @@ function ConvertFrom-PodeAuthDigestHeader return $obj } -function New-PodeAuthDigestChallenge -{ +function New-PodeAuthDigestChallenge { $items = @('qop="auth"', 'algorithm="MD5"', "nonce=`"$(New-PodeGuid -Secure -NoDashes)`"") return ($items -join ', ') } -function Get-PodeAuthFormType -{ +function Get-PodeAuthFormType { return { param($options) @@ -681,7 +668,7 @@ function Get-PodeAuthFormType if ([string]::IsNullOrWhiteSpace($username) -or [string]::IsNullOrWhiteSpace($password)) { return @{ Message = 'Username or Password not supplied' - Code = 401 + Code = 401 } } @@ -700,21 +687,32 @@ function Get-PodeAuthFormType } } -function Get-PodeAuthUserFileMethod -{ +function Get-PodeAuthUserFileMethod { return { param($username, $password, $options) + # using pscreds? + if (($null -eq $options) -and ($username -is [pscredential])) { + $_username = ([pscredential]$username).UserName + $_password = ([pscredential]$username).GetNetworkCredential().Password + $_options = [hashtable]$password + } + else { + $_username = $username + $_password = $password + $_options = $options + } + # load the file - $users = (Get-Content -Path $options.FilePath -Raw | ConvertFrom-Json) + $users = (Get-Content -Path $_options.FilePath -Raw | ConvertFrom-Json) # find the user by username - only use the first one $user = @(foreach ($_user in $users) { - if ($_user.Username -ieq $username) { - $_user - break - } - })[0] + if ($_user.Username -ieq $_username) { + $_user + break + } + })[0] # fail if no user if ($null -eq $user) { @@ -722,11 +720,11 @@ function Get-PodeAuthUserFileMethod } # check the user's password - if (![string]::IsNullOrWhiteSpace($options.HmacSecret)) { - $hash = Invoke-PodeHMACSHA256Hash -Value $password -Secret $options.HmacSecret + if (![string]::IsNullOrWhiteSpace($_options.HmacSecret)) { + $hash = Invoke-PodeHMACSHA256Hash -Value $_password -Secret $_options.HmacSecret } else { - $hash = Invoke-PodeSHA256Hash -Value $password + $hash = Invoke-PodeSHA256Hash -Value $_password } if ($user.Password -ne $hash) { @@ -735,23 +733,23 @@ function Get-PodeAuthUserFileMethod # convert the user to a hashtable $user = @{ - Name = $user.Name + Name = $user.Name Username = $user.Username - Email = $user.Email - Groups = $user.Groups + Email = $user.Email + Groups = $user.Groups Metadata = $user.Metadata } # is the user valid for any users/groups? - if (!(Test-PodeAuthUserGroups -User $user -Users $options.Users -Groups $options.Groups)) { + if (!(Test-PodeAuthUserGroups -User $user -Users $_options.Users -Groups $_options.Groups)) { return @{ Message = 'You are not authorised to access this website' } } $result = @{ User = $user } # call additional scriptblock if supplied - if ($null -ne $options.ScriptBlock.Script) { - $result = Invoke-PodeAuthInbuiltScriptBlock -User $result.User -ScriptBlock $options.ScriptBlock.Script -UsingVariables $options.ScriptBlock.UsingVariables + if ($null -ne $_options.ScriptBlock.Script) { + $result = Invoke-PodeAuthInbuiltScriptBlock -User $result.User -ScriptBlock $_options.ScriptBlock.Script -UsingVariables $_options.ScriptBlock.UsingVariables } # return final result, this could contain a user obj, or an error message from custom scriptblock @@ -759,26 +757,37 @@ function Get-PodeAuthUserFileMethod } } -function Get-PodeAuthWindowsADMethod -{ +function Get-PodeAuthWindowsADMethod { return { param($username, $password, $options) + # using pscreds? + if (($null -eq $options) -and ($username -is [pscredential])) { + $_username = ([pscredential]$username).UserName + $_password = ([pscredential]$username).GetNetworkCredential().Password + $_options = [hashtable]$password + } + else { + $_username = $username + $_password = $password + $_options = $options + } + # parse username to remove domains - $username = (($username -split '@')[0] -split '\\')[-1] + $_username = (($_username -split '@')[0] -split '\\')[-1] # validate and retrieve the AD user - $noGroups = $options.NoGroups - $directGroups = $options.DirectGroups - $keepCredential = $options.KeepCredential + $noGroups = $_options.NoGroups + $directGroups = $_options.DirectGroups + $keepCredential = $_options.KeepCredential $result = Get-PodeAuthADResult ` - -Server $options.Server ` - -Domain $options.Domain ` - -SearchBase $options.SearchBase ` - -Username $username ` - -Password $password ` - -Provider $options.Provider ` + -Server $_options.Server ` + -Domain $_options.Domain ` + -SearchBase $_options.SearchBase ` + -Username $_username ` + -Password $_password ` + -Provider $_options.Provider ` -NoGroups:$noGroups ` -DirectGroups:$directGroups ` -KeepCredential:$keepCredential @@ -794,13 +803,13 @@ function Get-PodeAuthWindowsADMethod } # is the user valid for any users/groups - if not, error! - if (!(Test-PodeAuthUserGroups -User $result.User -Users $options.Users -Groups $options.Groups)) { + if (!(Test-PodeAuthUserGroups -User $result.User -Users $_options.Users -Groups $_options.Groups)) { return @{ Message = 'You are not authorised to access this website' } } # call additional scriptblock if supplied - if ($null -ne $options.ScriptBlock.Script) { - $result = Invoke-PodeAuthInbuiltScriptBlock -User $result.User -ScriptBlock $options.ScriptBlock.Script -UsingVariables $options.ScriptBlock.UsingVariables + if ($null -ne $_options.ScriptBlock.Script) { + $result = Invoke-PodeAuthInbuiltScriptBlock -User $result.User -ScriptBlock $_options.ScriptBlock.Script -UsingVariables $_options.ScriptBlock.UsingVariables } # return final result, this could contain a user obj, or an error message from custom scriptblock @@ -808,14 +817,13 @@ function Get-PodeAuthWindowsADMethod } } -function Invoke-PodeAuthInbuiltScriptBlock -{ +function Invoke-PodeAuthInbuiltScriptBlock { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [hashtable] $User, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [scriptblock] $ScriptBlock, @@ -827,39 +835,50 @@ function Invoke-PodeAuthInbuiltScriptBlock return (Invoke-PodeScriptBlock -ScriptBlock $ScriptBlock -Arguments $_args -Return -Splat) } -function Get-PodeAuthWindowsLocalMethod -{ +function Get-PodeAuthWindowsLocalMethod { return { param($username, $password, $options) + # using pscreds? + if (($null -eq $options) -and ($username -is [pscredential])) { + $_username = ([pscredential]$username).UserName + $_password = ([pscredential]$username).GetNetworkCredential().Password + $_options = [hashtable]$password + } + else { + $_username = $username + $_password = $password + $_options = $options + } + $user = @{ - UserType = 'Local' + UserType = 'Local' AuthenticationType = 'WinNT' - Username = $username - Name = [string]::Empty - Fqdn = $PodeContext.Server.ComputerName - Domain = 'localhost' - Groups = @() + Username = $_username + Name = [string]::Empty + Fqdn = $PodeContext.Server.ComputerName + Domain = 'localhost' + Groups = @() } Add-Type -AssemblyName System.DirectoryServices.AccountManagement -ErrorAction Stop $context = [System.DirectoryServices.AccountManagement.PrincipalContext]::new('Machine', $PodeContext.Server.ComputerName) - $valid = $context.ValidateCredentials($username, $password) + $valid = $context.ValidateCredentials($_username, $_password) if (!$valid) { return @{ Message = 'Invalid credentials supplied' } } try { - $tmpUsername = $username -replace '\\', '/' - if ($username -inotlike "$($PodeContext.Server.ComputerName)*") { - $tmpUsername = "$($PodeContext.Server.ComputerName)/$($username)" + $tmpUsername = $_username -replace '\\', '/' + if ($_username -inotlike "$($PodeContext.Server.ComputerName)*") { + $tmpUsername = "$($PodeContext.Server.ComputerName)/$($_username)" } $ad = [adsi]"WinNT://$($tmpUsername)" $user.Name = @($ad.FullName)[0] - if (!$options.NoGroups) { + if (!$_options.NoGroups) { $cmd = "`$ad = [adsi]'WinNT://$($tmpUsername)'; @(`$ad.Groups() | Foreach-Object { `$_.GetType().InvokeMember('Name', 'GetProperty', `$null, `$_, `$null) })" $user.Groups = [string[]](powershell -c $cmd) } @@ -869,15 +888,15 @@ function Get-PodeAuthWindowsLocalMethod } # is the user valid for any users/groups - if not, error! - if (!(Test-PodeAuthUserGroups -User $user -Users $options.Users -Groups $options.Groups)) { + if (!(Test-PodeAuthUserGroups -User $user -Users $_options.Users -Groups $_options.Groups)) { return @{ Message = 'You are not authorised to access this website' } } $result = @{ User = $user } # call additional scriptblock if supplied - if ($null -ne $options.ScriptBlock.Script) { - $result = Invoke-PodeAuthInbuiltScriptBlock -User $result.User -ScriptBlock $options.ScriptBlock.Script -UsingVariables $options.ScriptBlock.UsingVariables + if ($null -ne $_options.ScriptBlock.Script) { + $result = Invoke-PodeAuthInbuiltScriptBlock -User $result.User -ScriptBlock $_options.ScriptBlock.Script -UsingVariables $_options.ScriptBlock.UsingVariables } # return final result, this could contain a user obj, or an error message from custom scriptblock @@ -885,8 +904,7 @@ function Get-PodeAuthWindowsLocalMethod } } -function Get-PodeAuthWindowsADIISMethod -{ +function Get-PodeAuthWindowsADIISMethod { return { param($token, $options) @@ -907,18 +925,18 @@ function Get-PodeAuthWindowsADIISMethod # create base user object $user = @{ - UserType = 'Domain' - Identity = @{ + UserType = 'Domain' + Identity = @{ AccessToken = $winIdentity.AccessToken } AuthenticationType = $winIdentity.AuthenticationType - DistinguishedName = [string]::Empty - Username = $username - Name = [string]::Empty - Email = [string]::Empty - Fqdn = [string]::Empty - Domain = $domain - Groups = @() + DistinguishedName = [string]::Empty + Username = $username + Name = [string]::Empty + Email = [string]::Empty + Fqdn = [string]::Empty + Domain = $domain + Groups = @() } # if the domain isn't local, attempt AD user @@ -926,7 +944,7 @@ function Get-PodeAuthWindowsADIISMethod # get the server's fdqn (and name/email) try { # Open ADSISearcher and change context to given domain - $searcher = [adsisearcher]"" + $searcher = [adsisearcher]'' $searcher.SearchRoot = [adsi]"LDAP://$($domain)" $searcher.Filter = "ObjectSid=$($winIdentity.User.Value.ToString())" @@ -1018,10 +1036,9 @@ function Get-PodeAuthWindowsADIISMethod } } -function Test-PodeAuthUserGroups -{ +function Test-PodeAuthUserGroups { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [hashtable] $User, @@ -1059,174 +1076,339 @@ function Test-PodeAuthUserGroups return $false } -function Get-PodeAuthMiddlewareScript -{ - return { - param($opts) +function Invoke-PodeAuthValidation { + param( + [Parameter(Mandatory = $true)] + [string] + $Name + ) - # get the auth method - $auth = Find-PodeAuth -Name $opts.Name + # get auth method + $auth = $PodeContext.Server.Authentications.Methods[$Name] - # route options for using sessions - $sessionless = $auth.Sessionless - $usingSessions = (Test-PodeSessionsInUse) - $useHeaders = $PodeContext.Server.Sessions.Info.UseHeaders - $loginRoute = $opts.Login + # if it's a merged auth, re-call this function and check against "succeed" value + if ($auth.Merged) { + $results = @{} - # check for logout command - if ($opts.Logout) { - Remove-PodeAuthSession + foreach ($authName in $auth.Authentications) { + $result = Invoke-PodeAuthValidation -Name $authName - if ($useHeaders) { - return (Set-PodeAuthStatus -StatusCode 401 -Sessionless:$sessionless -NoSuccessRedirect) + # if the auth is trying to redirect, we need to bubble the this back now + if ($result.Redirected) { + return $result } - else { - $auth.Failure.Url = (Protect-PodeValue -Value $auth.Failure.Url -Default $WebEvent.Request.Url.AbsolutePath) - return (Set-PodeAuthStatus -StatusCode 302 -Failure $auth.Failure -Sessionless:$sessionless -NoSuccessRedirect) - } - } - # if the session already has a user/isAuth'd, then skip auth - or allow anon - if ($usingSessions) { - # existing session auth'd - if (Test-PodeAuthUser) { - $WebEvent.Auth = $WebEvent.Session.Data.Auth - return (Set-PodeAuthStatus -Success $auth.Success -LoginRoute:$loginRoute -Sessionless:$sessionless -NoSuccessRedirect) + # if the auth passed, and we only need one auth to pass, return current result + if ($result.Success -and $auth.PassOne) { + return $result } - # if we're allowing anon access, and using sessions, then stop here - as a session will be created from a login route for auth'ing users - if ($opts.Anon) { - if (!(Test-PodeIsEmpty $WebEvent.Session.Data.Auth)) { - Revoke-PodeSession - } + # if the auth failed, but we need all to pass, return current result + if (!$result.Success -and !$auth.PassOne) { + return $result + } - return $true + # remember result if we need all to pass + if (!$auth.PassOne) { + $results[$authName] = $result } } - # check if the login flag is set, in which case just return and load a login get-page (allowing anon access) - if ($loginRoute -and !$useHeaders -and ($WebEvent.Method -ieq 'get')) { - if (!(Test-PodeIsEmpty $WebEvent.Session.Data.Auth)) { - Revoke-PodeSession - } + # if the last auth failed, and we only need one auth to pass, set failure and return + if (!$result.Success -and $auth.PassOne) { + return $result + } - return $true + # if the last auth succeeded, and we need all to pass, merge users/headers and return result + if ($result.Success -and !$auth.PassOne) { + # invoke scriptblock + $result = Invoke-PodeAuthInbuiltScriptBlock -User $results -ScriptBlock $auth.ScriptBlock.Script -UsingVariables $auth.ScriptBlock.UsingVariables + + # reset default properties and return + $result.Success = $true + $result.Auth = $results.Keys + return $result } - try { - $result = $null + # default failure + return @{ + Success = $false + StatusCode = 500 + } + } + + # main auth validation logic + $result = (Test-PodeAuthValidation -Name $Name) + $result.Auth = $Name + return $result +} + +function Test-PodeAuthValidation { + param( + [Parameter(Mandatory = $true)] + [string] + $Name + ) - # run pre-auth middleware - if ($null -ne $auth.Scheme.Middleware) { - if (!(Invoke-PodeMiddleware -Middleware $auth.Scheme.Middleware)) { - return $false + try { + # get auth method + $auth = $PodeContext.Server.Authentications.Methods[$Name] + + # auth result + $result = $null + + # run pre-auth middleware + if ($null -ne $auth.Scheme.Middleware) { + if (!(Invoke-PodeMiddleware -Middleware $auth.Scheme.Middleware)) { + return @{ + Success = $false } } + } - # run auth scheme script to parse request for data - $_args = @(Get-PodeScriptblockArguments -ArgumentList $auth.Scheme.Arguments -UsingVariables $auth.Scheme.ScriptBlock.UsingVariables) + # run auth scheme script to parse request for data + $_args = @(Get-PodeScriptblockArguments -ArgumentList $auth.Scheme.Arguments -UsingVariables $auth.Scheme.ScriptBlock.UsingVariables) - # call inner schemes first - if ($null -ne $auth.Scheme.InnerScheme) { - $schemes = @() + # call inner schemes first + if ($null -ne $auth.Scheme.InnerScheme) { + $schemes = @() - $_scheme = $auth.Scheme - $_inner = @(while ($null -ne $_scheme.InnerScheme) { + $_scheme = $auth.Scheme + $_inner = @(while ($null -ne $_scheme.InnerScheme) { $_scheme = $_scheme.InnerScheme $_scheme }) - for ($i = $_inner.Length - 1; $i -ge 0; $i--) { - $_tmp_args = @(Get-PodeScriptblockArguments -ArgumentList $_inner[$i].Arguments -UsingVariables $_inner[$i].ScriptBlock.UsingVariables) + for ($i = $_inner.Length - 1; $i -ge 0; $i--) { + $_tmp_args = @(Get-PodeScriptblockArguments -ArgumentList $_inner[$i].Arguments -UsingVariables $_inner[$i].ScriptBlock.UsingVariables) - $_tmp_args += ,$schemes - $result = (Invoke-PodeScriptBlock -ScriptBlock $_inner[$i].ScriptBlock.Script -Arguments $_tmp_args -Return -Splat) - if ($result -is [hashtable]) { - break - } - - $schemes += ,$result - $result = $null + $_tmp_args += , $schemes + $result = (Invoke-PodeScriptBlock -ScriptBlock $_inner[$i].ScriptBlock.Script -Arguments $_tmp_args -Return -Splat) + if ($result -is [hashtable]) { + break } - $_args += ,$schemes + $schemes += , $result + $result = $null } - if ($null -eq $result) { - $result = (Invoke-PodeScriptBlock -ScriptBlock $auth.Scheme.ScriptBlock.Script -Arguments $_args -Return -Splat) - } + $_args += , $schemes + } - # if data is a hashtable, then don't call validator (parser either failed, or forced a success) - if ($result -isnot [hashtable]) { - $original = $result + if ($null -eq $result) { + $result = (Invoke-PodeScriptBlock -ScriptBlock $auth.Scheme.ScriptBlock.Script -Arguments $_args -Return -Splat) + } - $_args = @($result) + @($auth.Arguments) - $_args = @(Get-PodeScriptblockArguments -ArgumentList $_args -UsingVariables $auth.UsingVariables) - $result = (Invoke-PodeScriptBlock -ScriptBlock $auth.ScriptBlock -Arguments $_args -Return -Splat) + # if data is a hashtable, then don't call validator (parser either failed, or forced a success) + if ($result -isnot [hashtable]) { + $original = $result - # if we have user, then run post validator if present - if ([string]::IsNullOrWhiteSpace($result.Code) -and !(Test-PodeIsEmpty $auth.Scheme.PostValidator.Script)) { - $_args = @($original) + @($result) + @($auth.Scheme.Arguments) - $_args = @(Get-PodeScriptblockArguments -ArgumentList $_args -UsingVariables $auth.Scheme.PostValidator.UsingVariables) - $result = (Invoke-PodeScriptBlock -ScriptBlock $auth.Scheme.PostValidator.Script -Arguments $_args -Return -Splat) - } + $_args = @($result) + @($auth.Arguments) + $_args = @(Get-PodeScriptblockArguments -ArgumentList $_args -UsingVariables $auth.UsingVariables) + $result = (Invoke-PodeScriptBlock -ScriptBlock $auth.ScriptBlock -Arguments $_args -Return -Splat) + + # if we have user, then run post validator if present + if ([string]::IsNullOrEmpty($result.Code) -and ($null -ne $auth.Scheme.PostValidator.Script)) { + $_args = @($original) + @($result) + @($auth.Scheme.Arguments) + $_args = @(Get-PodeScriptblockArguments -ArgumentList $_args -UsingVariables $auth.Scheme.PostValidator.UsingVariables) + $result = (Invoke-PodeScriptBlock -ScriptBlock $auth.Scheme.PostValidator.Script -Arguments $_args -Return -Splat) } } - catch { - $_ | Write-PodeErrorLog - return (Set-PodeAuthStatus -StatusCode 500 -Description $_.Exception.Message -Failure $auth.Failure -Sessionless:$sessionless) - } - # did the auth force a redirect? + # is the auth trying to redirect ie: oauth? if ($result.IsRedirected) { - return $false + return @{ + Success = $false + Redirected = $true + } } - # if there is no result, return false (failed auth) - but skip if allow anon access - if ((Test-PodeIsEmpty $result) -or (Test-PodeIsEmpty $result.User)) { - if (!$opts.Anon) { - $_code = (Protect-PodeValue -Value $result.Code -Default 401) + # if there's no result, or no user, then the auth failed - but allow auth if anon enabled + if (($null -eq $result) -or ($result.Count -eq 0) -or (Test-PodeIsEmpty $result.User)) { + $code = (Protect-PodeValue -Value $result.Code -Default 401) - # set the www-auth header - $validCode = (($_code -eq 401) -or ![string]::IsNullOrWhiteSpace($result.Challenge)) - $validHeaders = (($null -eq $result.Headers) -or !$result.Headers.ContainsKey('WWW-Authenticate')) + # set the www-auth header + $validCode = (($code -eq 401) -or ![string]::IsNullOrEmpty($result.Challenge)) - if ($validCode -and $validHeaders) { - $_wwwHeader = Get-PodeAuthWwwHeaderValue -Name $auth.Scheme.Name -Realm $auth.Scheme.Realm -Challenge $result.Challenge - if (![string]::IsNullOrWhiteSpace($_wwwHeader)) { - Set-PodeHeader -Name 'WWW-Authenticate' -Value $_wwwHeader - } + if ($validCode) { + if ($null -eq $result) { + $result = @{} + } + + if ($null -eq $result.Headers) { + $result.Headers = @{} } - $isErrored = [bool]$result.IsErrored - return (Set-PodeAuthStatus ` - -StatusCode $_code ` - -Description $result.Message ` - -Headers $result.Headers ` - -Failure $auth.Failure ` - -Success $auth.Success ` - -LoginRoute:$loginRoute ` - -Sessionless:$sessionless ` - -NoFailureRedirect:$isErrored) + if (![string]::IsNullOrWhiteSpace($auth.Scheme.Name) -and !$result.Headers.ContainsKey('WWW-Authenticate')) { + $authHeader = Get-PodeAuthWwwHeaderValue -Name $auth.Scheme.Name -Realm $auth.Scheme.Realm -Challenge $result.Challenge + $result.Headers['WWW-Authenticate'] = $authHeader + } } - else { - return $true + + return @{ + Success = $false + StatusCode = $code + Description = $result.Message + Headers = $result.Headers + FailureRedirect = [bool]$result.IsErrored } } - # assign the user to the session, and wire up a quick method - $WebEvent.Auth = @{} - $WebEvent.Auth.User = $result.User - $WebEvent.Auth.IsAuthenticated = $true - $WebEvent.Auth.Store = !$sessionless + # authentication was successful + return @{ + Success = $true + User = $result.User + Headers = $result.Headers + } + } + catch { + $_ | Write-PodeErrorLog + return @{ + Success = $false + StatusCode = 500 + Exception = $_ + } + } +} - # continue - return (Set-PodeAuthStatus -Headers $result.Headers -Success $auth.Success -LoginRoute:$loginRoute -Sessionless:$sessionless) +function Get-PodeAuthMiddlewareScript { + return { + param($opts) + + return Test-PodeAuthInternal ` + -Name $opts.Name ` + -Login:($opts.Login) ` + -Logout:($opts.Logout) ` + -AllowAnon:($opts.Anon) } } -function Get-PodeAuthWwwHeaderValue -{ +function Test-PodeAuthInternal { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [string] + $Name, + + [switch] + $Login, + + [switch] + $Logout, + + [switch] + $AllowAnon + ) + + # get the auth method + $auth = $PodeContext.Server.Authentications.Methods[$Name] + + # check for logout command + if ($Logout) { + Remove-PodeAuthSession + + if ($PodeContext.Server.Sessions.Info.UseHeaders) { + return Set-PodeAuthStatus ` + -StatusCode 401 ` + -Name $Name ` + -NoSuccessRedirect + } + else { + $auth.Failure.Url = (Protect-PodeValue -Value $auth.Failure.Url -Default $WebEvent.Request.Url.AbsolutePath) + return Set-PodeAuthStatus ` + -StatusCode 302 ` + -Name $Name ` + -NoSuccessRedirect + } + } + + # if the session already has a user/isAuth'd, then skip auth - or allow anon + if (Test-PodeSessionsInUse) { + # existing session auth'd + if (Test-PodeAuthUser) { + $WebEvent.Auth = $WebEvent.Session.Data.Auth + return Set-PodeAuthStatus ` + -Name $Name ` + -LoginRoute:($Login) ` + -NoSuccessRedirect + } + + # if we're allowing anon access, and using sessions, then stop here - as a session will be created from a login route for auth'ing users + if ($AllowAnon) { + if (!(Test-PodeIsEmpty $WebEvent.Session.Data.Auth)) { + Revoke-PodeSession + } + + return $true + } + } + + # check if the login flag is set, in which case just return and load a login get-page (allowing anon access) + if ($Login -and !$PodeContext.Server.Sessions.Info.UseHeaders -and ($WebEvent.Method -ieq 'get')) { + if (!(Test-PodeIsEmpty $WebEvent.Session.Data.Auth)) { + Revoke-PodeSession + } + + return $true + } + + try { + $result = Invoke-PodeAuthValidation -Name $Name + } + catch { + $_ | Write-PodeErrorLog + return Set-PodeAuthStatus ` + -StatusCode 500 ` + -Description $_.Exception.Message ` + -Name $Name + } + + # did the auth force a redirect? + if ($result.Redirected) { + return $false + } + + # if auth failed, are we allowing anon access? + if (!$result.Success -and $AllowAnon) { + return $true + } + + # if auth failed, set appropriate response headers/redirects + if (!$result.Success) { + return Set-PodeAuthStatus ` + -StatusCode $result.StatusCode ` + -Description $result.Description ` + -Headers $result.Headers ` + -Name $Name ` + -LoginRoute:$Login ` + -NoFailureRedirect:($result.FailureRedirect) + } + + # if auth passed, assign the user to the session + $WebEvent.Auth = [ordered]@{ + User = $result.User + IsAuthenticated = $true + IsAuthorised = $true + Store = !$auth.Sessionless + Name = $result.Auth + } + + # successful auth + $authName = $null + if ($auth.Merged -and !$auth.PassOne) { + $authName = $Name + } + else { + $authName = @($result.Auth)[0] + } + + return Set-PodeAuthStatus ` + -Headers $result.Headers ` + -Name $authName ` + -LoginRoute:$Login +} + +function Get-PodeAuthWwwHeaderValue { param( [Parameter()] [string] @@ -1257,8 +1439,7 @@ function Get-PodeAuthWwwHeaderValue return $header } -function Remove-PodeAuthSession -{ +function Remove-PodeAuthSession { # blank out the auth $WebEvent.Auth = @{} @@ -1271,35 +1452,131 @@ function Remove-PodeAuthSession Revoke-PodeSession } -function Set-PodeAuthStatus -{ - param ( +function Get-PodeAuthFailureInfo { + param( + [Parameter(Mandatory = $true)] + [string] + $Name, + [Parameter()] - [int] - $StatusCode = 0, + [hashtable] + $Info, [Parameter()] [string] - $Description, + $BaseName + ) + + # base name + if ([string]::IsNullOrEmpty($BaseName)) { + $BaseName = $Name + } + + # get auth method + $auth = $PodeContext.Server.Authentications.Methods[$Name] + + # cached failure? + if ($null -ne $auth.Cache.Failure) { + return $auth.Cache.Failure + } + + # find failure info + if ($null -eq $Info) { + $Info = @{ + Url = $auth.Failure.Url + Message = $auth.Failure.Message + } + } + + if ([string]::IsNullOrEmpty($Info.Url)) { + $Info.Url = $auth.Failure.Url + } + + if ([string]::IsNullOrEmpty($Info.Message)) { + $Info.Message = $auth.Failure.Message + } + + if ((![string]::IsNullOrEmpty($Info.Url) -and ![string]::IsNullOrEmpty($Info.Message)) -or [string]::IsNullOrEmpty($auth.Parent)) { + $PodeContext.Server.Authentications.Methods[$BaseName].Cache.Failure = $Info + return $Info + } + + return (Get-PodeAuthFailureInfo -Name $auth.Parent -Info $Info -BaseName $BaseName) +} + +function Get-PodeAuthSuccessInfo { + param( + [Parameter(Mandatory = $true)] + [string] + $Name, [Parameter()] [hashtable] - $Headers, + $Info, [Parameter()] - [hashtable] - $Failure, + [string] + $BaseName + ) + + # base name + if ([string]::IsNullOrEmpty($BaseName)) { + $BaseName = $Name + } + + # get auth method + $auth = $PodeContext.Server.Authentications.Methods[$Name] + + # cached success? + if ($null -ne $auth.Cache.Success) { + return $auth.Cache.Success + } + + # find success info + if ($null -eq $Info) { + $Info = @{ + Url = $auth.Success.Url + UseOrigin = $auth.Success.UseOrigin + } + } + + if ([string]::IsNullOrEmpty($Info.Url)) { + $Info.Url = $auth.Success.Url + } + + if (!$Info.UseOrigin) { + $Info.UseOrigin = $auth.Success.UseOrigin + } + + if ((![string]::IsNullOrEmpty($Info.Url) -and $Info.UseOrigin) -or [string]::IsNullOrEmpty($auth.Parent)) { + $PodeContext.Server.Authentications.Methods[$BaseName].Cache.Success = $Info + return $Info + } + + return (Get-PodeAuthSuccessInfo -Name $auth.Parent -Info $Info -BaseName $BaseName) +} + +function Set-PodeAuthStatus { + param( + [Parameter(Mandatory = $true)] + [string] + $Name, + + [Parameter()] + [int] + $StatusCode = 0, + + [Parameter()] + [string] + $Description, [Parameter()] [hashtable] - $Success, + $Headers, [switch] $LoginRoute, - [switch] - $Sessionless, - [switch] $NoSuccessRedirect, @@ -1309,28 +1586,40 @@ function Set-PodeAuthStatus # if we have any headers, set them if (($null -ne $Headers) -and ($Headers.Count -gt 0)) { - foreach ($name in $Headers.Keys) { - Set-PodeHeader -Name $name -Value $Headers[$name] + foreach ($key in $Headers.Keys) { + Set-PodeHeader -Name $key -Value $Headers[$key] } } + # get auth method + $auth = $PodeContext.Server.Authentications.Methods[$Name] + + # cookie redirect name + $redirectCookie = 'pode.redirecturl' + + # get Success object from auth + $success = Get-PodeAuthSuccessInfo -Name $Name + # if a statuscode supplied, assume failure if ($StatusCode -gt 0) { + # get Failure object from auth + $failure = Get-PodeAuthFailureInfo -Name $Name + # override description with the failureMessage if supplied - $Description = (Protect-PodeValue -Value $Failure.Message -Default $Description) + $Description = (Protect-PodeValue -Value $failure.Message -Default $Description) # add error to flash - if ($LoginRoute -and !$Sessionless -and ![string]::IsNullOrWhiteSpace($Description)) { + if ($LoginRoute -and !$auth.Sessionless -and ![string]::IsNullOrWhiteSpace($Description)) { Add-PodeFlashMessage -Name 'auth-error' -Message $Description } # check if we have a failure url redirect - if (!$NoFailureRedirect -and ![string]::IsNullOrWhiteSpace($Failure.Url)) { - if ($Success.UseOrigin -and ($WebEvent.Method -ieq 'get')) { - $null = Set-PodeCookie -Name 'pode.redirecturl' -Value $WebEvent.Request.Url.PathAndQuery + if (!$NoFailureRedirect -and ![string]::IsNullOrWhiteSpace($failure.Url)) { + if ($success.UseOrigin -and ($WebEvent.Method -ieq 'get')) { + $null = Set-PodeCookie -Name $redirectCookie -Value $WebEvent.Request.Url.PathAndQuery } - Move-PodeResponseUrl -Url $Failure.Url + Move-PodeResponseUrl -Url $failure.Url } else { Set-PodeResponseStatus -Code $StatusCode -Description $Description @@ -1340,11 +1629,12 @@ function Set-PodeAuthStatus } # if no statuscode, success, so check if we have a success url redirect (but only for auto-login routes) - if ((!$NoSuccessRedirect -or $LoginRoute) -and ![string]::IsNullOrWhiteSpace($Success.Url)) { - $url = $Success.Url - if ($Success.UseOrigin) { - $tmpUrl = Get-PodeCookieValue -Name 'pode.redirecturl' - Remove-PodeCookie -Name 'pode.redirecturl' + if ((!$NoSuccessRedirect -or $LoginRoute) -and ![string]::IsNullOrWhiteSpace($success.Url)) { + $url = $success.Url + + if ($success.UseOrigin) { + $tmpUrl = Get-PodeCookieValue -Name $redirectCookie + Remove-PodeCookie -Name $redirectCookie if (![string]::IsNullOrWhiteSpace($tmpUrl)) { $url = $tmpUrl @@ -1358,8 +1648,7 @@ function Set-PodeAuthStatus return $true } -function Get-PodeADServerFromDistinguishedName -{ +function Get-PodeADServerFromDistinguishedName { param( [Parameter()] [string] @@ -1382,8 +1671,7 @@ function Get-PodeADServerFromDistinguishedName return ($name -join '.') } -function Get-PodeAuthADResult -{ +function Get-PodeAuthADResult { param( [Parameter()] [string] @@ -1420,8 +1708,7 @@ function Get-PodeAuthADResult $KeepCredential ) - try - { + try { # validate the user's AD creds $result = (Open-PodeAuthADConnection -Server $Server -Domain $Domain -Username $Username -Password $Password -Provider $Provider) if (!$result.Success) { @@ -1444,33 +1731,32 @@ function Get-PodeAuthADResult } # check if we want to keep the credentials in the User object - if($KeepCredential){ - $credential = [pscredential]::new($($Domain+'\'+$Username), (ConvertTo-SecureString -String $Password -AsPlainText -Force)) + if ($KeepCredential) { + $credential = [pscredential]::new($($Domain + '\' + $Username), (ConvertTo-SecureString -String $Password -AsPlainText -Force)) } - else{ + else { $credential = $null } # return the user return @{ User = @{ - UserType = 'Domain' + UserType = 'Domain' AuthenticationType = 'LDAP' - DistinguishedName = $user.DistinguishedName - Username = ($Username -split '\\')[-1] - Name = $user.Name - Email = $user.Email - Fqdn = $Server - Domain = $Domain - Groups = $groups - Credential = $credential + DistinguishedName = $user.DistinguishedName + Username = ($Username -split '\\')[-1] + Name = $user.Name + Email = $user.Email + Fqdn = $Server + Domain = $Domain + Groups = $groups + Credential = $credential } } } finally { if ($null -ne $connection) { - switch ($Provider.ToLowerInvariant()) - { + switch ($Provider.ToLowerInvariant()) { 'openldap' { $connection.Username = $null $connection.Password = $null @@ -1489,10 +1775,9 @@ function Get-PodeAuthADResult } } -function Open-PodeAuthADConnection -{ +function Open-PodeAuthADConnection { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Server, @@ -1527,8 +1812,7 @@ function Open-PodeAuthADConnection $connection = $null # validate the user's AD creds - switch ($Provider.ToLowerInvariant()) - { + switch ($Provider.ToLowerInvariant()) { 'openldap' { if (![string]::IsNullOrWhiteSpace($SearchBase)) { $baseDn = $SearchBase @@ -1553,7 +1837,7 @@ function Open-PodeAuthADConnection $connection = @{ Hostname = $hostname Username = $user - BaseDN = $baseDn + BaseDN = $baseDn Password = $Password } } @@ -1592,15 +1876,14 @@ function Open-PodeAuthADConnection } return @{ - Success = $result + Success = $result Connection = $connection } } -function Get-PodeAuthADQuery -{ +function Get-PodeAuthADQuery { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Username ) @@ -1608,13 +1891,12 @@ function Get-PodeAuthADQuery return "(&(objectCategory=person)(samaccountname=$($Username)))" } -function Get-PodeAuthADUser -{ +function Get-PodeAuthADUser { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] $Connection, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Username, @@ -1628,8 +1910,7 @@ function Get-PodeAuthADUser $user = $null # generate query to find user - switch ($Provider.ToLowerInvariant()) - { + switch ($Provider.ToLowerInvariant()) { 'openldap' { $result = (ldapsearch -x -LLL -H "$($Connection.Hostname)" -D "$($Connection.Username)" -w "$($Connection.Password)" -b "$($Connection.BaseDN)" -o ldif-wrap=no "$($query)" name mail) if (!$? -or ($LASTEXITCODE -ne 0)) { @@ -1638,8 +1919,8 @@ function Get-PodeAuthADUser $user = @{ DistinguishedName = (Get-PodeOpenLdapValue -Lines $result -Property 'dn') - Name = (Get-PodeOpenLdapValue -Lines $result -Property 'name') - Email = (Get-PodeOpenLdapValue -Lines $result -Property 'mail') + Name = (Get-PodeOpenLdapValue -Lines $result -Property 'name') + Email = (Get-PodeOpenLdapValue -Lines $result -Property 'mail') } } @@ -1647,8 +1928,8 @@ function Get-PodeAuthADUser $result = Get-ADUser -LDAPFilter $query -Credential $Connection.Credential -Properties mail $user = @{ DistinguishedName = $result.DistinguishedName - Name = $result.Name - Email = $result.mail + Name = $result.Name + Email = $result.mail } } @@ -1663,8 +1944,8 @@ function Get-PodeAuthADUser $user = @{ DistinguishedName = @($result.distinguishedname)[0] - Name = @($result.name)[0] - Email = @($result.mail)[0] + Name = @($result.name)[0] + Email = @($result.mail)[0] } } } @@ -1672,8 +1953,7 @@ function Get-PodeAuthADUser return $user } -function Get-PodeOpenLdapValue -{ +function Get-PodeOpenLdapValue { param( [Parameter()] [string[]] @@ -1700,10 +1980,9 @@ function Get-PodeOpenLdapValue } } -function Get-PodeAuthADGroups -{ +function Get-PodeAuthADGroups { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] $Connection, [Parameter()] @@ -1730,10 +2009,9 @@ function Get-PodeAuthADGroups return (Get-PodeAuthADGroupsAll -Connection $Connection -DistinguishedName $DistinguishedName -Provider $Provider) } -function Get-PodeAuthADGroupsDirect -{ +function Get-PodeAuthADGroupsDirect { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] $Connection, [Parameter()] @@ -1751,8 +2029,7 @@ function Get-PodeAuthADGroupsDirect $groups = @() # get the groups - switch ($Provider.ToLowerInvariant()) - { + switch ($Provider.ToLowerInvariant()) { 'openldap' { $result = (ldapsearch -x -LLL -H "$($Connection.Hostname)" -D "$($Connection.Username)" -w "$($Connection.Password)" -b "$($Connection.BaseDN)" -o ldif-wrap=no "$($query)" memberof) $groups = (Get-PodeOpenLdapValue -Lines $result -Property 'memberof' -All) @@ -1773,18 +2050,17 @@ function Get-PodeAuthADGroupsDirect } $groups = @(foreach ($group in $groups) { - if ($group -imatch '^CN=(?.+?),') { - $Matches['group'] - } - }) + if ($group -imatch '^CN=(?.+?),') { + $Matches['group'] + } + }) return $groups } -function Get-PodeAuthADGroupsAll -{ +function Get-PodeAuthADGroupsAll { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] $Connection, [Parameter()] @@ -1802,8 +2078,7 @@ function Get-PodeAuthADGroupsAll $groups = @() # get the groups - switch ($Provider.ToLowerInvariant()) - { + switch ($Provider.ToLowerInvariant()) { 'openldap' { $result = (ldapsearch -x -LLL -H "$($Connection.Hostname)" -D "$($Connection.Username)" -w "$($Connection.Password)" -b "$($Connection.BaseDN)" -o ldif-wrap=no "$($query)" samaccountname) $groups = (Get-PodeOpenLdapValue -Lines $result -Property 'sAMAccountName' -All) @@ -1827,8 +2102,7 @@ function Get-PodeAuthADGroupsAll return $groups } -function Get-PodeAuthDomainName -{ +function Get-PodeAuthDomainName { if (Test-PodeIsUnix) { $dn = (dnsdomainname) if ([string]::IsNullOrWhiteSpace($dn)) { @@ -1847,32 +2121,18 @@ function Get-PodeAuthDomainName } } -function Find-PodeAuth -{ - param( - [Parameter(Mandatory=$true)] - [ValidateNotNullOrEmpty()] - [string] - $Name - ) - - return $PodeContext.Server.Authentications[$Name] -} - -function Test-PodeAuth -{ +function Find-PodeAuth { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $Name ) - return $PodeContext.Server.Authentications.ContainsKey($Name) + return $PodeContext.Server.Authentications.Methods[$Name] } -function Import-PodeAuthADModule -{ +function Import-PodeAuthADModule { if (!(Test-PodeIsWindows)) { throw 'Active Directory module only available on Windows' } @@ -1885,8 +2145,7 @@ function Import-PodeAuthADModule Export-PodeModule -Name ActiveDirectory } -function Get-PodeAuthADProvider -{ +function Get-PodeAuthADProvider { param( [switch] $OpenLDAP, diff --git a/src/Private/AutoImport.ps1 b/src/Private/AutoImport.ps1 index d2d9def93..a200cdbc8 100644 --- a/src/Private/AutoImport.ps1 +++ b/src/Private/AutoImport.ps1 @@ -1,11 +1,10 @@ -function Import-PodeFunctionsIntoRunspaceState -{ +function Import-PodeFunctionsIntoRunspaceState { param( - [Parameter(Mandatory=$true, ParameterSetName='Script')] + [Parameter(Mandatory = $true, ParameterSetName = 'Script')] [scriptblock] $ScriptBlock, - [Parameter(Mandatory=$true, ParameterSetName='File')] + [Parameter(Mandatory = $true, ParameterSetName = 'File')] [string] $FilePath ) @@ -52,8 +51,7 @@ function Import-PodeFunctionsIntoRunspaceState } } -function Import-PodeModulesIntoRunspaceState -{ +function Import-PodeModulesIntoRunspaceState { # do nothing if disabled if (!$PodeContext.Server.AutoImport.Modules.Enabled) { return @@ -72,8 +70,8 @@ function Import-PodeModulesIntoRunspaceState # work out which order the modules need to be loaded $modulesOrder = @(foreach ($module in $modules) { - Get-PodeModuleDependencies -Module $module - }) | + Get-PodeModuleDependencies -Module $module + }) | Where-Object { ($_.Name -inotin @('pode', 'pode.internal')) -and ($_.Name -inotlike 'microsoft.powershell.*') } | Select-Object -Unique @@ -97,8 +95,7 @@ function Import-PodeModulesIntoRunspaceState } } -function Import-PodeSnapinsIntoRunspaceState -{ +function Import-PodeSnapinsIntoRunspaceState { # if non-windows or core, do nothing if ((Test-PodeIsPSCore) -or (Test-PodeIsUnix)) { return @@ -127,28 +124,27 @@ function Import-PodeSnapinsIntoRunspaceState } } -function Initialize-PodeAutoImportConfiguration -{ +function Initialize-PodeAutoImportConfiguration { return @{ - Modules = @{ - Enabled = $true + Modules = @{ + Enabled = $true ExportList = @() ExportOnly = $false } - Snapins = @{ - Enabled = $true + Snapins = @{ + Enabled = $true ExportList = @() ExportOnly = $false } - Functions = @{ - Enabled = $true + Functions = @{ + Enabled = $true ExportList = @() ExportOnly = $false } SecretVaults = @{ - Enabled = $true + Enabled = $true SecretManagement = @{ - Enabled = $false + Enabled = $false ExportList = @() ExportOnly = $false } @@ -156,8 +152,7 @@ function Initialize-PodeAutoImportConfiguration } } -function Import-PodeSecretVaultsIntoRegistry -{ +function Import-PodeSecretVaultsIntoRegistry { # do nothing if disabled if (!$PodeContext.Server.AutoImport.SecretVaults.Enabled) { return @@ -166,8 +161,7 @@ function Import-PodeSecretVaultsIntoRegistry Import-PodeSecretManagementVaultsIntoRegistry } -function Import-PodeSecretManagementVaultsIntoRegistry -{ +function Import-PodeSecretManagementVaultsIntoRegistry { # do nothing if disabled if (!$PodeContext.Server.AutoImport.SecretVaults.SecretManagement.Enabled) { return @@ -203,22 +197,21 @@ function Import-PodeSecretManagementVaultsIntoRegistry # register the vault $PodeContext.Server.Secrets.Vaults[$vault.Name] = @{ - Name = $vault.Name - Type = 'secretmanagement' - Parameters = $vault.VaultParameters - AutoImported = $true - Unlock = $null - Cache = $null + Name = $vault.Name + Type = 'secretmanagement' + Parameters = $vault.VaultParameters + AutoImported = $true + Unlock = $null + Cache = $null SecretManagement = @{ - VaultName = $vault.Name + VaultName = $vault.Name ModuleName = $vault.ModulePath } } } } -function Read-PodeAutoImportConfiguration -{ +function Read-PodeAutoImportConfiguration { param( [Parameter()] [hashtable] @@ -231,25 +224,25 @@ function Read-PodeAutoImportConfiguration $impSecretVaults = $Configuration.AutoImport.SecretVaults return @{ - Modules = @{ - Enabled = (($null -eq $impModules.Enable) -or [bool]$impModules.Enable) + Modules = @{ + Enabled = (($null -eq $impModules.Enable) -or [bool]$impModules.Enable) ExportList = @() ExportOnly = ([bool]$impModules.ExportOnly) } - Snapins = @{ - Enabled = (($null -eq $impSnapins.Enable) -or [bool]$impSnapins.Enable) + Snapins = @{ + Enabled = (($null -eq $impSnapins.Enable) -or [bool]$impSnapins.Enable) ExportList = @() ExportOnly = ([bool]$impSnapins.ExportOnly) } - Functions = @{ - Enabled = (($null -eq $impFuncs.Enable) -or [bool]$impFuncs.Enable) + Functions = @{ + Enabled = (($null -eq $impFuncs.Enable) -or [bool]$impFuncs.Enable) ExportList = @() ExportOnly = ([bool]$impFuncs.ExportOnly) } SecretVaults = @{ - Enabled = (($null -eq $impSecretVaults.Enable) -or [bool]$impSecretVaults.Enable) + Enabled = (($null -eq $impSecretVaults.Enable) -or [bool]$impSecretVaults.Enable) SecretManagement = @{ - Enabled = ((($null -eq $impSecretVaults.Enable) -and (Test-PodeModuleInstalled -Name Microsoft.PowerShell.SecretManagement)) -or [bool]$impSecretVaults.Enable) + Enabled = ((($null -eq $impSecretVaults.Enable) -and (Test-PodeModuleInstalled -Name Microsoft.PowerShell.SecretManagement)) -or [bool]$impSecretVaults.Enable) ExportList = @() ExportOnly = ([bool]$impSecretVaults.SecretManagement.ExportOnly) } @@ -257,8 +250,7 @@ function Read-PodeAutoImportConfiguration } } -function Reset-PodeAutoImportConfiguration -{ +function Reset-PodeAutoImportConfiguration { $PodeContext.Server.AutoImport.Modules.ExportList = @() $PodeContext.Server.AutoImport.Snapins.ExportList = @() $PodeContext.Server.AutoImport.Functions.ExportList = @() diff --git a/src/Private/Context.ps1 b/src/Private/Context.ps1 index 515577feb..cacdb4cbe 100644 --- a/src/Private/Context.ps1 +++ b/src/Private/Context.ps1 @@ -1,9 +1,8 @@ using namespace Pode -function New-PodeContext -{ +function New-PodeContext { [CmdletBinding()] - param ( + param( [Parameter()] [scriptblock] $ScriptBlock, @@ -101,24 +100,24 @@ function New-PodeContext # list of timers/schedules/tasks/fim $ctx.Timers = @{ Enabled = ($EnablePool -icontains 'timers') - Items = @{} + Items = @{} } $ctx.Schedules = @{ - Enabled = ($EnablePool -icontains 'schedules') - Items = @{} + Enabled = ($EnablePool -icontains 'schedules') + Items = @{} Processes = @{} } $ctx.Tasks = @{ Enabled = ($EnablePool -icontains 'tasks') - Items = @{} + Items = @{} Results = @{} } $ctx.Fim = @{ Enabled = ($EnablePool -icontains 'files') - Items = @{} + Items = @{} } # auto importing (modules, funcs, snap-ins) @@ -127,28 +126,28 @@ function New-PodeContext # basic logging setup $ctx.Server.Logging = @{ Enabled = $true - Types = @{} + Types = @{} } # set thread counts $ctx.Threads = @{ - General = $Threads - Schedules = 10 - Files = 1 - Tasks = 2 + General = $Threads + Schedules = 10 + Files = 1 + Tasks = 2 WebSockets = 2 } # set socket details for pode server $ctx.Server.Sockets = @{ - Ssl = @{ + Ssl = @{ Protocols = Get-PodeDefaultSslProtocols } ReceiveTimeout = 100 } $ctx.Server.Signals = @{ - Enabled = $false + Enabled = $false Listener = $null } @@ -160,7 +159,7 @@ function New-PodeContext # set default request config $ctx.Server.Request = @{ - Timeout = 30 + Timeout = 30 BodySize = 100MB } @@ -208,11 +207,11 @@ function New-PodeContext # set iis token/settings $ctx.Server.IIS = @{ - Token = $env:ASPNETCORE_TOKEN - Port = $env:ASPNETCORE_PORT - Path = @{ - Raw = '/' - Pattern = '^/' + Token = $env:ASPNETCORE_TOKEN + Port = $env:ASPNETCORE_PORT + Path = @{ + Raw = '/' + Pattern = '^/' IsNonRoot = $false } Shutdown = $false @@ -262,11 +261,11 @@ function New-PodeContext # view engine for rendering pages $ctx.Server.ViewEngine = @{ - Type = 'html' - Extension = 'html' - ScriptBlock = $null + Type = 'html' + Extension = 'html' + ScriptBlock = $null UsingVariables = $null - IsDynamic = $false + IsDynamic = $false } # pode default preferences @@ -278,19 +277,19 @@ function New-PodeContext # routes for pages and api $ctx.Server.Routes = @{ - 'connect' = [ordered]@{} - 'delete' = [ordered]@{} - 'get' = [ordered]@{} - 'head' = [ordered]@{} - 'merge' = [ordered]@{} - 'options' = [ordered]@{} - 'patch' = [ordered]@{} - 'post' = [ordered]@{} - 'put' = [ordered]@{} - 'trace' = [ordered]@{} - 'static' = [ordered]@{} - 'signal' = [ordered]@{} - '*' = [ordered]@{} + 'connect' = [ordered]@{} + 'delete' = [ordered]@{} + 'get' = [ordered]@{} + 'head' = [ordered]@{} + 'merge' = [ordered]@{} + 'options' = [ordered]@{} + 'patch' = [ordered]@{} + 'post' = [ordered]@{} + 'put' = [ordered]@{} + 'trace' = [ordered]@{} + 'static' = [ordered]@{} + 'signal' = [ordered]@{} + '*' = [ordered]@{} } # verbs for tcp @@ -314,18 +313,18 @@ function New-PodeContext # setup basic access placeholders $ctx.Server.Access = @{ Allow = @{} - Deny = @{} + Deny = @{} } # setup basic limit rules $ctx.Server.Limits = @{ - Rules = @{} + Rules = @{} Active = @{} } # cookies and session logic $ctx.Server.Cookies = @{ - Csrf = @{} + Csrf = @{} Secrets = @{} } @@ -337,27 +336,33 @@ function New-PodeContext # server metrics $ctx.Metrics = @{ - Server = @{ + Server = @{ InitialLoadTime = [datetime]::UtcNow - StartTime = [datetime]::UtcNow - RestartCount = 0 + StartTime = [datetime]::UtcNow + RestartCount = 0 } Requests = @{ - Total = 0 + Total = 0 StatusCodes = @{} } - Signals = @{ + Signals = @{ Total = 0 } } - # authnetication methods - $ctx.Server.Authentications = @{} + # authentication and authorisation methods + $ctx.Server.Authentications = @{ + Methods = @{} + } + + $ctx.Server.Authorisations = @{ + Methods = @{} + } # create new cancellation tokens $ctx.Tokens = @{ Cancellation = New-Object System.Threading.CancellationTokenSource - Restart = New-Object System.Threading.CancellationTokenSource + Restart = New-Object System.Threading.CancellationTokenSource } # requests that should be logged @@ -377,15 +382,15 @@ function New-PodeContext # runspace pools $ctx.RunspacePools = @{ - Main = $null - Web = $null - Smtp = $null - Tcp = $null - Signals = $null - Schedules = $null - Gui = $null - Tasks = $null - Files = $null + Main = $null + Web = $null + Smtp = $null + Tcp = $null + Signals = $null + Schedules = $null + Gui = $null + Tasks = $null + Files = $null } # threading locks, etc. @@ -402,12 +407,13 @@ function New-PodeContext # setup events $ctx.Server.Events = @{ - Start = [ordered]@{} + Start = [ordered]@{} Terminate = [ordered]@{} - Restart = [ordered]@{} - Browser = [ordered]@{} - Crash = [ordered]@{} - Stop = [ordered]@{} + Restart = [ordered]@{} + Browser = [ordered]@{} + Crash = [ordered]@{} + Stop = [ordered]@{} + Running = [ordered]@{} } # modules @@ -415,9 +421,10 @@ function New-PodeContext # setup security $ctx.Server.Security = @{ - Headers = @{} - Cache = @{ - ContentSecurity = @{} + ServerDetails = $true + Headers = @{} + Cache = @{ + ContentSecurity = @{} PermissionsPolicy = @{} } } @@ -426,8 +433,7 @@ function New-PodeContext return $ctx } -function New-PodeRunspaceState -{ +function New-PodeRunspaceState { # create the state, and add the pode modules $state = [initialsessionstate]::CreateDefault() $state.ImportPSModule($PodeContext.Server.PodeModule.DataPath) @@ -449,19 +455,18 @@ function New-PodeRunspaceState $PodeContext.RunspaceState = $state } -function New-PodeRunspacePools -{ +function New-PodeRunspacePools { if ($PodeContext.Server.IsServerless) { return } # setup main runspace pool $threadsCounts = @{ - Default = 3 - Timer = 1 - Log = 1 + Default = 3 + Timer = 1 + Log = 1 Schedule = 1 - Misc = 1 + Misc = 1 } if (!(Test-PodeTimersExist)) { @@ -479,14 +484,14 @@ function New-PodeRunspacePools # main runspace - for timers, schedules, etc $totalThreadCount = ($threadsCounts.Values | Measure-Object -Sum).Sum $PodeContext.RunspacePools.Main = @{ - Pool = [runspacefactory]::CreateRunspacePool(1, $totalThreadCount, $PodeContext.RunspaceState, $Host) + Pool = [runspacefactory]::CreateRunspacePool(1, $totalThreadCount, $PodeContext.RunspaceState, $Host) State = 'Waiting' } # web runspace - if we have any http/s endpoints if (Test-PodeEndpoints -Type Http) { $PodeContext.RunspacePools.Web = @{ - Pool = [runspacefactory]::CreateRunspacePool(1, ($PodeContext.Threads.General + 1), $PodeContext.RunspaceState, $Host) + Pool = [runspacefactory]::CreateRunspacePool(1, ($PodeContext.Threads.General + 1), $PodeContext.RunspaceState, $Host) State = 'Waiting' } } @@ -494,7 +499,7 @@ function New-PodeRunspacePools # smtp runspace - if we have any smtp endpoints if (Test-PodeEndpoints -Type Smtp) { $PodeContext.RunspacePools.Smtp = @{ - Pool = [runspacefactory]::CreateRunspacePool(1, ($PodeContext.Threads.General + 1), $PodeContext.RunspaceState, $Host) + Pool = [runspacefactory]::CreateRunspacePool(1, ($PodeContext.Threads.General + 1), $PodeContext.RunspaceState, $Host) State = 'Waiting' } } @@ -502,7 +507,7 @@ function New-PodeRunspacePools # tcp runspace - if we have any tcp endpoints if (Test-PodeEndpoints -Type Tcp) { $PodeContext.RunspacePools.Tcp = @{ - Pool = [runspacefactory]::CreateRunspacePool(1, ($PodeContext.Threads.General + 1), $PodeContext.RunspaceState, $Host) + Pool = [runspacefactory]::CreateRunspacePool(1, ($PodeContext.Threads.General + 1), $PodeContext.RunspaceState, $Host) State = 'Waiting' } } @@ -510,7 +515,7 @@ function New-PodeRunspacePools # signals runspace - if we have any ws/s endpoints if (Test-PodeEndpoints -Type Ws) { $PodeContext.RunspacePools.Signals = @{ - Pool = [runspacefactory]::CreateRunspacePool(1, ($PodeContext.Threads.General + 2), $PodeContext.RunspaceState, $Host) + Pool = [runspacefactory]::CreateRunspacePool(1, ($PodeContext.Threads.General + 2), $PodeContext.RunspaceState, $Host) State = 'Waiting' } } @@ -518,7 +523,7 @@ function New-PodeRunspacePools # web socket connections runspace - for receiving data for external sockets if (Test-PodeWebSocketsExist) { $PodeContext.RunspacePools.WebSockets = @{ - Pool = [runspacefactory]::CreateRunspacePool(1, $PodeContext.Threads.WebSockets + 1, $PodeContext.RunspaceState, $Host) + Pool = [runspacefactory]::CreateRunspacePool(1, $PodeContext.Threads.WebSockets + 1, $PodeContext.RunspaceState, $Host) State = 'Waiting' } @@ -528,7 +533,7 @@ function New-PodeRunspacePools # setup schedule runspace pool -if we have any schedules if (Test-PodeSchedulesExist) { $PodeContext.RunspacePools.Schedules = @{ - Pool = [runspacefactory]::CreateRunspacePool(1, $PodeContext.Threads.Schedules, $PodeContext.RunspaceState, $Host) + Pool = [runspacefactory]::CreateRunspacePool(1, $PodeContext.Threads.Schedules, $PodeContext.RunspaceState, $Host) State = 'Waiting' } } @@ -536,7 +541,7 @@ function New-PodeRunspacePools # setup tasks runspace pool -if we have any tasks if (Test-PodeTasksExist) { $PodeContext.RunspacePools.Tasks = @{ - Pool = [runspacefactory]::CreateRunspacePool(1, $PodeContext.Threads.Tasks, $PodeContext.RunspaceState, $Host) + Pool = [runspacefactory]::CreateRunspacePool(1, $PodeContext.Threads.Tasks, $PodeContext.RunspaceState, $Host) State = 'Waiting' } } @@ -544,7 +549,7 @@ function New-PodeRunspacePools # setup files runspace pool -if we have any file watchers if (Test-PodeFileWatchersExist) { $PodeContext.RunspacePools.Files = @{ - Pool = [runspacefactory]::CreateRunspacePool(1, $PodeContext.Threads.Files + 1, $PodeContext.RunspaceState, $Host) + Pool = [runspacefactory]::CreateRunspacePool(1, $PodeContext.Threads.Files + 1, $PodeContext.RunspaceState, $Host) State = 'Waiting' } } @@ -552,7 +557,7 @@ function New-PodeRunspacePools # setup gui runspace pool (only for non-ps-core) - if gui enabled if (Test-PodeGuiEnabled) { $PodeContext.RunspacePools.Gui = @{ - Pool = [runspacefactory]::CreateRunspacePool(1, 1, $PodeContext.RunspaceState, $Host) + Pool = [runspacefactory]::CreateRunspacePool(1, 1, $PodeContext.RunspaceState, $Host) State = 'Waiting' } @@ -560,14 +565,13 @@ function New-PodeRunspacePools } } -function Open-PodeRunspacePools -{ +function Open-PodeRunspacePools { if ($PodeContext.Server.IsServerless) { return } $start = [datetime]::Now - Write-Verbose "Opening RunspacePools" + Write-Verbose 'Opening RunspacePools' # open pools async foreach ($key in $PodeContext.RunspacePools.Keys) { @@ -619,14 +623,13 @@ function Open-PodeRunspacePools Write-Verbose "RunspacePools opened [duration: $(([datetime]::Now - $start).TotalSeconds)s]" } -function Close-PodeRunspacePools -{ +function Close-PodeRunspacePools { if ($PodeContext.Server.IsServerless -or ($null -eq $PodeContext.RunspacePools)) { return } $start = [datetime]::Now - Write-Verbose "Closing RunspacePools" + Write-Verbose 'Closing RunspacePools' # close pools async foreach ($key in $PodeContext.RunspacePools.Keys) { @@ -686,31 +689,29 @@ function Close-PodeRunspacePools Write-Verbose "RunspacePools closed [duration: $(([datetime]::Now - $start).TotalSeconds)s]" } -function New-PodeStateContext -{ - param ( - [Parameter(Mandatory=$true)] +function New-PodeStateContext { + param( + [Parameter(Mandatory = $true)] [ValidateNotNull()] $Context ) return (New-Object -TypeName psobject | - Add-Member -MemberType NoteProperty -Name Threads -Value $Context.Threads -PassThru | - Add-Member -MemberType NoteProperty -Name Timers -Value $Context.Timers -PassThru | - Add-Member -MemberType NoteProperty -Name Schedules -Value $Context.Schedules -PassThru | - Add-Member -MemberType NoteProperty -Name Tasks -Value $Context.Tasks -PassThru | - Add-Member -MemberType NoteProperty -Name Fim -Value $Context.Fim -PassThru | - Add-Member -MemberType NoteProperty -Name RunspacePools -Value $Context.RunspacePools -PassThru | - Add-Member -MemberType NoteProperty -Name Tokens -Value $Context.Tokens -PassThru | - Add-Member -MemberType NoteProperty -Name Metrics -Value $Context.Metrics -PassThru | - Add-Member -MemberType NoteProperty -Name LogsToProcess -Value $Context.LogsToProcess -PassThru | - Add-Member -MemberType NoteProperty -Name Threading -Value $Context.Threading -PassThru | - Add-Member -MemberType NoteProperty -Name Server -Value $Context.Server -PassThru) + Add-Member -MemberType NoteProperty -Name Threads -Value $Context.Threads -PassThru | + Add-Member -MemberType NoteProperty -Name Timers -Value $Context.Timers -PassThru | + Add-Member -MemberType NoteProperty -Name Schedules -Value $Context.Schedules -PassThru | + Add-Member -MemberType NoteProperty -Name Tasks -Value $Context.Tasks -PassThru | + Add-Member -MemberType NoteProperty -Name Fim -Value $Context.Fim -PassThru | + Add-Member -MemberType NoteProperty -Name RunspacePools -Value $Context.RunspacePools -PassThru | + Add-Member -MemberType NoteProperty -Name Tokens -Value $Context.Tokens -PassThru | + Add-Member -MemberType NoteProperty -Name Metrics -Value $Context.Metrics -PassThru | + Add-Member -MemberType NoteProperty -Name LogsToProcess -Value $Context.LogsToProcess -PassThru | + Add-Member -MemberType NoteProperty -Name Threading -Value $Context.Threading -PassThru | + Add-Member -MemberType NoteProperty -Name Server -Value $Context.Server -PassThru) } -function Open-PodeConfiguration -{ - param ( +function Open-PodeConfiguration { + param( [Parameter()] [string] $ServerRoot = $null, @@ -742,8 +743,7 @@ function Open-PodeConfiguration return $config } -function Set-PodeServerConfiguration -{ +function Set-PodeServerConfiguration { param( [Parameter()] [hashtable] @@ -755,11 +755,11 @@ function Set-PodeServerConfiguration # file monitoring $Context.Server.FileMonitor = @{ - Enabled = ([bool]$Configuration.FileMonitor.Enable) - Exclude = (Convert-PodePathPatternsToRegex -Paths @($Configuration.FileMonitor.Exclude)) - Include = (Convert-PodePathPatternsToRegex -Paths @($Configuration.FileMonitor.Include)) + Enabled = ([bool]$Configuration.FileMonitor.Enable) + Exclude = (Convert-PodePathPatternsToRegex -Paths @($Configuration.FileMonitor.Exclude)) + Include = (Convert-PodePathPatternsToRegex -Paths @($Configuration.FileMonitor.Include)) ShowFiles = ([bool]$Configuration.FileMonitor.ShowFiles) - Files = @() + Files = @() } # logging @@ -767,9 +767,9 @@ function Set-PodeServerConfiguration Enabled = (($null -eq $Configuration.Logging.Enable) -or [bool]$Configuration.Logging.Enable) Masking = @{ Patterns = (Remove-PodeEmptyItemsFromArray -Array @($Configuration.Logging.Masking.Patterns)) - Mask = (Protect-PodeValue -Value $Configuration.Logging.Masking.Mask -Default '********') + Mask = (Protect-PodeValue -Value $Configuration.Logging.Masking.Mask -Default '********') } - Types = @{} + Types = @{} } # sockets @@ -782,7 +782,7 @@ function Set-PodeServerConfiguration } # auto-import - $Context.Server.AutoImport = Read-PodeAutoImportConfiguration + $Context.Server.AutoImport = Read-PodeAutoImportConfiguration -Configuration $Configuration # request if ([int]$Configuration.Request.Timeout -gt 0) { @@ -794,9 +794,8 @@ function Set-PodeServerConfiguration } } -function Set-PodeWebConfiguration -{ - param ( +function Set-PodeWebConfiguration { + param( [Parameter()] [hashtable] $Configuration, @@ -807,30 +806,30 @@ function Set-PodeWebConfiguration # setup the main web config $Context.Server.Web = @{ - Static = @{ + Static = @{ Defaults = $Configuration.Static.Defaults - Cache = @{ + Cache = @{ Enabled = [bool]$Configuration.Static.Cache.Enable - MaxAge = [int](Protect-PodeValue -Value $Configuration.Static.Cache.MaxAge -Default 3600) + MaxAge = [int](Protect-PodeValue -Value $Configuration.Static.Cache.MaxAge -Default 3600) Include = (Convert-PodePathPatternsToRegex -Paths @($Configuration.Static.Cache.Include) -NotSlashes) Exclude = (Convert-PodePathPatternsToRegex -Paths @($Configuration.Static.Cache.Exclude) -NotSlashes) } } - ErrorPages = @{ - ShowExceptions = [bool]$Configuration.ErrorPages.ShowExceptions + ErrorPages = @{ + ShowExceptions = [bool]$Configuration.ErrorPages.ShowExceptions StrictContentTyping = [bool]$Configuration.ErrorPages.StrictContentTyping - Default = $Configuration.ErrorPages.Default - Routes = @{} + Default = $Configuration.ErrorPages.Default + Routes = @{} } - ContentType = @{ + ContentType = @{ Default = $Configuration.ContentType.Default - Routes = @{} + Routes = @{} } TransferEncoding = @{ Default = $Configuration.TransferEncoding.Default - Routes = @{} + Routes = @{} } - Compression = @{ + Compression = @{ Enabled = [bool]$Configuration.Compression.Enable } } @@ -857,11 +856,10 @@ function Set-PodeWebConfiguration } } -function New-PodeAutoRestartServer -{ +function New-PodeAutoRestartServer { # don't configure if not supplied, or running as serverless $config = (Get-PodeConfig) - if (($null -eq $config) -or ($null -eq $config.Server.Restart) -or $PodeContext.Server.IsServerless) { + if (($null -eq $config) -or ($null -eq $config.Server.Restart) -or $PodeContext.Server.IsServerless) { return } @@ -899,8 +897,7 @@ function New-PodeAutoRestartServer } } -function Set-PodeOutputVariables -{ +function Set-PodeOutputVariables { if (Test-PodeIsEmpty $PodeContext.Server.Output.Variables) { return } diff --git a/src/Private/Cookies.ps1 b/src/Private/Cookies.ps1 index 7917cefae..982a00ba1 100644 --- a/src/Private/Cookies.ps1 +++ b/src/Private/Cookies.ps1 @@ -1,5 +1,4 @@ -function ConvertTo-PodeCookie -{ +function ConvertTo-PodeCookie { param( [Parameter()] [System.Net.Cookie] @@ -11,23 +10,22 @@ function ConvertTo-PodeCookie } return @{ - Name = $Cookie.Name - Value = $Cookie.Value - Expires = $Cookie.Expires - Expired = $Cookie.Expired - Discard = $Cookie.Discard - HttpOnly = $Cookie.HttpOnly - Secure = $Cookie.Secure - Path = $Cookie.Path + Name = $Cookie.Name + Value = $Cookie.Value + Expires = $Cookie.Expires + Expired = $Cookie.Expired + Discard = $Cookie.Discard + HttpOnly = $Cookie.HttpOnly + Secure = $Cookie.Secure + Path = $Cookie.Path TimeStamp = $Cookie.TimeStamp - Signed = $Cookie.Value.StartsWith('s:') + Signed = $Cookie.Value.StartsWith('s:') } } -function ConvertTo-PodeCookieString -{ +function ConvertTo-PodeCookieString { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] $Cookie ) diff --git a/src/Private/CronParser.ps1 b/src/Private/CronParser.ps1 index 190c405a5..55336397d 100644 --- a/src/Private/CronParser.ps1 +++ b/src/Private/CronParser.ps1 @@ -1,5 +1,4 @@ -function Get-PodeCronFields -{ +function Get-PodeCronFields { return @( 'Minute', 'Hour', @@ -9,10 +8,9 @@ function Get-PodeCronFields ) } -function Get-PodeCronFieldConstraints -{ +function Get-PodeCronFieldConstraints { return @{ - MinMax = @( + MinMax = @( @(0, 59), @(0, 23), @(1, 31), @@ -22,40 +20,38 @@ function Get-PodeCronFieldConstraints DaysInMonths = @( 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ) - Months = @( + Months = @( 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' ) } } -function Get-PodeCronPredefined -{ +function Get-PodeCronPredefined { return @{ # normal - '@minutely' = '* * * * *'; - '@hourly' = '0 * * * *'; - '@daily' = '0 0 * * *'; - '@weekly' = '0 0 * * 0'; - '@monthly' = '0 0 1 * *'; - '@quarterly' = '0 0 1 1,4,7,10 *'; - '@yearly' = '0 0 1 1 *'; - '@annually' = '0 0 1 1 *'; + '@minutely' = '* * * * *' + '@hourly' = '0 * * * *' + '@daily' = '0 0 * * *' + '@weekly' = '0 0 * * 0' + '@monthly' = '0 0 1 * *' + '@quarterly' = '0 0 1 1,4,7,10 *' + '@yearly' = '0 0 1 1 *' + '@annually' = '0 0 1 1 *' # twice - '@twice-hourly' = '0,30 * * * *'; - '@twice-daily' = '0 0,12 * * *'; - '@twice-weekly' = '0 0 * * 0,4'; - '@twice-monthly' = '0 0 1,15 * *'; - '@twice-yearly' = '0 0 1 1,6 *'; - '@twice-annually' = '0 0 1 1,6 *'; + '@twice-hourly' = '0,30 * * * *' + '@twice-daily' = '0 0,12 * * *' + '@twice-weekly' = '0 0 * * 0,4' + '@twice-monthly' = '0 0 1,15 * *' + '@twice-yearly' = '0 0 1 1,6 *' + '@twice-annually' = '0 0 1 1,6 *' } } -function Get-PodeCronFieldAliases -{ +function Get-PodeCronFieldAliases { return @{ - Month = @{ + Month = @{ Jan = 1 Feb = 2 Mar = 3 @@ -81,24 +77,22 @@ function Get-PodeCronFieldAliases } } -function ConvertFrom-PodeCronExpressions -{ - param ( - [Parameter(Mandatory=$true)] +function ConvertFrom-PodeCronExpressions { + param( + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string[]] $Expressions ) return @(@($Expressions) | ForEach-Object { - ConvertFrom-PodeCronExpression -Expression $_ - }) + ConvertFrom-PodeCronExpression -Expression $_ + }) } -function ConvertFrom-PodeCronExpression -{ - param ( - [Parameter(Mandatory=$true)] +function ConvertFrom-PodeCronExpression { + param( + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $Expression @@ -127,14 +121,13 @@ function ConvertFrom-PodeCronExpression $aliases = Get-PodeCronFieldAliases $cron = @{} - for ($i = 0; $i -lt $atoms.Length; $i++) - { + for ($i = 0; $i -lt $atoms.Length; $i++) { $_cronExp = @{ - Range = $null - Values = $null + Range = $null + Values = $null Constraints = $null - Random = $false - WildCard = $false + Random = $false + WildCard = $false } $_atom = $atoms[$i] @@ -253,8 +246,7 @@ function ConvertFrom-PodeCronExpression } # post validation for month/days in month - if (($null -ne $cron['Month'].Values) -and ($null -ne $cron['DayOfMonth'].Values)) - { + if (($null -ne $cron['Month'].Values) -and ($null -ne $cron['DayOfMonth'].Values)) { foreach ($mon in $cron['Month'].Values) { foreach ($day in $cron['DayOfMonth'].Values) { if ($day -gt $constraints.DaysInMonths[$mon - 1]) { @@ -271,23 +263,21 @@ function ConvertFrom-PodeCronExpression return $cron } -function Reset-PodeRandomCronExpressions -{ - param ( - [Parameter(Mandatory=$true)] +function Reset-PodeRandomCronExpressions { + param( + [Parameter(Mandatory = $true)] [ValidateNotNull()] $Expressions ) return @(@($Expressions) | ForEach-Object { - Reset-PodeRandomCronExpression -Expression $_ - }) + Reset-PodeRandomCronExpression -Expression $_ + }) } -function Reset-PodeRandomCronExpression -{ - param ( - [Parameter(Mandatory=$true)] +function Reset-PodeRandomCronExpression { + param( + [Parameter(Mandatory = $true)] [ValidateNotNull()] $Expression ) @@ -317,10 +307,9 @@ function Reset-PodeRandomCronExpression return $Expression } -function Test-PodeCronExpressions -{ - param ( - [Parameter(Mandatory=$true)] +function Test-PodeCronExpressions { + param( + [Parameter(Mandatory = $true)] [ValidateNotNull()] $Expressions, @@ -329,14 +318,13 @@ function Test-PodeCronExpressions ) return ((@($Expressions) | Where-Object { - Test-PodeCronExpression -Expression $_ -DateTime $DateTime - } | Measure-Object).Count -gt 0) + Test-PodeCronExpression -Expression $_ -DateTime $DateTime + } | Measure-Object).Count -gt 0) } -function Test-PodeCronExpression -{ - param ( - [Parameter(Mandatory=$true)] +function Test-PodeCronExpression { + param( + [Parameter(Mandatory = $true)] [ValidateNotNull()] $Expression, @@ -386,10 +374,9 @@ function Test-PodeCronExpression return $true } -function Get-PodeCronNextEarliestTrigger -{ +function Get-PodeCronNextEarliestTrigger { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [ValidateNotNull()] $Expressions, @@ -401,14 +388,13 @@ function Get-PodeCronNextEarliestTrigger ) return (@($Expressions) | Foreach-Object { - Get-PodeCronNextTrigger -Expression $_ -StartTime $StartTime -EndTime $EndTime - } | Where-Object { $null -ne $_ } | Sort-Object | Select-Object -First 1) + Get-PodeCronNextTrigger -Expression $_ -StartTime $StartTime -EndTime $EndTime + } | Where-Object { $null -ne $_ } | Sort-Object | Select-Object -First 1) } -function Get-PodeCronNextTrigger -{ +function Get-PodeCronNextTrigger { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [ValidateNotNull()] $Expression, @@ -448,8 +434,7 @@ function Get-PodeCronNextTrigger } # loop until we get a date - while ($true) - { + while ($true) { # check the minute if (!$Expression.Minute.WildCard) { $minute = Get-ClosestValue -AtomContraint $Expression.Minute -NowValue $NextTime.Minute diff --git a/src/Private/Cryptography.ps1 b/src/Private/Cryptography.ps1 index d1d5b5c26..99a90e34c 100644 --- a/src/Private/Cryptography.ps1 +++ b/src/Private/Cryptography.ps1 @@ -1,16 +1,15 @@ -function Invoke-PodeHMACSHA256Hash -{ - [CmdletBinding(DefaultParameterSetName='String')] +function Invoke-PodeHMACSHA256Hash { + [CmdletBinding(DefaultParameterSetName = 'String')] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Value, - [Parameter(Mandatory=$true, ParameterSetName='String')] + [Parameter(Mandatory = $true, ParameterSetName = 'String')] [string] $Secret, - [Parameter(Mandatory=$true, ParameterSetName='Bytes')] + [Parameter(Mandatory = $true, ParameterSetName = 'Bytes')] [byte[]] $SecretBytes ) @@ -20,26 +19,25 @@ function Invoke-PodeHMACSHA256Hash } if ($SecretBytes.Length -eq 0) { - throw "No secret supplied for HMAC256 hash" + throw 'No secret supplied for HMAC256 hash' } $crypto = [System.Security.Cryptography.HMACSHA256]::new($SecretBytes) return [System.Convert]::ToBase64String($crypto.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($Value))) } -function Invoke-PodeHMACSHA384Hash -{ - [CmdletBinding(DefaultParameterSetName='String')] +function Invoke-PodeHMACSHA384Hash { + [CmdletBinding(DefaultParameterSetName = 'String')] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Value, - [Parameter(Mandatory=$true, ParameterSetName='String')] + [Parameter(Mandatory = $true, ParameterSetName = 'String')] [string] $Secret, - [Parameter(Mandatory=$true, ParameterSetName='Bytes')] + [Parameter(Mandatory = $true, ParameterSetName = 'Bytes')] [byte[]] $SecretBytes ) @@ -49,26 +47,25 @@ function Invoke-PodeHMACSHA384Hash } if ($SecretBytes.Length -eq 0) { - throw "No secret supplied for HMAC384 hash" + throw 'No secret supplied for HMAC384 hash' } $crypto = [System.Security.Cryptography.HMACSHA384]::new($SecretBytes) return [System.Convert]::ToBase64String($crypto.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($Value))) } -function Invoke-PodeHMACSHA512Hash -{ - [CmdletBinding(DefaultParameterSetName='String')] +function Invoke-PodeHMACSHA512Hash { + [CmdletBinding(DefaultParameterSetName = 'String')] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Value, - [Parameter(Mandatory=$true, ParameterSetName='String')] + [Parameter(Mandatory = $true, ParameterSetName = 'String')] [string] $Secret, - [Parameter(Mandatory=$true, ParameterSetName='Bytes')] + [Parameter(Mandatory = $true, ParameterSetName = 'Bytes')] [byte[]] $SecretBytes ) @@ -78,17 +75,16 @@ function Invoke-PodeHMACSHA512Hash } if ($SecretBytes.Length -eq 0) { - throw "No secret supplied for HMAC512 hash" + throw 'No secret supplied for HMAC512 hash' } $crypto = [System.Security.Cryptography.HMACSHA512]::new($SecretBytes) return [System.Convert]::ToBase64String($crypto.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($Value))) } -function Invoke-PodeSHA256Hash -{ - param ( - [Parameter(Mandatory=$true)] +function Invoke-PodeSHA256Hash { + param( + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $Value @@ -98,10 +94,9 @@ function Invoke-PodeSHA256Hash return [System.Convert]::ToBase64String($crypto.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($Value))) } -function Invoke-PodeSHA1Hash -{ - param ( - [Parameter(Mandatory=$true)] +function Invoke-PodeSHA1Hash { + param( + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $Value @@ -111,14 +106,13 @@ function Invoke-PodeSHA1Hash return [System.Convert]::ToBase64String($crypto.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($Value))) } -function ConvertTo-PodeBase64Auth -{ +function ConvertTo-PodeBase64Auth { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Username, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Password ) @@ -126,10 +120,9 @@ function ConvertTo-PodeBase64Auth return [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes("$($Username):$($Password)")) } -function Invoke-PodeMD5Hash -{ - param ( - [Parameter(Mandatory=$true)] +function Invoke-PodeMD5Hash { + param( + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $Value @@ -139,25 +132,23 @@ function Invoke-PodeMD5Hash return [System.BitConverter]::ToString($crypto.ComputeHash([System.Text.Encoding]::ASCII.GetBytes($Value))).Replace('-', '').ToLowerInvariant() } -function Get-PodeRandomBytes -{ - param ( +function Get-PodeRandomBytes { + param( [Parameter()] [int] $Length = 16 ) return (Use-PodeStream -Stream ([System.Security.Cryptography.RandomNumberGenerator]::Create()) { - param($p) - $bytes = [byte[]]::new($Length) - $p.GetBytes($bytes) - return $bytes - }) + param($p) + $bytes = [byte[]]::new($Length) + $p.GetBytes($bytes) + return $bytes + }) } -function New-PodeSalt -{ - param ( +function New-PodeSalt { + param( [Parameter()] [int] $Length = 8 @@ -167,9 +158,8 @@ function New-PodeSalt return [System.Convert]::ToBase64String($bytes) } -function New-PodeGuid -{ - param ( +function New-PodeGuid { + param( [Parameter()] [int] $Length = 16, @@ -199,15 +189,14 @@ function New-PodeGuid return $guid } -function Invoke-PodeValueSign -{ - param ( - [Parameter(Mandatory=$true, ValueFromPipeline=$true)] +function Invoke-PodeValueSign { + param( + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [ValidateNotNullOrEmpty()] [string] $Value, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $Secret @@ -216,15 +205,14 @@ function Invoke-PodeValueSign return "s:$($Value).$(Invoke-PodeHMACSHA256Hash -Value $Value -Secret $Secret)" } -function Invoke-PodeValueUnsign -{ - param ( - [Parameter(Mandatory=$true, ValueFromPipeline=$true)] +function Invoke-PodeValueUnsign { + param( + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [ValidateNotNullOrEmpty()] [string] $Value, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $Secret @@ -253,40 +241,13 @@ function Invoke-PodeValueUnsign return $raw } -function Test-PodeJwt -{ - param( - [Parameter(Mandatory=$true)] - [pscustomobject] - $Payload - ) - - $now = [datetime]::Now - $unixStart = [datetime]::new(1970, 1, 1) - - # validate expiry - if (![string]::IsNullOrWhiteSpace($Payload.exp)) { - if ($now -gt $unixStart.AddSeconds($Payload.exp)) { - throw "The JWT has expired" - } - } - - # validate not-before - if (![string]::IsNullOrWhiteSpace($Payload.nbf)) { - if ($now -lt $unixStart.AddSeconds($Payload.nbf)) { - throw "The JWT is not yet valid for use" - } - } -} - -function New-PodeJwtSignature -{ +function New-PodeJwtSignature { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Algorithm, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Token, @@ -296,11 +257,11 @@ function New-PodeJwtSignature ) if (($Algorithm -ine 'none') -and (($null -eq $SecretBytes) -or ($SecretBytes.Length -eq 0))) { - throw "No Secret supplied for JWT signature" + throw 'No Secret supplied for JWT signature' } if (($Algorithm -ieq 'none') -and (($null -ne $secretBytes) -and ($SecretBytes.Length -gt 0))) { - throw "Expected no secret to be supplied for no signature" + throw 'Expected no secret to be supplied for no signature' } $sig = $null @@ -333,10 +294,9 @@ function New-PodeJwtSignature return $sig } -function ConvertTo-PodeBase64UrlValue -{ +function ConvertTo-PodeBase64UrlValue { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Value, @@ -355,10 +315,9 @@ function ConvertTo-PodeBase64UrlValue return $Value } -function ConvertFrom-PodeJwtBase64Value -{ +function ConvertFrom-PodeJwtBase64Value { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Value ) @@ -369,9 +328,17 @@ function ConvertFrom-PodeJwtBase64Value # add padding switch ($Value.Length % 4) { - 1 { $Value = $Value.Substring(0, $Value.Length - 1) } - 2 { $Value += '==' } - 3 { $Value += '=' } + 1 { + $Value = $Value.Substring(0, $Value.Length - 1) + } + + 2 { + $Value += '==' + } + + 3 { + $Value += '=' + } } # convert base64 to string @@ -379,7 +346,7 @@ function ConvertFrom-PodeJwtBase64Value $Value = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($Value)) } catch { - throw "Invalid Base64 encoded value found in JWT" + throw 'Invalid Base64 encoded value found in JWT' } # return json @@ -387,6 +354,6 @@ function ConvertFrom-PodeJwtBase64Value return ($Value | ConvertFrom-Json) } catch { - throw "Invalid JSON value found in JWT" + throw 'Invalid JSON value found in JWT' } } \ No newline at end of file diff --git a/src/Private/Endpoints.ps1 b/src/Private/Endpoints.ps1 index 33b624ca7..61e2f1e15 100644 --- a/src/Private/Endpoints.ps1 +++ b/src/Private/Endpoints.ps1 @@ -1,5 +1,4 @@ -function Find-PodeEndpoints -{ +function Find-PodeEndpoints { param( [Parameter()] [ValidateSet('', 'Http', 'Https')] @@ -21,8 +20,8 @@ function Find-PodeEndpoints if ([string]::IsNullOrWhiteSpace($EndpointName)) { $endpoints += @{ Protocol = $Protocol - Address = $Address - Name = [string]::Empty + Address = $Address + Name = [string]::Empty } } @@ -33,8 +32,8 @@ function Find-PodeEndpoints if ($null -ne $_endpoint) { $endpoints += @{ Protocol = $_endpoint.Protocol - Address = $_endpoint.RawAddress - Name = $name + Address = $_endpoint.RawAddress + Name = $name } } } @@ -51,10 +50,9 @@ function Find-PodeEndpoints return $endpoints } -function Get-PodeEndpoints -{ +function Get-PodeEndpoints { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [ValidateSet('Http', 'Ws', 'Smtp', 'Tcp')] [string[]] $Type @@ -85,10 +83,9 @@ function Get-PodeEndpoints return $endpoints } -function Test-PodeEndpointProtocol -{ +function Test-PodeEndpointProtocol { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [ValidateSet('Http', 'Https', 'Ws', 'Wss', 'Smtp', 'Smtps', 'Tcp', 'Tcps')] [string] $Protocol @@ -98,8 +95,7 @@ function Test-PodeEndpointProtocol return ($null -ne $endpoint) } -function Get-PodeEndpointType -{ +function Get-PodeEndpointType { param( [Parameter()] [ValidateSet('Http', 'Https', 'Smtp', 'Smtps', 'Tcp', 'Tcps', 'Ws', 'Wss')] @@ -108,16 +104,29 @@ function Get-PodeEndpointType ) switch ($Protocol) { - { $_ -iin @('http', 'https') } { 'Http' } - { $_ -iin @('ws', 'wss') } { 'Ws' } - { $_ -iin @('smtp', 'smtps') } { 'Smtp' } - { $_ -iin @('tcp', 'tcps') } { 'Tcp' } - default { $Protocol } + { $_ -iin @('http', 'https') } { + 'Http' + } + + { $_ -iin @('ws', 'wss') } { + 'Ws' + } + + { $_ -iin @('smtp', 'smtps') } { + 'Smtp' + } + + { $_ -iin @('tcp', 'tcps') } { + 'Tcp' + } + + default { + $Protocol + } } } -function Get-PodeEndpointRunspacePoolName -{ +function Get-PodeEndpointRunspacePoolName { param( [Parameter()] [ValidateSet('Http', 'Https', 'Smtp', 'Smtps', 'Tcp', 'Tcps', 'Ws', 'Wss')] @@ -126,18 +135,31 @@ function Get-PodeEndpointRunspacePoolName ) switch ($Protocol) { - { $_ -iin @('http', 'https') } { 'Web' } - { $_ -iin @('ws', 'wss') } { 'Signals' } - { $_ -iin @('smtp', 'smtps') } { 'Smtp' } - { $_ -iin @('tcp', 'tcps') } { 'Tcp' } - default { $Protocol } + { $_ -iin @('http', 'https') } { + 'Web' + } + + { $_ -iin @('ws', 'wss') } { + 'Signals' + } + + { $_ -iin @('smtp', 'smtps') } { + 'Smtp' + } + + { $_ -iin @('tcp', 'tcps') } { + 'Tcp' + } + + default { + $Protocol + } } } -function Test-PodeEndpoints -{ +function Test-PodeEndpoints { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [ValidateSet('Http', 'Ws', 'Smtp', 'Tcp')] [string] $Type @@ -148,8 +170,7 @@ function Test-PodeEndpoints } -function Find-PodeEndpointName -{ +function Find-PodeEndpointName { param( [Parameter()] [string] @@ -206,11 +227,11 @@ function Find-PodeEndpointName # try and find endpoint for address $key = @(foreach ($k in $PodeContext.Server.EndpointsMap.Keys) { - if ($k -imatch $key) { - $k - break - } - })[0] + if ($k -imatch $key) { + $k + break + } + })[0] if (![string]::IsNullOrWhiteSpace($key) -and $PodeContext.Server.EndpointsMap.ContainsKey($key)) { return $PodeContext.Server.EndpointsMap[$key] @@ -229,11 +250,11 @@ function Find-PodeEndpointName # try and find endpoint for local address $key = @(foreach ($k in $PodeContext.Server.EndpointsMap.Keys) { - if ($k -imatch $key) { - $k - break - } - })[0] + if ($k -imatch $key) { + $k + break + } + })[0] if (![string]::IsNullOrWhiteSpace($key) -and $PodeContext.Server.EndpointsMap.ContainsKey($key)) { return $PodeContext.Server.EndpointsMap[$key] @@ -249,11 +270,11 @@ function Find-PodeEndpointName # try and find endpoint for any address $key = @(foreach ($k in $PodeContext.Server.EndpointsMap.Keys) { - if ($k -imatch $key) { - $k - break - } - })[0] + if ($k -imatch $key) { + $k + break + } + })[0] if (![string]::IsNullOrWhiteSpace($key) -and $PodeContext.Server.EndpointsMap.ContainsKey($key)) { return $PodeContext.Server.EndpointsMap[$key] @@ -267,9 +288,8 @@ function Find-PodeEndpointName return $null } -function Get-PodeEndpointByName -{ - param ( +function Get-PodeEndpointByName { + param( [Parameter()] [string] $Name, diff --git a/src/Private/Endware.ps1 b/src/Private/Endware.ps1 index e5a829133..508842b0c 100644 --- a/src/Private/Endware.ps1 +++ b/src/Private/Endware.ps1 @@ -1,6 +1,5 @@ -function Invoke-PodeEndware -{ - param ( +function Invoke-PodeEndware { + param( [Parameter()] $Endware ) @@ -11,8 +10,7 @@ function Invoke-PodeEndware } # loop through each of the endware, invoking the next if it returns true - foreach ($eware in @($Endware)) - { + foreach ($eware in @($Endware)) { if (($null -eq $eware) -or ($null -eq $eware.Logic)) { continue } diff --git a/src/Private/Events.ps1 b/src/Private/Events.ps1 index 727f2a409..2025bb508 100644 --- a/src/Private/Events.ps1 +++ b/src/Private/Events.ps1 @@ -1,8 +1,7 @@ -function Invoke-PodeEvent -{ +function Invoke-PodeEvent { param( - [Parameter(Mandatory=$true)] - [ValidateSet('Start', 'Terminate', 'Restart', 'Browser', 'Crash', 'Stop')] + [Parameter(Mandatory = $true)] + [ValidateSet('Start', 'Terminate', 'Restart', 'Browser', 'Crash', 'Stop', 'Running')] [string] $Type ) diff --git a/src/Private/FileMonitor.ps1 b/src/Private/FileMonitor.ps1 index 46fbdd4d1..deee38e46 100644 --- a/src/Private/FileMonitor.ps1 +++ b/src/Private/FileMonitor.ps1 @@ -1,5 +1,4 @@ -function Start-PodeFileMonitor -{ +function Start-PodeFileMonitor { # don't configure if not supplied, or we're running as serverless if (!$PodeContext.Server.FileMonitor.Enabled -or $PodeContext.Server.IsServerless) { return @@ -11,8 +10,8 @@ function Start-PodeFileMonitor # setup the file monitor $watcher = New-Object System.IO.FileSystemWatcher $folder, $filter -Property @{ - IncludeSubdirectories = $true; - NotifyFilter = [System.IO.NotifyFilters]'FileName,LastWrite,CreationTime'; + IncludeSubdirectories = $true + NotifyFilter = [System.IO.NotifyFilters]'FileName,LastWrite,CreationTime' } $watcher.EnableRaisingEvents = $true @@ -24,7 +23,7 @@ function Start-PodeFileMonitor # setup the message data for the events $msgData = @{ - Timer = $timer + Timer = $timer Settings = $PodeContext.Server.FileMonitor } @@ -83,14 +82,13 @@ function Start-PodeFileMonitor $Event.MessageData.Tokens.Restart.Cancel() $Event.Sender.Stop() } -MessageData @{ - Tokens = $PodeContext.Tokens + Tokens = $PodeContext.Tokens FileSettings = $PodeContext.Server.FileMonitor - Quiet = $PodeContext.Server.Quiet + Quiet = $PodeContext.Server.Quiet } -SupportEvent } -function Stop-PodeFileMonitor -{ +function Stop-PodeFileMonitor { if ($PodeContext.Server.IsServerless) { return } @@ -103,10 +101,9 @@ function Stop-PodeFileMonitor } } -function Get-PodeFileMonitorName -{ - param ( - [Parameter(Mandatory=$true)] +function Get-PodeFileMonitorName { + param( + [Parameter(Mandatory = $true)] [ValidateSet('Create', 'Delete', 'Update')] [string] $Type @@ -115,7 +112,6 @@ function Get-PodeFileMonitorName return "PodeFileMonitor$($Type)" } -function Get-PodeFileMonitorTimerName -{ +function Get-PodeFileMonitorTimerName { return 'PodeFileMonitorTimer' } \ No newline at end of file diff --git a/src/Private/FileWatchers.ps1 b/src/Private/FileWatchers.ps1 index 0ad5b848b..7446ddb90 100644 --- a/src/Private/FileWatchers.ps1 +++ b/src/Private/FileWatchers.ps1 @@ -1,20 +1,17 @@ using namespace Pode -function Test-PodeFileWatchersExist -{ +function Test-PodeFileWatchersExist { return (($null -ne $PodeContext.Fim) -and (($PodeContext.Fim.Enabled) -or ($PodeContext.Fim.Items.Count -gt 0))) } -function New-PodeFileWatcher -{ +function New-PodeFileWatcher { $watcher = [PodeWatcher]::new($PodeContext.Tokens.Cancellation.Token) $watcher.ErrorLoggingEnabled = (Test-PodeErrorLoggingEnabled) $watcher.ErrorLoggingLevels = @(Get-PodeErrorLoggingLevels) return $watcher } -function Start-PodeFileWatcherRunspace -{ +function Start-PodeFileWatcherRunspace { if (!(Test-PodeFileWatchersExist)) { return } @@ -50,24 +47,20 @@ function Start-PodeFileWatcherRunspace $watchScript = { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] $Watcher, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [int] $ThreadId ) - try - { - while ($Watcher.IsConnected -and !$PodeContext.Tokens.Cancellation.IsCancellationRequested) - { + try { + while ($Watcher.IsConnected -and !$PodeContext.Tokens.Cancellation.IsCancellationRequested) { $evt = (Wait-PodeTask -Task $Watcher.GetFileEventAsync($PodeContext.Tokens.Cancellation.Token)) - try - { - try - { + try { + try { # get file watcher $fileWatcher = $PodeContext.Fim.Items[$evt.FileWatcher.Name] if ($null -eq $fileWatcher) { @@ -88,16 +81,16 @@ function Start-PodeFileWatcherRunspace # set file event object $FileEvent = @{ - Type = $evt.ChangeType - FullPath = $evt.FullPath - Name = $evt.Name - Old = @{ + Type = $evt.ChangeType + FullPath = $evt.FullPath + Name = $evt.Name + Old = @{ FullPath = $evt.OldFullPath - Name = $evt.OldName + Name = $evt.OldName } Parameters = @{} - Lockable = $PodeContext.Threading.Lockables.Global - Timestamp = [datetime]::UtcNow + Lockable = $PodeContext.Threading.Lockables.Global + Timestamp = [datetime]::UtcNow } # do we have any parameters? @@ -109,7 +102,8 @@ function Start-PodeFileWatcherRunspace $_args = @(Get-PodeScriptblockArguments -ArgumentList $fileWatcher.Arguments -UsingVariables $fileWatcher.UsingVariables) Invoke-PodeScriptBlock -ScriptBlock $fileWatcher.Script -Arguments $_args -Scoped -Splat } - catch [System.OperationCanceledException] {} + catch [System.OperationCanceledException] { + } catch { $_ | Write-PodeErrorLog $_.Exception | Write-PodeErrorLog -CheckInnerException @@ -121,7 +115,8 @@ function Start-PodeFileWatcherRunspace } } } - catch [System.OperationCanceledException] {} + catch [System.OperationCanceledException] { + } catch { $_ | Write-PodeErrorLog $_.Exception | Write-PodeErrorLog -CheckInnerException @@ -136,7 +131,7 @@ function Start-PodeFileWatcherRunspace # script to keep file watcher server alive until cancelled $waitScript = { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] $Watcher ) diff --git a/src/Private/Gui.ps1 b/src/Private/Gui.ps1 index b27a41f43..ec5592abf 100644 --- a/src/Private/Gui.ps1 +++ b/src/Private/Gui.ps1 @@ -1,5 +1,4 @@ -function Test-PodeGuiEnabled -{ +function Test-PodeGuiEnabled { return ($PodeContext.Server.Gui.Enabled -and !$PodeContext.Server.IsServerless -and !$PodeContext.Server.IsIIS -and @@ -17,7 +16,7 @@ function Start-PodeGuiRunspace { # if there are multiple endpoints, flag warning we're only using the first - unless explicitly set if ($null -eq $PodeContext.Server.Gui.Endpoint) { if ($PodeContext.Server.Endpoints.Values.Count -gt 1) { - Write-PodeHost "Multiple endpoints defined, only the first will be used for the GUI" -ForegroundColor Yellow + Write-PodeHost 'Multiple endpoints defined, only the first will be used for the GUI' -ForegroundColor Yellow } } @@ -52,7 +51,7 @@ function Start-PodeGuiRunspace { $null = [System.Reflection.Assembly]::LoadWithPartialName('PresentationCore') # Check for CefSharp - $loadCef = [bool]([AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.FullName.StartsWith("CefSharp.Wpf,") }) + $loadCef = [bool]([AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.FullName.StartsWith('CefSharp.Wpf,') }) # setup the WPF XAML for the server # Check for CefSharp and used Chromium based WPF if Modules exists @@ -117,11 +116,11 @@ function Start-PodeGuiRunspace { # get the browser object from XAML and navigate to base page if Cef is not loaded if (!$loadCef) { - $form.FindName("WebBrowser").Navigate($uri) + $form.FindName('WebBrowser').Navigate($uri) } # display the form - Write-PodeHost "Opening GUI" -ForegroundColor Yellow + Write-PodeHost 'Opening GUI' -ForegroundColor Yellow $null = $form.ShowDialog() Start-Sleep -Seconds 1 } diff --git a/src/Private/Helpers.ps1 b/src/Private/Helpers.ps1 index baefbede3..b9805ace1 100644 --- a/src/Private/Helpers.ps1 +++ b/src/Private/Helpers.ps1 @@ -1,10 +1,9 @@ using namespace Pode # read in the content from a dynamic pode file and invoke its content -function ConvertFrom-PodeFile -{ +function ConvertFrom-PodeFile { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [ValidateNotNull()] $Content, @@ -24,10 +23,9 @@ function ConvertFrom-PodeFile return (Invoke-PodeScriptBlock -ScriptBlock ([scriptblock]::Create($Content)) -Arguments $Data -Return) } -function Get-PodeViewEngineType -{ +function Get-PodeViewEngineType { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Path ) @@ -43,10 +41,9 @@ function Get-PodeViewEngineType return $type } -function Get-PodeFileContentUsingViewEngine -{ +function Get-PodeFileContentUsingViewEngine { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Path, @@ -62,8 +59,7 @@ function Get-PodeFileContentUsingViewEngine $content = [string]::Empty # run the relevant engine logic - switch ($engine.ToLowerInvariant()) - { + switch ($engine.ToLowerInvariant()) { 'html' { $content = Get-Content -Path $Path -Raw -Encoding utf8 } @@ -93,10 +89,9 @@ function Get-PodeFileContentUsingViewEngine return $content } -function Get-PodeFileContent -{ +function Get-PodeFileContent { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Path ) @@ -104,8 +99,7 @@ function Get-PodeFileContent return (Get-Content -Path $Path -Raw -Encoding utf8) } -function Get-PodeType -{ +function Get-PodeType { param( [Parameter()] $Value @@ -122,13 +116,11 @@ function Get-PodeType } } -function Get-PodePSVersionTable -{ +function Get-PodePSVersionTable { return $PSVersionTable } -function Test-PodeIsAdminUser -{ +function Test-PodeIsAdminUser { # check the current platform, if it's unix then return true if (Test-PodeIsUnix) { return $true @@ -149,10 +141,9 @@ function Test-PodeIsAdminUser } } -function Get-PodeHostIPRegex -{ +function Get-PodeHostIPRegex { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [ValidateSet('Both', 'Hostname', 'IP')] [string] $Type @@ -161,8 +152,7 @@ function Get-PodeHostIPRegex $ip_rgx = '\[[a-f0-9\:]+\]|((\d+\.){3}\d+)|\:\:\d*|\*|all' $host_rgx = '([a-z]|\*\.)(([a-z0-9]|[a-z0-9][a-z0-9\-]*[a-z0-9])\.)*([a-z0-9]|[a-z0-9][a-z0-9\-]*[a-z0-9])+' - switch ($Type.ToLowerInvariant()) - { + switch ($Type.ToLowerInvariant()) { 'both' { return "(?($($ip_rgx)|$($host_rgx)))" } @@ -177,13 +167,11 @@ function Get-PodeHostIPRegex } } -function Get-PortRegex -{ +function Get-PortRegex { return '(?\d+)' } -function Get-PodeEndpointInfo -{ +function Get-PodeEndpointInfo { param( [Parameter()] [string] @@ -235,8 +223,7 @@ function Get-PodeEndpointInfo } } -function Test-PodeIPAddress -{ +function Test-PodeIPAddress { param( [Parameter()] [string] @@ -263,8 +250,7 @@ function Test-PodeIPAddress } } -function Test-PodeHostname -{ +function Test-PodeHostname { param( [Parameter()] [string] @@ -274,10 +260,9 @@ function Test-PodeHostname return ($Hostname -imatch "^$(Get-PodeHostIPRegex -Type Hostname)$") } -function ConvertTo-PodeIPAddress -{ +function ConvertTo-PodeIPAddress { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [ValidateNotNull()] $Address ) @@ -285,14 +270,13 @@ function ConvertTo-PodeIPAddress return [System.Net.IPAddress]::Parse(([System.Net.IPEndPoint]$Address).Address.ToString()) } -function Get-PodeIPAddressesForHostname -{ +function Get-PodeIPAddressesForHostname { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Hostname, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [ValidateSet('All', 'IPv4', 'IPv6')] [string] $Type @@ -311,32 +295,30 @@ function Get-PodeIPAddressesForHostname } # return ips based on type - switch ($Type.ToLowerInvariant()) - { + switch ($Type.ToLowerInvariant()) { 'ipv4' { $ips = @(foreach ($ip in $ips) { - if ($ip.AddressFamily -ieq 'InterNetwork') { - $ip - } - }) + if ($ip.AddressFamily -ieq 'InterNetwork') { + $ip + } + }) } 'ipv6' { $ips = @(foreach ($ip in $ips) { - if ($ip.AddressFamily -ieq 'InterNetworkV6') { - $ip - } - }) + if ($ip.AddressFamily -ieq 'InterNetworkV6') { + $ip + } + }) } } return (@($ips)).IPAddressToString } -function Test-PodeIPAddressLocal -{ +function Test-PodeIPAddressLocal { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $IP ) @@ -344,10 +326,9 @@ function Test-PodeIPAddressLocal return (@('127.0.0.1', '::1', '[::1]', 'localhost') -icontains $IP) } -function Test-PodeIPAddressAny -{ +function Test-PodeIPAddressAny { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $IP ) @@ -355,10 +336,9 @@ function Test-PodeIPAddressAny return (@('0.0.0.0', '*', 'all', '::', '[::]') -icontains $IP) } -function Test-PodeIPAddressLocalOrAny -{ +function Test-PodeIPAddressLocalOrAny { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $IP ) @@ -366,8 +346,7 @@ function Test-PodeIPAddressLocalOrAny return ((Test-PodeIPAddressLocal -IP $IP) -or (Test-PodeIPAddressAny -IP $IP)) } -function Get-PodeIPAddress -{ +function Get-PodeIPAddress { param( [Parameter()] [string] @@ -398,16 +377,15 @@ function Get-PodeIPAddress return [System.Net.IPAddress]::Parse($IP) } -function Test-PodeIPAddressInRange -{ +function Test-PodeIPAddressInRange { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] $IP, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] $LowerIP, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] $UpperIP ) @@ -427,10 +405,9 @@ function Test-PodeIPAddressInRange return $valid } -function Test-PodeIPAddressIsSubnetMask -{ +function Test-PodeIPAddressIsSubnetMask { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $IP @@ -439,10 +416,9 @@ function Test-PodeIPAddressIsSubnetMask return (($IP -split '/').Length -gt 1) } -function Get-PodeSubnetRange -{ +function Get-PodeSubnetRange { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $SubnetMask @@ -458,7 +434,7 @@ function Get-PodeSubnetRange $bits = [int]$split[1] # generate the netmask - $network = @("", "", "", "") + $network = @('', '', '', '') $count = 0 foreach ($i in 0..3) { @@ -466,10 +442,10 @@ function Get-PodeSubnetRange $count++ if ($count -le $bits) { - $network[$i] += "1" + $network[$i] += '1' } else { - $network[$i] += "0" + $network[$i] += '0' } } } @@ -481,37 +457,36 @@ function Get-PodeSubnetRange # calculate the bottom range $bottom = @(foreach ($i in 0..3) { - [byte]([byte]$network[$i] -band [byte]$ip_parts[$i]) - }) + [byte]([byte]$network[$i] -band [byte]$ip_parts[$i]) + }) # calculate the range $range = @(foreach ($i in 0..3) { - 256 + (-bnot [byte]$network[$i]) - }) + 256 + (-bnot [byte]$network[$i]) + }) # calculate the top range $top = @(foreach ($i in 0..3) { - [byte]([byte]$ip_parts[$i] + [byte]$range[$i]) - }) + [byte]([byte]$ip_parts[$i] + [byte]$range[$i]) + }) return @{ - 'Lower' = ($bottom -join '.'); - 'Upper' = ($top -join '.'); - 'Range' = ($range -join '.'); - 'Netmask' = ($network -join '.'); - 'IP' = ($ip_parts -join '.'); + 'Lower' = ($bottom -join '.') + 'Upper' = ($top -join '.') + 'Range' = ($range -join '.') + 'Netmask' = ($network -join '.') + 'IP' = ($ip_parts -join '.') } } -function Add-PodeRunspace -{ +function Add-PodeRunspace { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [ValidateSet('Main', 'Signals', 'Schedules', 'Gui', 'Web', 'Smtp', 'Tcp', 'Tasks', 'WebSockets', 'Files')] [string] $Type, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [ValidateNotNull()] [scriptblock] $ScriptBlock, @@ -533,8 +508,7 @@ function Add-PodeRunspace $PassThru ) - try - { + try { # create powershell pipelines $ps = [powershell]::Create() $ps.RunspacePool = $PodeContext.RunspacePools[$Type].Pool @@ -571,17 +545,17 @@ function Add-PodeRunspace elseif ($PassThru) { return @{ Pipeline = $ps - Handler = $pipeline + Handler = $pipeline } } # or store it here for later clean-up else { $PodeContext.Runspaces += @{ - Pool = $Type + Pool = $Type Pipeline = $ps - Handler = $pipeline - Stopped = $false + Handler = $pipeline + Stopped = $false } } } @@ -591,10 +565,9 @@ function Add-PodeRunspace } } -function Open-PodeRunspace -{ +function Open-PodeRunspace { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Type ) @@ -615,8 +588,7 @@ function Open-PodeRunspace } } -function Close-PodeRunspaces -{ +function Close-PodeRunspaces { param( [switch] $ClosePool @@ -628,7 +600,7 @@ function Close-PodeRunspaces try { if (!(Test-PodeIsEmpty $PodeContext.Runspaces)) { - Write-Verbose "Waiting until all Listeners are disposed" + Write-Verbose 'Waiting until all Listeners are disposed' $count = 0 $continue = $false @@ -665,28 +637,28 @@ function Close-PodeRunspaces break } - Write-Verbose "All Listeners disposed" + Write-Verbose 'All Listeners disposed' # now dispose runspaces - Write-Verbose "Disposing Runspaces" + Write-Verbose 'Disposing Runspaces' $runspaceErrors = @(foreach ($item in $PodeContext.Runspaces) { - if ($item.Stopped) { - continue - } + if ($item.Stopped) { + continue + } - try { - # only do this, if the pool is in error - if ($PodeContext.RunspacePools[$item.Pool].State -ieq 'error') { - $item.Pipeline.EndInvoke($item.Handler) + try { + # only do this, if the pool is in error + if ($PodeContext.RunspacePools[$item.Pool].State -ieq 'error') { + $item.Pipeline.EndInvoke($item.Handler) + } + } + catch { + "$($item.Pool) runspace failed to load: $($_.Exception.InnerException.Message)" } - } - catch { - "$($item.Pool) runspace failed to load: $($_.Exception.InnerException.Message)" - } - Close-PodeDisposable -Disposable $item.Pipeline - $item.Stopped = $true - }) + Close-PodeDisposable -Disposable $item.Pipeline + $item.Stopped = $true + }) # dispose of schedule runspaces if ($PodeContext.Schedules.Processes.Count -gt 0) { @@ -703,7 +675,7 @@ function Close-PodeRunspaces } $PodeContext.Runspaces = @() - Write-Verbose "Runspaces disposed" + Write-Verbose 'Runspaces disposed' } # close/dispose the runspace pools @@ -731,8 +703,7 @@ function Close-PodeRunspaces } } -function Get-PodeConsoleKey -{ +function Get-PodeConsoleKey { if ([Console]::IsInputRedirected -or ![Console]::KeyAvailable) { return $null } @@ -740,8 +711,7 @@ function Get-PodeConsoleKey return [Console]::ReadKey($true) } -function Test-PodeTerminationPressed -{ +function Test-PodeTerminationPressed { param( [Parameter()] $Key = $null @@ -754,8 +724,7 @@ function Test-PodeTerminationPressed return (Test-PodeKeyPressed -Key $Key -Character 'c') } -function Test-PodeRestartPressed -{ +function Test-PodeRestartPressed { param( [Parameter()] $Key = $null @@ -764,8 +733,7 @@ function Test-PodeRestartPressed return (Test-PodeKeyPressed -Key $Key -Character 'r') } -function Test-PodeOpenBrowserPressed -{ +function Test-PodeOpenBrowserPressed { param( [Parameter()] $Key = $null @@ -774,13 +742,12 @@ function Test-PodeOpenBrowserPressed return (Test-PodeKeyPressed -Key $Key -Character 'b') } -function Test-PodeKeyPressed -{ +function Test-PodeKeyPressed { param( [Parameter()] $Key = $null, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Character ) @@ -793,8 +760,7 @@ function Test-PodeKeyPressed (($Key.Modifiers -band [ConsoleModifiers]::Control) -or ((Test-PodeIsUnix) -and ($Key.Modifiers -band [ConsoleModifiers]::Shift)))) } -function Close-PodeServerInternal -{ +function Close-PodeServerInternal { param( [switch] $ShowDoneMessage @@ -802,26 +768,26 @@ function Close-PodeServerInternal # ensure the token is cancelled if ($null -ne $PodeContext.Tokens.Cancellation) { - Write-Verbose "Cancelling main cancellation token" + Write-Verbose 'Cancelling main cancellation token' $PodeContext.Tokens.Cancellation.Cancel() } # stop all current runspaces - Write-Verbose "Closing runspaces" + Write-Verbose 'Closing runspaces' Close-PodeRunspaces -ClosePool # stop the file monitor if it's running - Write-Verbose "Stopping file monitor" + Write-Verbose 'Stopping file monitor' Stop-PodeFileMonitor try { # remove all the cancellation tokens - Write-Verbose "Disposing cancellation tokens" + Write-Verbose 'Disposing cancellation tokens' Close-PodeDisposable -Disposable $PodeContext.Tokens.Cancellation Close-PodeDisposable -Disposable $PodeContext.Tokens.Restart # dispose mutex/semaphores - Write-Verbose "Diposing mutex and semaphores" + Write-Verbose 'Diposing mutex and semaphores' Clear-PodeMutexes Clear-PodeSemaphores } @@ -830,18 +796,17 @@ function Close-PodeServerInternal } # remove all of the pode temp drives - Write-Verbose "Removing internal PSDrives" + Write-Verbose 'Removing internal PSDrives' Remove-PodePSDrives if ($ShowDoneMessage -and ($PodeContext.Server.Types.Length -gt 0) -and !$PodeContext.Server.IsServerless) { - Write-PodeHost " Done" -ForegroundColor Green + Write-PodeHost ' Done' -ForegroundColor Green } } -function New-PodePSDrive -{ +function New-PodePSDrive { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Path, @@ -884,10 +849,9 @@ function New-PodePSDrive return "$($drive.Name):$([System.IO.Path]::DirectorySeparatorChar)" } -function Get-PodePSDrive -{ +function Get-PodePSDrive { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name ) @@ -895,10 +859,9 @@ function Get-PodePSDrive return (Get-PSDrive -Name $Name -PSProvider FileSystem -Scope Global -ErrorAction Ignore) } -function Test-PodePSDrive -{ +function Test-PodePSDrive { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name, @@ -919,23 +882,20 @@ function Test-PodePSDrive return $true } -function Add-PodePSDrives -{ +function Add-PodePSDrives { foreach ($key in $PodeContext.Server.Drives.Keys) { $null = New-PodePSDrive -Path $PodeContext.Server.Drives[$key] -Name $key } } -function Import-PodeModules -{ +function Import-PodeModules { # import other modules in the session foreach ($path in $PodeContext.Server.Modules.Values) { $null = Import-Module $path -DisableNameChecking -Scope Global -ErrorAction Stop } } -function Add-PodePSInbuiltDrives -{ +function Add-PodePSInbuiltDrives { # create drive for views, if path exists $path = (Join-PodeServerRoot 'views') if (Test-Path $path) { @@ -955,15 +915,13 @@ function Add-PodePSInbuiltDrives } } -function Remove-PodePSDrives -{ +function Remove-PodePSDrives { $null = Get-PSDrive PodeDir* | Remove-PSDrive } -function Join-PodeServerRoot -{ +function Join-PodeServerRoot { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $Folder, @@ -986,10 +944,9 @@ function Join-PodeServerRoot return [System.IO.Path]::Combine($Root, $Folder, $FilePath) } -function Remove-PodeEmptyItemsFromArray -{ +function Remove-PodeEmptyItemsFromArray { param( - [Parameter(ValueFromPipeline=$true)] + [Parameter(ValueFromPipeline = $true)] $Array ) @@ -1000,10 +957,9 @@ function Remove-PodeEmptyItemsFromArray return @(@($Array -ne ([string]::Empty)) -ne $null) } -function Remove-PodeNullKeysFromHashtable -{ +function Remove-PodeNullKeysFromHashtable { param( - [Parameter(ValueFromPipeline=$true)] + [Parameter(ValueFromPipeline = $true)] [hashtable] $Hashtable ) @@ -1041,8 +997,7 @@ function Remove-PodeNullKeysFromHashtable } } -function Get-PodeFileExtension -{ +function Get-PodeFileExtension { param( [Parameter()] [string] @@ -1060,8 +1015,7 @@ function Get-PodeFileExtension return $ext } -function Get-PodeFileName -{ +function Get-PodeFileName { param( [Parameter()] [string] @@ -1078,8 +1032,7 @@ function Get-PodeFileName return [System.IO.Path]::GetFileName($Path) } -function Test-PodeValidNetworkFailure -{ +function Test-PodeValidNetworkFailure { param( [Parameter()] $Exception @@ -1093,18 +1046,17 @@ function Test-PodeValidNetworkFailure ) $match = @(foreach ($msg in $msgs) { - if ($Exception.Message -ilike $msg) { - $msg - } - })[0] + if ($Exception.Message -ilike $msg) { + $msg + } + })[0] return ($null -ne $match) } -function ConvertFrom-PodeHeaderQValue -{ +function ConvertFrom-PodeHeaderQValue { param( - [Parameter(ValueFromPipeline=$true)] + [Parameter(ValueFromPipeline = $true)] [string] $Value ) @@ -1135,8 +1087,7 @@ function ConvertFrom-PodeHeaderQValue return $qs } -function Get-PodeAcceptEncoding -{ +function Get-PodeAcceptEncoding { param( [Parameter()] [string] @@ -1222,8 +1173,7 @@ function Get-PodeAcceptEncoding return $found.Name } -function Get-PodeRanges -{ +function Get-PodeRanges { param( [Parameter()] [string] @@ -1277,8 +1227,7 @@ function Get-PodeRanges return $ranges } -function Get-PodeTransferEncoding -{ +function Get-PodeTransferEncoding { param( [Parameter()] [string] @@ -1333,8 +1282,7 @@ function Get-PodeTransferEncoding return [string]::Empty } -function Get-PodeEncodingFromContentType -{ +function Get-PodeEncodingFromContentType { param( [Parameter()] [string] @@ -1356,10 +1304,9 @@ function Get-PodeEncodingFromContentType return [System.Text.Encoding]::UTF8 } -function New-PodeRequestException -{ +function New-PodeRequestException { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [int] $StatusCode ) @@ -1369,10 +1316,9 @@ function New-PodeRequestException return $err } -function ConvertTo-PodeResponseContent -{ +function ConvertTo-PodeResponseContent { param( - [Parameter(ValueFromPipeline=$true)] + [Parameter(ValueFromPipeline = $true)] $InputObject, [Parameter()] @@ -1419,8 +1365,8 @@ function ConvertTo-PodeResponseContent { $_ -ilike '*/xml' } { if ($InputObject -isnot [string]) { $temp = @(foreach ($item in $InputObject) { - New-Object psobject -Property $item - }) + New-Object psobject -Property $item + }) return ($temp | ConvertTo-Xml -Depth $Depth -As String -NoTypeInformation) } @@ -1433,8 +1379,8 @@ function ConvertTo-PodeResponseContent { $_ -ilike '*/csv' } { if ($InputObject -isnot [string]) { $temp = @(foreach ($item in $InputObject) { - New-Object psobject -Property $item - }) + New-Object psobject -Property $item + }) if (Test-PodeIsPSCore) { $temp = ($temp | ConvertTo-Csv -Delimiter $Delimiter -IncludeTypeInformation:$false) @@ -1471,8 +1417,7 @@ function ConvertTo-PodeResponseContent return ([string]$InputObject) } -function ConvertFrom-PodeRequestContent -{ +function ConvertFrom-PodeRequestContent { param( [Parameter()] $Request, @@ -1491,7 +1436,7 @@ function ConvertFrom-PodeRequestContent # result object for data/files $Result = @{ - Data = @{} + Data = @{} Files = @{} } @@ -1617,8 +1562,7 @@ function ConvertFrom-PodeRequestContent return $Result } -function Split-PodeContentType -{ +function Split-PodeContentType { param( [Parameter()] [string] @@ -1632,8 +1576,7 @@ function Split-PodeContentType return @($ContentType -isplit ';')[0].Trim() } -function ConvertFrom-PodeNameValueToHashTable -{ +function ConvertFrom-PodeNameValueToHashTable { param( [Parameter()] [System.Collections.Specialized.NameValueCollection] @@ -1657,8 +1600,7 @@ function ConvertFrom-PodeNameValueToHashTable return $ht } -function Get-PodeCount -{ +function Get-PodeCount { param( [Parameter()] $Object @@ -1668,7 +1610,7 @@ function Get-PodeCount return 0 } - if ($Object -is [string]){ + if ($Object -is [string]) { return $Object.Length } @@ -1679,10 +1621,9 @@ function Get-PodeCount return $Object.Count } -function Test-PodePathAccess -{ +function Test-PodePathAccess { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Path ) @@ -1697,8 +1638,7 @@ function Test-PodePathAccess return $true } -function Test-PodePath -{ +function Test-PodePath { param( [Parameter()] $Path, @@ -1740,8 +1680,7 @@ function Test-PodePath return $true } -function Test-PodePathIsFile -{ +function Test-PodePathIsFile { param( [Parameter()] [string] @@ -1762,8 +1701,7 @@ function Test-PodePathIsFile return (![string]::IsNullOrWhiteSpace([System.IO.Path]::GetExtension($Path))) } -function Test-PodePathIsWildcard -{ +function Test-PodePathIsWildcard { param( [Parameter()] [string] @@ -1777,10 +1715,9 @@ function Test-PodePathIsWildcard return $Path.Contains('*') } -function Test-PodePathIsDirectory -{ +function Test-PodePathIsDirectory { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $Path, @@ -1796,22 +1733,20 @@ function Test-PodePathIsDirectory return ([string]::IsNullOrWhiteSpace([System.IO.Path]::GetExtension($Path))) } -function Convert-PodePathSeparators -{ +function Convert-PodePathSeparators { param( [Parameter()] $Paths ) return @($Paths | ForEach-Object { - if (![string]::IsNullOrWhiteSpace($_)) { - $_ -ireplace '[\\/]', [System.IO.Path]::DirectorySeparatorChar - } - }) + if (![string]::IsNullOrWhiteSpace($_)) { + $_ -ireplace '[\\/]', [System.IO.Path]::DirectorySeparatorChar + } + }) } -function Convert-PodePathPatternToRegex -{ +function Convert-PodePathPatternToRegex { param( [Parameter()] [string] @@ -1842,8 +1777,7 @@ function Convert-PodePathPatternToRegex return "^$($Path)$" } -function Convert-PodePathPatternsToRegex -{ +function Convert-PodePathPatternsToRegex { param( [Parameter()] [string[]] @@ -1858,10 +1792,10 @@ function Convert-PodePathPatternsToRegex # replace certain chars $Paths = @(foreach ($path in $Paths) { - if (![string]::IsNullOrEmpty($path)) { - Convert-PodePathPatternToRegex -Path $path -NotStrict -NotSlashes:$NotSlashes - } - }) + if (![string]::IsNullOrEmpty($path)) { + Convert-PodePathPatternToRegex -Path $path -NotStrict -NotSlashes:$NotSlashes + } + }) # if no paths, return null if (($null -eq $Paths) -or ($Paths.Length -eq 0)) { @@ -1878,8 +1812,7 @@ function Convert-PodePathPatternsToRegex return "^$($joined)$" } -function Get-PodeDefaultSslProtocols -{ +function Get-PodeDefaultSslProtocols { if (Test-PodeIsMacOS) { return (ConvertTo-PodeSslProtocols -Protocols Tls12) } @@ -1887,8 +1820,7 @@ function Get-PodeDefaultSslProtocols return (ConvertTo-PodeSslProtocols -Protocols Ssl3, Tls12) } -function ConvertTo-PodeSslProtocols -{ +function ConvertTo-PodeSslProtocols { param( [Parameter()] [ValidateSet('Ssl2', 'Ssl3', 'Tls', 'Tls11', 'Tls12', 'Tls13')] @@ -1904,8 +1836,7 @@ function ConvertTo-PodeSslProtocols return [System.Security.Authentication.SslProtocols]($protos) } -function Get-PodeModuleDetails -{ +function Get-PodeModuleDetails { # if there's 1 module imported already, use that $importedModule = @(Get-Module -Name Pode) if (($importedModule | Measure-Object).Count -eq 1) { @@ -1919,7 +1850,8 @@ function Get-PodeModuleDetails return (Convert-PodeModuleDetails -Module $usedModule) } } - catch {} + catch { + } # if there were multiple to begin with, use the newest version if (($importedModule | Measure-Object).Count -gt 1) { @@ -1930,31 +1862,29 @@ function Get-PodeModuleDetails return (Convert-PodeModuleDetails -Module @(Get-Module -ListAvailable -Name Pode | Sort-Object -Property Version)[-1]) } -function Convert-PodeModuleDetails -{ +function Convert-PodeModuleDetails { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [psmoduleinfo] $Module ) $details = @{ - Name = $Module.Name - Path = $Module.Path - BasePath = $Module.ModuleBase - DataPath = (Find-PodeModuleFile -Module $Module -CheckVersion) + Name = $Module.Name + Path = $Module.Path + BasePath = $Module.ModuleBase + DataPath = (Find-PodeModuleFile -Module $Module -CheckVersion) InternalPath = $null - InPath = (Test-PodeModuleInPath -Module $Module) + InPath = (Test-PodeModuleInPath -Module $Module) } $details.InternalPath = $details.DataPath -ireplace 'Pode\.(ps[md]1)', 'Pode.Internal.$1' return $details } -function Test-PodeModuleInPath -{ +function Test-PodeModuleInPath { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [psmoduleinfo] $Module ) @@ -1975,10 +1905,9 @@ function Test-PodeModuleInPath return $false } -function Get-PodeModuleDependencies -{ +function Get-PodeModuleDependencies { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [psmoduleinfo] $Module ) @@ -1995,23 +1924,19 @@ function Get-PodeModuleDependencies return ($mods + $module) } -function Get-PodeModuleRootPath -{ +function Get-PodeModuleRootPath { return (Split-Path -Parent -Path $PodeContext.Server.PodeModule.Path) } -function Get-PodeModuleMiscPath -{ +function Get-PodeModuleMiscPath { return [System.IO.Path]::Combine((Get-PodeModuleRootPath), 'Misc') } -function Get-PodeUrl -{ +function Get-PodeUrl { return "$($WebEvent.Endpoint.Protocol)://$($WebEvent.Endpoint.Address)$($WebEvent.Path)" } -function Find-PodeErrorPage -{ +function Find-PodeErrorPage { param( [Parameter()] [int] @@ -2042,10 +1967,10 @@ function Find-PodeErrorPage if (!(Test-PodeIsEmpty $PodeContext.Server.Web.ErrorPages.Routes)) { # find type by pattern $matched = @(foreach ($key in $PodeContext.Server.Web.ErrorPages.Routes.Keys) { - if ($WebEvent.Path -imatch $key) { - $key - } - })[0] + if ($WebEvent.Path -imatch $key) { + $key + } + })[0] # if we have a match, see if a page exists if (!(Test-PodeIsEmpty $matched)) { @@ -2084,8 +2009,7 @@ function Find-PodeErrorPage return $null } -function Get-PodeErrorPage -{ +function Get-PodeErrorPage { param( [Parameter()] [int] @@ -2119,8 +2043,7 @@ function Get-PodeErrorPage return $path } -function Find-PodeCustomErrorPage -{ +function Find-PodeCustomErrorPage { param( [Parameter()] [int] @@ -2155,8 +2078,7 @@ function Find-PodeCustomErrorPage return $null } -function Find-PodeFileForContentType -{ +function Find-PodeFileForContentType { param( [Parameter()] [string] @@ -2194,28 +2116,27 @@ function Find-PodeFileForContentType } $engineFiles = @(foreach ($file in $files) { - if ($file.Name -imatch "\.$($Engine)$") { - $file - } - }) + if ($file.Name -imatch "\.$($Engine)$") { + $file + } + }) $files = @(foreach ($file in $files) { - if ($file.Name -inotmatch "\.$($Engine)$") { - $file - } - }) + if ($file.Name -inotmatch "\.$($Engine)$") { + $file + } + }) # only attempt static files if we still have files after any engine filtering - if ($null -ne $files -and $files.Length -gt 0) - { + if ($null -ne $files -and $files.Length -gt 0) { # get files of the format '.' $file = @(foreach ($f in $files) { - if ($f.Name -imatch "^$($Name)\.(?.*?)$") { - if (($ContentType -ieq (Get-PodeContentType -Extension $Matches['ext']))) { - $f.FullName + if ($f.Name -imatch "^$($Name)\.(?.*?)$") { + if (($ContentType -ieq (Get-PodeContentType -Extension $Matches['ext']))) { + $f.FullName + } } - } - })[0] + })[0] if (![string]::IsNullOrWhiteSpace($file)) { return $file @@ -2223,16 +2144,15 @@ function Find-PodeFileForContentType } # only attempt these formats if we have a files for the view engine - if ($null -ne $engineFiles -and $engineFiles.Length -gt 0) - { + if ($null -ne $engineFiles -and $engineFiles.Length -gt 0) { # get files of the format '..' $file = @(foreach ($f in $engineFiles) { - if ($f.Name -imatch "^$($Name)\.(?.*?)\.$($engine)$") { - if ($ContentType -ieq (Get-PodeContentType -Extension $Matches['ext'])) { - $f.FullName + if ($f.Name -imatch "^$($Name)\.(?.*?)\.$($engine)$") { + if ($ContentType -ieq (Get-PodeContentType -Extension $Matches['ext'])) { + $f.FullName + } } - } - })[0] + })[0] if (![string]::IsNullOrWhiteSpace($file)) { return $file @@ -2240,10 +2160,10 @@ function Find-PodeFileForContentType # get files of the format '.' $file = @(foreach ($f in $engineFiles) { - if ($f.Name -imatch "^$($Name)\.$($engine)$") { - $f.FullName - } - })[0] + if ($f.Name -imatch "^$($Name)\.$($engine)$") { + $f.FullName + } + })[0] if (![string]::IsNullOrWhiteSpace($file)) { return $file @@ -2254,10 +2174,9 @@ function Find-PodeFileForContentType return $null } -function Get-PodeRelativePath -{ +function Get-PodeRelativePath { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Path, @@ -2298,10 +2217,9 @@ function Get-PodeRelativePath return $Path } -function Get-PodeWildcardFiles -{ +function Get-PodeWildcardFiles { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Path, @@ -2328,8 +2246,7 @@ function Get-PodeWildcardFiles return $null } -function Test-PodeIsServerless -{ +function Test-PodeIsServerless { param( [Parameter()] [string] @@ -2348,8 +2265,7 @@ function Test-PodeIsServerless } } -function Get-PodeEndpointUrl -{ +function Get-PodeEndpointUrl { param( [Parameter()] $Endpoint @@ -2371,8 +2287,7 @@ function Get-PodeEndpointUrl return $url } -function Get-PodeDefaultPort -{ +function Get-PodeDefaultPort { param( [Parameter()] [ValidateSet('Http', 'Https', 'Smtp', 'Smtps', 'Tcp', 'Tcps', 'Ws', 'Wss')] @@ -2391,15 +2306,15 @@ function Get-PodeDefaultPort # are we after the real default ports? if ($Real) { return (@{ - Http = @{ Implicit = 80 } - Https = @{ Implicit = 443 } - Smtp = @{ Implicit = 25 } - Smtps = @{ Implicit = 465; Explicit = 587 } - Tcp = @{ Implicit = 9001 } - Tcps = @{ Implicit = 9002; Explicit = 9003 } - Ws = @{ Implicit = 80 } - Wss = @{ Implicit = 443 } - })[$Protocol.ToLowerInvariant()][$TlsMode.ToLowerInvariant()] + Http = @{ Implicit = 80 } + Https = @{ Implicit = 443 } + Smtp = @{ Implicit = 25 } + Smtps = @{ Implicit = 465; Explicit = 587 } + Tcp = @{ Implicit = 9001 } + Tcps = @{ Implicit = 9002; Explicit = 9003 } + Ws = @{ Implicit = 80 } + Wss = @{ Implicit = 443 } + })[$Protocol.ToLowerInvariant()][$TlsMode.ToLowerInvariant()] } # if we running as iis, return the ASPNET port @@ -2414,19 +2329,18 @@ function Get-PodeDefaultPort # otherwise, get the port for the protocol return (@{ - Http = @{ Implicit = 8080 } - Https = @{ Implicit = 8443 } - Smtp = @{ Implicit = 25 } - Smtps = @{ Implicit = 465; Explicit = 587 } - Tcp = @{ Implicit = 9001 } - Tcps = @{ Implicit = 9002; Explicit = 9003 } - Ws = @{ Implicit = 9080 } - Wss = @{ Implicit = 9443 } - })[$Protocol.ToLowerInvariant()][$TlsMode.ToLowerInvariant()] + Http = @{ Implicit = 8080 } + Https = @{ Implicit = 8443 } + Smtp = @{ Implicit = 25 } + Smtps = @{ Implicit = 465; Explicit = 587 } + Tcp = @{ Implicit = 9001 } + Tcps = @{ Implicit = 9002; Explicit = 9003 } + Ws = @{ Implicit = 9080 } + Wss = @{ Implicit = 9443 } + })[$Protocol.ToLowerInvariant()][$TlsMode.ToLowerInvariant()] } -function Set-PodeServerHeader -{ +function Set-PodeServerHeader { param( [Parameter()] [string] @@ -2444,10 +2358,9 @@ function Set-PodeServerHeader Set-PodeHeader -Name 'Server' -Value $name } -function Get-PodeHandler -{ +function Get-PodeHandler { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [ValidateSet('Service', 'Smtp')] [string] $Type, @@ -2464,10 +2377,9 @@ function Get-PodeHandler return $PodeContext.Server.Handlers[$Type][$Name] } -function Convert-PodeFileToScriptBlock -{ +function Convert-PodeFileToScriptBlock { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $FilePath ) @@ -2488,8 +2400,7 @@ function Convert-PodeFileToScriptBlock return ([scriptblock](Use-PodeScript -Path $FilePath)) } -function Convert-PodeQueryStringToHashTable -{ +function Convert-PodeQueryStringToHashTable { param( [Parameter()] [string] @@ -2513,8 +2424,7 @@ function Convert-PodeQueryStringToHashTable return (ConvertFrom-PodeNameValueToHashTable -Collection $tmpQuery) } -function Convert-PodeScopedVariables -{ +function Convert-PodeScopedVariables { param( [Parameter()] [scriptblock] @@ -2566,8 +2476,7 @@ function Convert-PodeScopedVariables } } -function Invoke-PodeStateScriptConversion -{ +function Invoke-PodeStateScriptConversion { param( [Parameter()] [scriptblock] @@ -2600,8 +2509,7 @@ function Invoke-PodeStateScriptConversion return $ScriptBlock } -function Invoke-PodeSecretScriptConversion -{ +function Invoke-PodeSecretScriptConversion { param( [Parameter()] [scriptblock] @@ -2634,8 +2542,7 @@ function Invoke-PodeSecretScriptConversion return $ScriptBlock } -function Invoke-PodeSessionScriptConversion -{ +function Invoke-PodeSessionScriptConversion { param( [Parameter()] [scriptblock] @@ -2663,14 +2570,13 @@ function Invoke-PodeSessionScriptConversion return $ScriptBlock } -function Invoke-PodeUsingScriptConversion -{ +function Invoke-PodeUsingScriptConversion { param( [Parameter()] [scriptblock] $ScriptBlock, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [System.Management.Automation.SessionState] $PSSession ) @@ -2709,10 +2615,9 @@ function Invoke-PodeUsingScriptConversion return @($newScriptBlock, $usingVars) } -function Get-PodeScriptUsingVariables -{ +function Get-PodeScriptUsingVariables { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [scriptblock] $ScriptBlock ) @@ -2720,13 +2625,12 @@ function Get-PodeScriptUsingVariables return $ScriptBlock.Ast.FindAll({ $args[0] -is [System.Management.Automation.Language.UsingExpressionAst] }, $true) } -function ConvertTo-PodeUsingVariables -{ +function ConvertTo-PodeUsingVariables { param( [Parameter()] $UsingVariables, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [System.Management.Automation.SessionState] $PSSession ) @@ -2755,11 +2659,11 @@ function ConvertTo-PodeUsingVariables # add to mapped $mapped[$varName] = @{ - OldName = $usingVar.SubExpression.Extent.Text - NewName = "__using_$($varName)" + OldName = $usingVar.SubExpression.Extent.Text + NewName = "__using_$($varName)" NewNameWithDollar = "`$__using_$($varName)" - SubExpressions = @() - Value = $value.Value + SubExpressions = @() + Value = $value.Value } } @@ -2770,10 +2674,9 @@ function ConvertTo-PodeUsingVariables return @($mapped.Values) } -function ConvertTo-PodeUsingScript -{ +function ConvertTo-PodeUsingScript { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [scriptblock] $ScriptBlock, @@ -2818,10 +2721,9 @@ function ConvertTo-PodeUsingScript return $convertedScriptBlock } -function Get-PodeDotSourcedFiles -{ +function Get-PodeDotSourcedFiles { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [System.Management.Automation.Language.Ast] $Ast, @@ -2841,7 +2743,7 @@ function Get-PodeDotSourcedFiles ($args[0] -is [System.Management.Automation.Language.CommandAst]) -and ($args[0].InvocationOperator -iin $cmdTypes) -and ($args[0].CommandElements.StaticType.Name -ieq 'string') - }, $false)).CommandElements.Value + }, $false)).CommandElements.Value $fileOrder = @() @@ -2867,10 +2769,9 @@ function Get-PodeDotSourcedFiles return $fileOrder } -function Get-PodeAstFromFile -{ +function Get-PodeAstFromFile { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $FilePath ) @@ -2882,10 +2783,9 @@ function Get-PodeAstFromFile return [System.Management.Automation.Language.Parser]::ParseFile($FilePath, [ref]$null, [ref]$null) } -function Get-PodeFunctionsFromFile -{ +function Get-PodeFunctionsFromFile { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $FilePath ) @@ -2894,10 +2794,9 @@ function Get-PodeFunctionsFromFile return @(Get-PodeFunctionsFromAst -Ast $ast) } -function Get-PodeFunctionsFromAst -{ +function Get-PodeFunctionsFromAst { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [System.Management.Automation.Language.Ast] $Ast ) @@ -2905,34 +2804,33 @@ function Get-PodeFunctionsFromAst $funcs = @(($Ast.FindAll({ $args[0] -is [System.Management.Automation.Language.FunctionDefinitionAst] }, $false))) return @(foreach ($func in $funcs) { - # skip null - if ($null -eq $func) { - continue - } + # skip null + if ($null -eq $func) { + continue + } - # skip pode funcs - if ($func.Name -ilike '*-Pode*') { - continue - } + # skip pode funcs + if ($func.Name -ilike '*-Pode*') { + continue + } - # definition - $def = "$($func.Body)".Trim('{}').Trim() - if (($null -ne $func.Parameters) -and ($func.Parameters.Count -gt 0)) { - $def = "param($($func.Parameters.Name -join ','))`n$($def)" - } + # definition + $def = "$($func.Body)".Trim('{}').Trim() + if (($null -ne $func.Parameters) -and ($func.Parameters.Count -gt 0)) { + $def = "param($($func.Parameters.Name -join ','))`n$($def)" + } - # the found func - @{ - Name = $func.Name - Definition = $def - } - }) + # the found func + @{ + Name = $func.Name + Definition = $def + } + }) } -function Get-PodeFunctionsFromScriptBlock -{ +function Get-PodeFunctionsFromScriptBlock { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [scriptblock] $ScriptBlock ) @@ -2946,8 +2844,7 @@ function Get-PodeFunctionsFromScriptBlock $callstack = ($callstack | Select-Object -Skip 4) $bindingFlags = [System.Reflection.BindingFlags]'NonPublic, Instance, Static' - foreach ($call in $callstack) - { + foreach ($call in $callstack) { $_funcContext = $call.GetType().GetProperty('FunctionContext', $bindingFlags).GetValue($call, $null) $_scriptBlock = $_funcContext.GetType().GetField('_scriptBlock', $bindingFlags).GetValue($_funcContext) $foundFuncs += @(Get-PodeFunctionsFromAst -Ast $_scriptBlock.Ast) @@ -2961,10 +2858,9 @@ function Get-PodeFunctionsFromScriptBlock return $foundFuncs } -function Read-PodeWebExceptionDetails -{ +function Read-PodeWebExceptionDetails { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [System.Management.Automation.ErrorRecord] $ErrorRecord ) @@ -2992,21 +2888,20 @@ function Read-PodeWebExceptionDetails return @{ Status = @{ - Code = $code + Code = $code Description = $desc } - Body = $body + Body = $body } } -function Use-PodeFolder -{ +function Use-PodeFolder { param( [Parameter()] [string] $Path, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $DefaultPath ) @@ -3030,14 +2925,13 @@ function Use-PodeFolder } } -function Find-PodeModuleFile -{ +function Find-PodeModuleFile { param( - [Parameter(Mandatory=$true, ParameterSetName='Name')] + [Parameter(Mandatory = $true, ParameterSetName = 'Name')] [string] $Name, - [Parameter(Mandatory=$true, ParameterSetName='Module')] + [Parameter(Mandatory = $true, ParameterSetName = 'Module')] [psmoduleinfo] $Module, @@ -3086,8 +2980,7 @@ function Find-PodeModuleFile return $path } -function Get-PodeScriptblockArguments -{ +function Get-PodeScriptblockArguments { param( [Parameter()] [object[]] @@ -3108,16 +3001,15 @@ function Get-PodeScriptblockArguments $_vars = @() foreach ($_var in $UsingVariables) { - $_vars += ,$_var.Value + $_vars += , $_var.Value } return ($_vars + $ArgumentList) } -function Clear-PodeHashtableInnerKeys -{ +function Clear-PodeHashtableInnerKeys { param( - [Parameter(ValueFromPipeline=$true)] + [Parameter(ValueFromPipeline = $true)] [hashtable] $InputObject ) @@ -3131,8 +3023,7 @@ function Clear-PodeHashtableInnerKeys } } -function Set-PodeCronInterval -{ +function Set-PodeCronInterval { param( [Parameter()] [hashtable] @@ -3167,10 +3058,9 @@ function Set-PodeCronInterval return ($Value.Length -eq 1) } -function Test-PodeModuleInstalled -{ +function Test-PodeModuleInstalled { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name ) @@ -3178,15 +3068,13 @@ function Test-PodeModuleInstalled return ($null -ne (Get-Module -Name $Name -ListAvailable -ErrorAction Ignore -Verbose:$false)) } -function Get-PodePlaceholderRegex -{ +function Get-PodePlaceholderRegex { return '\:(?[\w]+)' } -function Resolve-PodePlaceholders -{ +function Resolve-PodePlaceholders { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Path, @@ -3222,10 +3110,9 @@ function Resolve-PodePlaceholders return (Convert-PodePlaceholders -Path $Path -Pattern $Pattern -Prepend $Prepend -Append $Append) } -function Convert-PodePlaceholders -{ +function Convert-PodePlaceholders { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Path, @@ -3253,10 +3140,9 @@ function Convert-PodePlaceholders return $Path } -function Test-PodePlaceholders -{ +function Test-PodePlaceholders { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Path, diff --git a/src/Private/Logging.ps1 b/src/Private/Logging.ps1 index 5241e7ce0..24f8f393f 100644 --- a/src/Private/Logging.ps1 +++ b/src/Private/Logging.ps1 @@ -1,5 +1,4 @@ -function Get-PodeLoggingTerminalMethod -{ +function Get-PodeLoggingTerminalMethod { return { param($item, $options) @@ -18,8 +17,7 @@ function Get-PodeLoggingTerminalMethod } } -function Get-PodeLoggingFileMethod -{ +function Get-PodeLoggingFileMethod { return { param($item, $options) @@ -77,8 +75,7 @@ function Get-PodeLoggingFileMethod } } -function Get-PodeLoggingEventViewerMethod -{ +function Get-PodeLoggingEventViewerMethod { return { param($item, $options, $rawItem) @@ -106,13 +103,13 @@ function Get-PodeLoggingEventViewerMethod $message = ($item[$i] | Protect-PodeLogItem) $entryLog.WriteEvent($entryInstance, $message) } - catch {} + catch { + } } } } -function ConvertTo-PodeEventViewerLevel -{ +function ConvertTo-PodeEventViewerLevel { param( [Parameter()] [string] @@ -134,17 +131,15 @@ function ConvertTo-PodeEventViewerLevel return [System.Diagnostics.EventLogEntryType]::Information } -function Get-PodeLoggingInbuiltType -{ - param ( - [Parameter(Mandatory=$true)] +function Get-PodeLoggingInbuiltType { + param( + [Parameter(Mandatory = $true)] [ValidateSet('Errors', 'Requests')] [string] $Type ) - switch ($Type.ToLowerInvariant()) - { + switch ($Type.ToLowerInvariant()) { 'requests' { $script = { param($item, $options) @@ -204,20 +199,17 @@ function Get-PodeLoggingInbuiltType return $script } -function Get-PodeRequestLoggingName -{ +function Get-PodeRequestLoggingName { return '__pode_log_requests__' } -function Get-PodeErrorLoggingName -{ +function Get-PodeErrorLoggingName { return '__pode_log_errors__' } -function Get-PodeLogger -{ - param ( - [Parameter(Mandatory=$true)] +function Get-PodeLogger { + param( + [Parameter(Mandatory = $true)] [string] $Name ) @@ -225,10 +217,9 @@ function Get-PodeLogger return $PodeContext.Server.Logging.Types[$Name] } -function Test-PodeLoggerEnabled -{ - param ( - [Parameter(Mandatory=$true)] +function Test-PodeLoggerEnabled { + param( + [Parameter(Mandatory = $true)] [string] $Name ) @@ -236,28 +227,24 @@ function Test-PodeLoggerEnabled return ($PodeContext.Server.Logging.Enabled -and $PodeContext.Server.Logging.Types.ContainsKey($Name)) } -function Get-PodeErrorLoggingLevels -{ +function Get-PodeErrorLoggingLevels { return (Get-PodeLogger -Name (Get-PodeErrorLoggingName)).Arguments.Levels } -function Test-PodeErrorLoggingEnabled -{ +function Test-PodeErrorLoggingEnabled { return (Test-PodeLoggerEnabled -Name (Get-PodeErrorLoggingName)) } -function Test-PodeRequestLoggingEnabled -{ +function Test-PodeRequestLoggingEnabled { return (Test-PodeLoggerEnabled -Name (Get-PodeRequestLoggingName)) } -function Write-PodeRequestLog -{ - param ( - [Parameter(Mandatory=$true)] +function Write-PodeRequestLog { + param( + [Parameter(Mandatory = $true)] $Request, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] $Response, [Parameter()] @@ -273,39 +260,53 @@ function Write-PodeRequestLog # build a request object $item = @{ - Host = $Request.RemoteEndPoint.Address.IPAddressToString + Host = $Request.RemoteEndPoint.Address.IPAddressToString RfcUserIdentity = '-' - User = '-' - Date = [DateTime]::Now.ToString('dd/MMM/yyyy:HH:mm:ss zzz') - Request = @{ - Method = $Request.HttpMethod.ToUpperInvariant() + User = '-' + Date = [DateTime]::Now.ToString('dd/MMM/yyyy:HH:mm:ss zzz') + Request = @{ + Method = $Request.HttpMethod.ToUpperInvariant() Resource = $Path Protocol = "HTTP/$($Request.ProtocolVersion)" Referrer = $Request.UrlReferrer - Agent = $Request.UserAgent + Agent = $Request.UserAgent } - Response = @{ - StatusCode = $Response.StatusCode + Response = @{ + StatusCode = $Response.StatusCode StatusDescription = $Response.StatusDescription - Size = '-' + Size = '-' } } + # set size if >0 if ($Response.ContentLength64 -gt 0) { $item.Response.Size = $Response.ContentLength64 } + # set username - dot spaces + if (Test-PodeAuthUser -IgnoreSession) { + $userProps = (Get-PodeLogger -Name $name).Properties.Username.Split('.') + + $user = $WebEvent.Auth.User + foreach ($atom in $userProps) { + $user = $user.($atom) + } + + if (![string]::IsNullOrWhiteSpace($user)) { + $item.User = $user -ireplace '\s+', '.' + } + } + # add the item to be processed $null = $PodeContext.LogsToProcess.Add(@{ - Name = $name - Item = $item - }) + Name = $name + Item = $item + }) } -function Add-PodeRequestLogEndware -{ - param ( - [Parameter(Mandatory=$true)] +function Add-PodeRequestLogEndware { + param( + [Parameter(Mandatory = $true)] [ValidateNotNull()] $WebEvent ) @@ -324,8 +325,7 @@ function Add-PodeRequestLogEndware } } -function Test-PodeLoggersExist -{ +function Test-PodeLoggersExist { if (($null -eq $PodeContext.Server.Logging) -or ($null -eq $PodeContext.Server.Logging.Types)) { return $false } @@ -333,16 +333,14 @@ function Test-PodeLoggersExist return (($PodeContext.Server.Logging.Types.Count -gt 0) -or ($PodeContext.Server.Logging.Enabled)) } -function Start-PodeLoggingRunspace -{ +function Start-PodeLoggingRunspace { # skip if there are no loggers configured, or logging is disabled if (!(Test-PodeLoggersExist)) { return } $script = { - while (!$PodeContext.Tokens.Cancellation.IsCancellationRequested) - { + while (!$PodeContext.Tokens.Cancellation.IsCancellationRequested) { # if there are no logs to process, just sleep for a few seconds - but after checking the batch if ($PodeContext.LogsToProcess.Count -eq 0) { Test-PodeLoggerBatches @@ -352,10 +350,10 @@ function Start-PodeLoggingRunspace # safely pop off the first log from the array $log = (Lock-PodeObject -Return -Object $PodeContext.LogsToProcess -ScriptBlock { - $log = $PodeContext.LogsToProcess[0] - $null = $PodeContext.LogsToProcess.RemoveAt(0) - return $log - }) + $log = $PodeContext.LogsToProcess[0] + $null = $PodeContext.LogsToProcess.RemoveAt(0) + return $log + }) # run the log item through the appropriate method $logger = Get-PodeLogger -Name $log.Name @@ -398,7 +396,7 @@ function Start-PodeLoggingRunspace # send the writable log item off to the log writer if ($null -ne $result) { - $_args = @(,$result) + @($logger.Method.Arguments) + @(,$rawItems) + $_args = @(, $result) + @($logger.Method.Arguments) + @(, $rawItems) $_args = @(Get-PodeScriptblockArguments -ArgumentList $_args -UsingVariables $logger.Method.UsingVariables) Invoke-PodeScriptBlock -ScriptBlock $logger.Method.ScriptBlock -Arguments $_args -Splat } @@ -411,24 +409,20 @@ function Start-PodeLoggingRunspace Add-PodeRunspace -Type Main -ScriptBlock $script } -function Test-PodeLoggerBatches -{ +function Test-PodeLoggerBatches { $now = [datetime]::Now # check each logger, and see if its batch needs to be written - foreach ($logger in $PodeContext.Server.Logging.Types.Values) - { + foreach ($logger in $PodeContext.Server.Logging.Types.Values) { $batch = $logger.Method.Batch - if (($batch.Size -gt 1) -and ($batch.Items.Length -gt 0) -and - ($batch.Timeout -gt 0) -and ($null -ne $batch.LastUpdate) -and ($batch.LastUpdate.AddSeconds($batch.Timeout) -le $now)) - { + if (($batch.Size -gt 1) -and ($batch.Items.Length -gt 0) -and ($batch.Timeout -gt 0) -and ($null -ne $batch.LastUpdate) -and ($batch.LastUpdate.AddSeconds($batch.Timeout) -le $now)) { $result = $batch.Items $rawItems = $batch.RawItems $batch.Items = @() $batch.RawItems = @() - $_args = @(,$result) + @($logger.Method.Arguments) + @(,$rawItems) + $_args = @(, $result) + @($logger.Method.Arguments) + @(, $rawItems) $_args = @(Get-PodeScriptblockArguments -ArgumentList $_args -UsingVariables $logger.Method.UsingVariables) Invoke-PodeScriptBlock -ScriptBlock $logger.Method.ScriptBlock -Arguments $_args -Splat } diff --git a/src/Private/Mappers.ps1 b/src/Private/Mappers.ps1 index 976312a5a..a16b6c05d 100644 --- a/src/Private/Mappers.ps1 +++ b/src/Private/Mappers.ps1 @@ -1,6 +1,5 @@ -function Get-PodeContentType -{ - param ( +function Get-PodeContentType { + param( [Parameter()] [string] $Extension, @@ -18,8 +17,7 @@ function Get-PodeContentType } # Sourced from https://github.com/samuelneff/MimeTypeMap - switch ($Extension.ToLowerInvariant()) - { + switch ($Extension.ToLowerInvariant()) { '.323' { return 'text/h323' } '.3g2' { return 'video/3gpp2' } '.3gp' { return 'video/3gpp' } @@ -644,16 +642,14 @@ function Get-PodeContentType } } -function Get-PodeStatusDescription -{ - param ( +function Get-PodeStatusDescription { + param( [Parameter()] [int] $StatusCode ) - switch ($StatusCode) - { + switch ($StatusCode) { 100 { return 'Continue' } 101 { return 'Switching Protocols' } 102 { return 'Processing' } diff --git a/src/Private/Metrics.ps1 b/src/Private/Metrics.ps1 index 975593432..d510ddd29 100644 --- a/src/Private/Metrics.ps1 +++ b/src/Private/Metrics.ps1 @@ -1,5 +1,4 @@ -function Update-PodeServerRequestMetrics -{ +function Update-PodeServerRequestMetrics { param( [Parameter()] [hashtable] @@ -33,8 +32,7 @@ function Update-PodeServerRequestMetrics } } -function Update-PodeServerSignalMetrics -{ +function Update-PodeServerSignalMetrics { param( [Parameter()] [hashtable] diff --git a/src/Private/Middleware.ps1 b/src/Private/Middleware.ps1 index 59b41ccb0..a0cb93b6b 100644 --- a/src/Private/Middleware.ps1 +++ b/src/Private/Middleware.ps1 @@ -1,8 +1,7 @@ using namespace System.Security.Cryptography -function Invoke-PodeMiddleware -{ - param ( +function Invoke-PodeMiddleware { + param( [Parameter()] $Middleware, @@ -17,25 +16,23 @@ function Invoke-PodeMiddleware } # filter the middleware down by route (retaining order) - if (![string]::IsNullOrWhiteSpace($Route)) - { + if (![string]::IsNullOrWhiteSpace($Route)) { $Middleware = @(foreach ($mware in $Middleware) { - if ($null -eq $mware) { - continue - } + if ($null -eq $mware) { + continue + } - if ([string]::IsNullOrWhiteSpace($mware.Route) -or ($mware.Route -ieq '/') -or ($mware.Route -ieq $Route) -or ($Route -imatch "^$($mware.Route)$")) { - $mware - } - }) + if ([string]::IsNullOrWhiteSpace($mware.Route) -or ($mware.Route -ieq '/') -or ($mware.Route -ieq $Route) -or ($Route -imatch "^$($mware.Route)$")) { + $mware + } + }) } # continue or halt? $continue = $true # loop through each of the middleware, invoking the next if it returns true - foreach ($midware in @($Middleware)) - { + foreach ($midware in @($Middleware)) { if (($null -eq $midware) -or ($null -eq $midware.Logic)) { continue } @@ -61,11 +58,10 @@ function Invoke-PodeMiddleware return $continue } -function New-PodeMiddlewareInternal -{ +function New-PodeMiddlewareInternal { [OutputType([hashtable])] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [scriptblock] $ScriptBlock, @@ -77,13 +73,13 @@ function New-PodeMiddlewareInternal [object[]] $ArgumentList, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [System.Management.Automation.SessionState] $PSSession ) if (Test-PodeIsEmpty $ScriptBlock) { - throw "[Middleware]: No ScriptBlock supplied" + throw '[Middleware]: No ScriptBlock supplied' } # if route is empty, set it to root @@ -94,9 +90,9 @@ function New-PodeMiddlewareInternal # create the middleware hashtable from a scriptblock $HashTable = @{ - Route = $Route - Logic = $ScriptBlock - Arguments = $ArgumentList + Route = $Route + Logic = $ScriptBlock + Arguments = $ArgumentList UsingVariables = $usingVars } @@ -104,15 +100,14 @@ function New-PodeMiddlewareInternal return $HashTable } -function Get-PodeInbuiltMiddleware -{ - param ( - [Parameter(Mandatory=$true)] +function Get-PodeInbuiltMiddleware { + param( + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $Name, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [ValidateNotNull()] [scriptblock] $ScriptBlock @@ -129,85 +124,81 @@ function Get-PodeInbuiltMiddleware # return the script return @{ - Name = $Name + Name = $Name Logic = $ScriptBlock } } -function Get-PodeAccessMiddleware -{ +function Get-PodeAccessMiddleware { return (Get-PodeInbuiltMiddleware -Name '__pode_mw_access__' -ScriptBlock { - # are there any rules? - if (($PodeContext.Server.Access.Allow.Count -eq 0) -and ($PodeContext.Server.Access.Deny.Count -eq 0)) { - return $true - } + # are there any rules? + if (($PodeContext.Server.Access.Allow.Count -eq 0) -and ($PodeContext.Server.Access.Deny.Count -eq 0)) { + return $true + } - # ensure the request IP address is allowed - if (!(Test-PodeIPAccess -IP $WebEvent.Request.RemoteEndPoint.Address)) { - Set-PodeResponseStatus -Code 403 - return $false - } + # ensure the request IP address is allowed + if (!(Test-PodeIPAccess -IP $WebEvent.Request.RemoteEndPoint.Address)) { + Set-PodeResponseStatus -Code 403 + return $false + } - # request is allowed - return $true - }) + # request is allowed + return $true + }) } -function Get-PodeLimitMiddleware -{ +function Get-PodeLimitMiddleware { return (Get-PodeInbuiltMiddleware -Name '__pode_mw_rate_limit__' -ScriptBlock { - # are there any rules? - if ($PodeContext.Server.Limits.Rules.Count -eq 0) { - return $true - } + # are there any rules? + if ($PodeContext.Server.Limits.Rules.Count -eq 0) { + return $true + } - # check the request IP address has not hit a rate limit - if (!(Test-PodeIPLimit -IP $WebEvent.Request.RemoteEndPoint.Address)) { - Set-PodeResponseStatus -Code 429 - return $false - } + # check the request IP address has not hit a rate limit + if (!(Test-PodeIPLimit -IP $WebEvent.Request.RemoteEndPoint.Address)) { + Set-PodeResponseStatus -Code 429 + return $false + } - # check the route - if (!(Test-PodeRouteLimit -Path $WebEvent.Path)) { - Set-PodeResponseStatus -Code 429 - return $false - } + # check the route + if (!(Test-PodeRouteLimit -Path $WebEvent.Path)) { + Set-PodeResponseStatus -Code 429 + return $false + } - # check the endpoint - if (!(Test-PodeEndpointLimit -EndpointName $WebEvent.Endpoint.Name)) { - Set-PodeResponseStatus -Code 429 - return $false - } + # check the endpoint + if (!(Test-PodeEndpointLimit -EndpointName $WebEvent.Endpoint.Name)) { + Set-PodeResponseStatus -Code 429 + return $false + } - # request is allowed - return $true - }) + # request is allowed + return $true + }) } -function Get-PodePublicMiddleware -{ +function Get-PodePublicMiddleware { return (Get-PodeInbuiltMiddleware -Name '__pode_mw_static_content__' -ScriptBlock { - # only find public static content here - $path = Find-PodePublicRoute -Path $WebEvent.Path - if ([string]::IsNullOrWhiteSpace($path)) { - return $true - } + # only find public static content here + $path = Find-PodePublicRoute -Path $WebEvent.Path + if ([string]::IsNullOrWhiteSpace($path)) { + return $true + } - # check current state of caching - $cachable = Test-PodeRouteValidForCaching -Path $WebEvent.Path + # check current state of caching + $cachable = Test-PodeRouteValidForCaching -Path $WebEvent.Path - # write the file to the response - Write-PodeFileResponse -Path $path -MaxAge $PodeContext.Server.Web.Static.Cache.MaxAge -Cache:$cachable + # write the file to the response + Write-PodeFileResponse -Path $path -MaxAge $PodeContext.Server.Web.Static.Cache.MaxAge -Cache:$cachable - # public static content found, stop - return $false - }) + # public static content found, stop + return $false + }) } -function Get-PodeRouteValidateMiddleware -{ +function Get-PodeRouteValidateMiddleware { return @{ - Name = '__pode_mw_route_validation__' + Name = '__pode_mw_route_validation__' Logic = { # check if the path is static route first, then check the main routes $route = Find-PodeStaticRoute -Path $WebEvent.Path -EndpointName $WebEvent.Endpoint.Name @@ -220,12 +211,12 @@ function Get-PodeRouteValidateMiddleware # check if a route exists for another method $methods = @('CONNECT', 'DELETE', 'GET', 'HEAD', 'MERGE', 'OPTIONS', 'PATCH', 'POST', 'PUT', 'TRACE') $diff_route = @(foreach ($method in $methods) { - $r = Find-PodeRoute -Method $method -Path $WebEvent.Path -EndpointName $WebEvent.Endpoint.Name - if ($null -ne $r) { - $r - break - } - })[0] + $r = Find-PodeRoute -Method $method -Path $WebEvent.Path -EndpointName $WebEvent.Endpoint.Name + if ($null -ne $r) { + $r + break + } + })[0] if ($null -ne $diff_route) { Set-PodeResponseStatus -Code 405 @@ -271,95 +262,90 @@ function Get-PodeRouteValidateMiddleware } } -function Get-PodeBodyMiddleware -{ +function Get-PodeBodyMiddleware { return (Get-PodeInbuiltMiddleware -Name '__pode_mw_body_parsing__' -ScriptBlock { - try { - # attempt to parse that data - $result = ConvertFrom-PodeRequestContent -Request $WebEvent.Request -ContentType $WebEvent.ContentType -TransferEncoding $WebEvent.TransferEncoding + try { + # attempt to parse that data + $result = ConvertFrom-PodeRequestContent -Request $WebEvent.Request -ContentType $WebEvent.ContentType -TransferEncoding $WebEvent.TransferEncoding - # set session data - $WebEvent.Data = $result.Data - $WebEvent.Files = $result.Files + # set session data + $WebEvent.Data = $result.Data + $WebEvent.Files = $result.Files - # payload parsed - return $true - } - catch { - Set-PodeResponseStatus -Code 400 -Exception $_ - return $false - } - }) + # payload parsed + return $true + } + catch { + Set-PodeResponseStatus -Code 400 -Exception $_ + return $false + } + }) } -function Get-PodeQueryMiddleware -{ +function Get-PodeQueryMiddleware { return (Get-PodeInbuiltMiddleware -Name '__pode_mw_query_parsing__' -ScriptBlock { - try { - # set the query string from the request - $WebEvent.Query = (ConvertFrom-PodeNameValueToHashTable -Collection $WebEvent.Request.QueryString) - return $true - } - catch { - Set-PodeResponseStatus -Code 400 -Exception $_ - return $false - } - }) + try { + # set the query string from the request + $WebEvent.Query = (ConvertFrom-PodeNameValueToHashTable -Collection $WebEvent.Request.QueryString) + return $true + } + catch { + Set-PodeResponseStatus -Code 400 -Exception $_ + return $false + } + }) } -function Get-PodeCookieMiddleware -{ +function Get-PodeCookieMiddleware { return (Get-PodeInbuiltMiddleware -Name '__pode_mw_cookie_parsing__' -ScriptBlock { - # if cookies already set, return - if ($WebEvent.Cookies.Count -gt 0) { - return $true - } + # if cookies already set, return + if ($WebEvent.Cookies.Count -gt 0) { + return $true + } - # if the request's header has no cookies, return - $h_cookie = (Get-PodeHeader -Name 'Cookie') - if ([string]::IsNullOrWhiteSpace($h_cookie)) { - return $true - } + # if the request's header has no cookies, return + $h_cookie = (Get-PodeHeader -Name 'Cookie') + if ([string]::IsNullOrWhiteSpace($h_cookie)) { + return $true + } - # parse the cookies from the header - $cookies = @($h_cookie -split '; ') - $WebEvent.Cookies = @{} + # parse the cookies from the header + $cookies = @($h_cookie -split '; ') + $WebEvent.Cookies = @{} - foreach ($cookie in $cookies) { - $atoms = $cookie.Split('=', 2) + foreach ($cookie in $cookies) { + $atoms = $cookie.Split('=', 2) - $value = [string]::Empty - if ($atoms.Length -gt 1) { - foreach ($atom in $atoms[1..($atoms.Length - 1)]) { - $value += $atom + $value = [string]::Empty + if ($atoms.Length -gt 1) { + foreach ($atom in $atoms[1..($atoms.Length - 1)]) { + $value += $atom + } } - } - $WebEvent.Cookies[$atoms[0]] = [System.Net.Cookie]::new($atoms[0], $value) - } + $WebEvent.Cookies[$atoms[0]] = [System.Net.Cookie]::new($atoms[0], $value) + } - return $true - }) + return $true + }) } -function Get-PodeSecurityMiddleware -{ +function Get-PodeSecurityMiddleware { return (Get-PodeInbuiltMiddleware -Name '__pode_mw_security__' -ScriptBlock { - # are there any security headers setup? - if ($PodeContext.Server.Security.Headers.Count -eq 0) { - return $true - } + # are there any security headers setup? + if ($PodeContext.Server.Security.Headers.Count -eq 0) { + return $true + } - # add security headers - Set-PodeHeaderBulk -Value $PodeContext.Server.Security.Headers + # add security headers + Set-PodeHeaderBulk -Value $PodeContext.Server.Security.Headers - # continue to next middleware/route - return $true - }) + # continue to next middleware/route + return $true + }) } -function Initialize-PodeIISMiddleware -{ +function Initialize-PodeIISMiddleware { # do nothing if not iis if (!$PodeContext.Server.IsIIS) { return @@ -367,7 +353,7 @@ function Initialize-PodeIISMiddleware # fail if no iis token - because there should be! if ([string]::IsNullOrWhiteSpace($PodeContext.Server.IIS.Token)) { - throw "IIS ASPNETCORE_TOKEN is missing" + throw 'IIS ASPNETCORE_TOKEN is missing' } # add middleware to check every request has the token diff --git a/src/Private/NameGenerator.ps1 b/src/Private/NameGenerator.ps1 index 20cf697cc..352c24f40 100644 --- a/src/Private/NameGenerator.ps1 +++ b/src/Private/NameGenerator.ps1 @@ -1,68 +1,67 @@ -function Get-PodeRandomName -{ +function Get-PodeRandomName { $adjs = @( - "admiring", - "agitated", - "blissful", - "dazzling", - "ecstatic", - "eloquent", - "friendly", - "gracious", - "hardcore", - "laughing", - "peaceful", - "pedantic", - "reverent", - "romantic", - "trusting", - "vigilant", - "vigorous", - "wizardly", - "youthful" + 'admiring', + 'agitated', + 'blissful', + 'dazzling', + 'ecstatic', + 'eloquent', + 'friendly', + 'gracious', + 'hardcore', + 'laughing', + 'peaceful', + 'pedantic', + 'reverent', + 'romantic', + 'trusting', + 'vigilant', + 'vigorous', + 'wizardly', + 'youthful' ) $names = @( - "almeida", - "babbage", - "bardeen", - "shannon", - "davinci", - "feynman", - "galileo", - "goodall", - "hawking", - "hermann", - "hodgkin", - "hypatia", - "jackson", - "johnson", - "kapitsa", - "keldysh", - "khorana", - "lalande", - "lamport", - "leavitt", - "lumiere", - "mcnulty", - "meitner", - "mestorf", - "murdock", - "neumann", - "noether", - "pasteur", - "perlman", - "poitras", - "ptolemy", - "ritchie", - "shirley", - "swanson", - "swirles", - "vaughan", - "volhard", - "villani", - "wescoff", - "wozniak" + 'almeida', + 'babbage', + 'bardeen', + 'shannon', + 'davinci', + 'feynman', + 'galileo', + 'goodall', + 'hawking', + 'hermann', + 'hodgkin', + 'hypatia', + 'jackson', + 'johnson', + 'kapitsa', + 'keldysh', + 'khorana', + 'lalande', + 'lamport', + 'leavitt', + 'lumiere', + 'mcnulty', + 'meitner', + 'mestorf', + 'murdock', + 'neumann', + 'noether', + 'pasteur', + 'perlman', + 'poitras', + 'ptolemy', + 'ritchie', + 'shirley', + 'swanson', + 'swirles', + 'vaughan', + 'volhard', + 'villani', + 'wescoff', + 'wozniak' ) $adjsRand = (Get-Random -Minimum 0 -Maximum $adjs.Length) diff --git a/src/Private/OpenApi.ps1 b/src/Private/OpenApi.ps1 index a2b8cdcf6..9c85b4d88 100644 --- a/src/Private/OpenApi.ps1 +++ b/src/Private/OpenApi.ps1 @@ -1,7 +1,6 @@ -function ConvertTo-PodeOAContentTypeSchema -{ +function ConvertTo-PodeOAContentTypeSchema { param( - [Parameter(ValueFromPipeline=$true)] + [Parameter(ValueFromPipeline = $true)] [hashtable] $Schemas ) @@ -21,10 +20,9 @@ function ConvertTo-PodeOAContentTypeSchema return (ConvertTo-PodeOAObjectSchema -Schemas $Schemas) } -function ConvertTo-PodeOAHeaderSchema -{ +function ConvertTo-PodeOAHeaderSchema { param( - [Parameter(ValueFromPipeline=$true)] + [Parameter(ValueFromPipeline = $true)] [hashtable] $Schemas ) @@ -37,10 +35,9 @@ function ConvertTo-PodeOAHeaderSchema return (ConvertTo-PodeOAObjectSchema -Schemas $Schemas) } -function ConvertTo-PodeOAObjectSchema -{ +function ConvertTo-PodeOAObjectSchema { param( - [Parameter(ValueFromPipeline=$true)] + [Parameter(ValueFromPipeline = $true)] [hashtable] $Schemas ) @@ -72,10 +69,9 @@ function ConvertTo-PodeOAObjectSchema return $obj } -function Test-PodeOAComponentSchema -{ +function Test-PodeOAComponentSchema { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name ) @@ -83,10 +79,9 @@ function Test-PodeOAComponentSchema return $PodeContext.Server.OpenAPI.components.schemas.ContainsKey($Name) } -function Test-PodeOAComponentResponse -{ +function Test-PodeOAComponentResponse { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name ) @@ -94,10 +89,9 @@ function Test-PodeOAComponentResponse return $PodeContext.Server.OpenAPI.components.responses.ContainsKey($Name) } -function Test-PodeOAComponentRequestBody -{ +function Test-PodeOAComponentRequestBody { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name ) @@ -105,10 +99,9 @@ function Test-PodeOAComponentRequestBody return $PodeContext.Server.OpenAPI.components.requestBodies.ContainsKey($Name) } -function Test-PodeOAComponentParameter -{ +function Test-PodeOAComponentParameter { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name ) @@ -116,10 +109,9 @@ function Test-PodeOAComponentParameter return $PodeContext.Server.OpenAPI.components.parameters.ContainsKey($Name) } -function ConvertTo-PodeOASchemaProperty -{ +function ConvertTo-PodeOASchemaProperty { param( - [Parameter(Mandatory=$true, ValueFromPipeline=$true)] + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [hashtable] $Property, @@ -129,10 +121,10 @@ function ConvertTo-PodeOASchemaProperty # base schema type $schema = @{ - type = $Property.type - format = $Property.format + type = $Property.type + format = $Property.format description = $Property.description - default = $Property.default + default = $Property.default } if ($Property.deprecated) { @@ -161,7 +153,7 @@ function ConvertTo-PodeOASchemaProperty $Property.array = $false $schema = @{ - type = 'array' + type = 'array' items = ($Property | ConvertTo-PodeOASchemaProperty) } } @@ -171,7 +163,7 @@ function ConvertTo-PodeOASchemaProperty $Property.object = $false $schema = @{ - type = 'object' + type = 'object' properties = (ConvertTo-PodeOASchemaObjectProperty -Properties $Property) } @@ -188,10 +180,9 @@ function ConvertTo-PodeOASchemaProperty return $schema } -function ConvertTo-PodeOASchemaObjectProperty -{ +function ConvertTo-PodeOASchemaObjectProperty { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [hashtable[]] $Properties ) @@ -205,10 +196,9 @@ function ConvertTo-PodeOASchemaObjectProperty return $schema } -function Get-PodeOpenApiDefinitionInternal -{ +function Get-PodeOpenApiDefinitionInternal { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Title, @@ -247,8 +237,8 @@ function Get-PodeOpenApiDefinitionInternal # metadata $def['info'] = @{ - title = $Title - version = $Version + title = $Title + version = $Version description = $Description } @@ -256,37 +246,41 @@ function Get-PodeOpenApiDefinitionInternal $def['servers'] = $null if (!$RestrictRoutes -and ($PodeContext.Server.Endpoints.Count -gt 1)) { $def.servers = @(foreach ($endpoint in $PodeContext.Server.Endpoints.Values) { - @{ - url = $endpoint.Url - description = (Protect-PodeValue -Value $endpoint.Description -Default $endpoint.Name) - } - }) + @{ + url = $endpoint.Url + description = (Protect-PodeValue -Value $endpoint.Description -Default $endpoint.Name) + } + }) } # components $def['components'] = $PodeContext.Server.OpenAPI.components # auth/security components - if ($PodeContext.Server.Authentications.Count -gt 0) { + if ($PodeContext.Server.Authentications.Methods.Count -gt 0) { if ($null -eq $def.components.securitySchemes) { $def.components.securitySchemes = @{} } - foreach ($authName in $PodeContext.Server.Authentications.Keys) { + foreach ($authName in $PodeContext.Server.Authentications.Methods.Keys) { $authType = (Find-PodeAuth -Name $authName).Scheme + if ([string]::IsNullOrWhiteSpace($authType)) { + continue + } + $_authName = ($authName -replace '\s+', '') $_authObj = @{} if ($authType.Scheme -ieq 'apikey') { $_authObj = @{ type = $authType.Scheme - in = $authType.Arguments.Location.ToLowerInvariant() + in = $authType.Arguments.Location.ToLowerInvariant() name = $authType.Arguments.LocationName } } else { $_authObj = @{ - type = $authType.Scheme.ToLowerInvariant() + type = $authType.Scheme.ToLowerInvariant() scheme = $authType.Name.ToLowerInvariant() } } @@ -336,15 +330,15 @@ function Get-PodeOpenApiDefinitionInternal # add path's http method to defintition $def.paths[$_route.OpenApi.Path][$method] = @{ - summary = $_route.OpenApi.Summary + summary = $_route.OpenApi.Summary description = $_route.OpenApi.Description operationId = $_route.OpenApi.OperationId - tags = @($_route.OpenApi.Tags) - responses = $_route.OpenApi.Responses - parameters = $_route.OpenApi.Parameters + tags = @($_route.OpenApi.Tags) + responses = $_route.OpenApi.Responses + parameters = $_route.OpenApi.Parameters requestBody = $_route.OpenApi.RequestBody - servers = $null - security = @($_route.OpenApi.Authentication) + servers = $null + security = @($_route.OpenApi.Authentication) } if ($_route.OpenApi.Deprecated) { @@ -398,10 +392,9 @@ function Get-PodeOpenApiDefinitionInternal return $def } -function ConvertTo-PodeOAPropertyFromCmdletParameter -{ +function ConvertTo-PodeOAPropertyFromCmdletParameter { param( - [Parameter(Mandatory=$true, ValueFromPipeline=$true)] + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [System.Management.Automation.ParameterMetadata] $Parameter ) @@ -424,25 +417,23 @@ function ConvertTo-PodeOAPropertyFromCmdletParameter New-PodeOAStringProperty -Name $Parameter.Name } -function Get-PodeOABaseObject -{ +function Get-PodeOABaseObject { return @{ - Path = $null - Title = $null + Path = $null + Title = $null components = @{ - schemas = @{} - responses = @{} + schemas = @{} + responses = @{} requestBodies = @{} - parameters = @{} + parameters = @{} } - Security = @() + Security = @() } } -function Set-PodeOAAuth -{ +function Set-PodeOAAuth { param( - [Parameter(Mandatory=$true, ValueFromPipeline=$true)] + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [ValidateNotNullOrEmpty()] [hashtable[]] $Route, @@ -453,22 +444,21 @@ function Set-PodeOAAuth ) foreach ($n in @($Name)) { - if (!(Test-PodeAuth -Name $n)) { + if (!(Test-PodeAuthExists -Name $n)) { throw "Authentication method does not exist: $($n)" } } foreach ($r in @($Route)) { $r.OpenApi.Authentication = @(foreach ($n in @($Name)) { - @{ - "$($n -replace '\s+', '')" = @() - } - }) + @{ + "$($n -replace '\s+', '')" = @() + } + }) } } -function Set-PodeOAGlobalAuth -{ +function Set-PodeOAGlobalAuth { param( [Parameter()] [string] @@ -479,7 +469,7 @@ function Set-PodeOAGlobalAuth $Route ) - if (!(Test-PodeAuth -Name $Name)) { + if (!(Test-PodeAuthExists -Name $Name)) { throw "Authentication method does not exist: $($Name)" } @@ -491,6 +481,6 @@ function Set-PodeOAGlobalAuth Definition = @{ "$($Name -replace '\s+', '')" = @() } - Route = (ConvertTo-PodeRouteRegex -Path $Route) + Route = (ConvertTo-PodeRouteRegex -Path $Route) } } \ No newline at end of file diff --git a/src/Private/PodeServer.ps1 b/src/Private/PodeServer.ps1 index 0ef9f6a6a..ad10d44cd 100644 --- a/src/Private/PodeServer.ps1 +++ b/src/Private/PodeServer.ps1 @@ -1,8 +1,7 @@ using namespace Pode -function Start-PodeWebServer -{ - param ( +function Start-PodeWebServer { + param( [switch] $Browse ) @@ -33,17 +32,18 @@ function Start-PodeWebServer # the endpoint $_endpoint = @{ - Key = "$($_ip):$($_.Port)" - Address = $_ip - Hostname = $_.HostName - IsIPAddress = $_.IsIPAddress - Port = $_.Port - Certificate = $_.Certificate.Raw + Key = "$($_ip):$($_.Port)" + Address = $_ip + Hostname = $_.HostName + IsIPAddress = $_.IsIPAddress + Port = $_.Port + Certificate = $_.Certificate.Raw AllowClientCertificate = $_.Certificate.AllowClientCertificate - Url = $_.Url - Protocol = $_.Protocol - Type = $_.Type - Pool = $_.Runspace.PoolName + Url = $_.Url + Protocol = $_.Protocol + Type = $_.Type + Pool = $_.Runspace.PoolName + SslProtocols = $_.Ssl.Protocols } # add endpoint to list @@ -66,12 +66,12 @@ function Start-PodeWebServer $listener.ErrorLoggingLevels = @(Get-PodeErrorLoggingLevels) $listener.RequestTimeout = $PodeContext.Server.Request.Timeout $listener.RequestBodySize = $PodeContext.Server.Request.BodySize + $listener.ShowServerDetails = [bool]$PodeContext.Server.Security.ServerDetails - try - { + try { # register endpoints on the listener $endpoints | ForEach-Object { - $socket = (. ([scriptblock]::Create("New-Pode$($PodeContext.Server.ListenerType)ListenerSocket -Address `$_.Address -Port `$_.Port -SslProtocols `$PodeContext.Server.Sockets.Ssl.Protocols -Type `$endpointsMap[`$_.Key].Type -Certificate `$_.Certificate -AllowClientCertificate `$_.AllowClientCertificate"))) + $socket = (. ([scriptblock]::Create("New-Pode$($PodeContext.Server.ListenerType)ListenerSocket -Address `$_.Address -Port `$_.Port -SslProtocols `$_.SslProtocols -Type `$endpointsMap[`$_.Key].Type -Certificate `$_.Certificate -AllowClientCertificate `$_.AllowClientCertificate"))) $socket.ReceiveTimeout = $PodeContext.Server.Sockets.ReceiveTimeout if (!$_.IsIPAddress) { @@ -98,57 +98,53 @@ function Start-PodeWebServer # script for listening out for incoming requests $listenScript = { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] $Listener, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [int] $ThreadId ) - try - { - while ($Listener.IsConnected -and !$PodeContext.Tokens.Cancellation.IsCancellationRequested) - { + try { + while ($Listener.IsConnected -and !$PodeContext.Tokens.Cancellation.IsCancellationRequested) { # get request and response $context = (Wait-PodeTask -Task $Listener.GetContextAsync($PodeContext.Tokens.Cancellation.Token)) - try - { - try - { + try { + try { $Request = $context.Request $Response = $context.Response # reset with basic event data $WebEvent = @{ - OnEnd = @() - Auth = @{} - Response = $Response - Request = $Request - Lockable = $PodeContext.Threading.Lockables.Global - Path = [System.Web.HttpUtility]::UrlDecode($Request.Url.AbsolutePath) - Method = $Request.HttpMethod.ToLowerInvariant() - Query = $null - Endpoint = @{ + OnEnd = @() + Auth = @{} + Response = $Response + Request = $Request + Lockable = $PodeContext.Threading.Lockables.Global + Path = [System.Web.HttpUtility]::UrlDecode($Request.Url.AbsolutePath) + Method = $Request.HttpMethod.ToLowerInvariant() + Query = $null + Endpoint = @{ Protocol = $Request.Url.Scheme - Address = $Request.Host - Name = $null + Address = $Request.Host + Name = $null } - ContentType = $Request.ContentType - ErrorType = $null - Cookies = @{} - PendingCookies = @{} - Parameters = $null - Data = $null - Files = $null - Streamed = $true - Route = $null - StaticContent = $null - Timestamp = [datetime]::UtcNow + ContentType = $Request.ContentType + ErrorType = $null + Cookies = @{} + PendingCookies = @{} + Parameters = $null + Data = $null + Files = $null + Streamed = $true + Route = $null + StaticContent = $null + Timestamp = [datetime]::UtcNow TransferEncoding = $null - AcceptEncoding = $null - Ranges = $null + AcceptEncoding = $null + Ranges = $null } # if iis, and we have an app path, alter it @@ -182,8 +178,7 @@ function Start-PodeWebServer throw $Request.Error } - if ((Invoke-PodeMiddleware -Middleware $WebEvent.Route.Middleware)) - { + if ((Invoke-PodeMiddleware -Middleware $WebEvent.Route.Middleware)) { # has the request been aborted if ($Request.IsAborted) { throw $Request.Error @@ -257,17 +252,15 @@ function Start-PodeWebServer # script to write messages back to the client(s) $signalScript = { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] $Listener ) try { - while ($Listener.IsConnected -and !$PodeContext.Tokens.Cancellation.IsCancellationRequested) - { + while ($Listener.IsConnected -and !$PodeContext.Tokens.Cancellation.IsCancellationRequested) { $message = (Wait-PodeTask -Task $Listener.GetServerSignalAsync($PodeContext.Tokens.Cancellation.Token)) - try - { + try { # get the sockets for the message $sockets = @() @@ -281,11 +274,11 @@ function Start-PodeWebServer # by path if (![string]::IsNullOrWhiteSpace($message.Path)) { $sockets = @(foreach ($socket in $sockets) { - if ($socket.Path -ieq $message.Path) { - $socket - break - } - }) + if ($socket.Path -ieq $message.Path) { + $socket + break + } + }) } } @@ -330,45 +323,43 @@ function Start-PodeWebServer # script to queue messages from clients to send back to other clients from the server $clientScript = { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] $Listener, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [int] $ThreadId ) try { - while ($Listener.IsConnected -and !$PodeContext.Tokens.Cancellation.IsCancellationRequested) - { + while ($Listener.IsConnected -and !$PodeContext.Tokens.Cancellation.IsCancellationRequested) { $context = (Wait-PodeTask -Task $Listener.GetClientSignalAsync($PodeContext.Tokens.Cancellation.Token)) - try - { + try { $payload = ($context.Message | ConvertFrom-Json) $Request = $context.Signal.Context.Request $Response = $context.Signal.Context.Response $SignalEvent = @{ - Response = $Response - Request = $Request - Lockable = $PodeContext.Threading.Lockables.Global - Path = [System.Web.HttpUtility]::UrlDecode($Request.Url.AbsolutePath) - Data = @{ - Path = [System.Web.HttpUtility]::UrlDecode($payload.path) - Message = $payload.message + Response = $Response + Request = $Request + Lockable = $PodeContext.Threading.Lockables.Global + Path = [System.Web.HttpUtility]::UrlDecode($Request.Url.AbsolutePath) + Data = @{ + Path = [System.Web.HttpUtility]::UrlDecode($payload.path) + Message = $payload.message ClientId = $payload.clientId - Direct = [bool]$payload.direct + Direct = [bool]$payload.direct } - Endpoint = @{ + Endpoint = @{ Protocol = $Request.Url.Scheme - Address = $Request.Host - Name = $null + Address = $Request.Host + Name = $null } - Route = $null - ClientId = $context.Signal.ClientId + Route = $null + ClientId = $context.Signal.ClientId Timestamp = $context.Timestamp - Streamed = $true + Streamed = $true } # endpoint name @@ -413,7 +404,7 @@ function Start-PodeWebServer # script to keep web server listening until cancelled $waitScript = { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [ValidateNotNull()] $Listener ) @@ -447,18 +438,17 @@ function Start-PodeWebServer } return @(foreach ($endpoint in $endpoints) { - @{ - Url = $endpoint.Url - Pool = $endpoint.Pool - } - }) + @{ + Url = $endpoint.Url + Pool = $endpoint.Pool + } + }) } -function New-PodeListener -{ +function New-PodeListener { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [System.Threading.CancellationToken] $CancellationToken ) @@ -466,15 +456,14 @@ function New-PodeListener return [PodeListener]::new($CancellationToken) } -function New-PodeListenerSocket -{ +function New-PodeListenerSocket { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [ipaddress] $Address, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [int] $Port, @@ -482,7 +471,7 @@ function New-PodeListenerSocket [System.Security.Authentication.SslProtocols] $SslProtocols, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [PodeProtocolType] $Type, diff --git a/src/Private/Responses.ps1 b/src/Private/Responses.ps1 index 7e8f955ca..e350c8d5a 100644 --- a/src/Private/Responses.ps1 +++ b/src/Private/Responses.ps1 @@ -1,5 +1,4 @@ -function Show-PodeErrorPage -{ +function Show-PodeErrorPage { param( [Parameter()] [int] @@ -38,8 +37,8 @@ function Show-PodeErrorPage # setup the data object for dynamic pages $data = @{ - Url = [System.Web.HttpUtility]::HtmlEncode((Get-PodeUrl)) - Status = @{ + Url = [System.Web.HttpUtility]::HtmlEncode((Get-PodeUrl)) + Status = @{ Code = $Code Description = $Description } diff --git a/src/Private/Routes.ps1 b/src/Private/Routes.ps1 index c46ea6d8e..427b115d2 100644 --- a/src/Private/Routes.ps1 +++ b/src/Private/Routes.ps1 @@ -1,12 +1,11 @@ -function Test-PodeRouteFromRequest -{ +function Test-PodeRouteFromRequest { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [ValidateSet('CONNECT', 'DELETE', 'GET', 'HEAD', 'MERGE', 'OPTIONS', 'PATCH', 'POST', 'PUT', 'TRACE', 'STATIC', 'SIGNAL', '*')] [string] $Method, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $Path, @@ -23,15 +22,14 @@ function Test-PodeRouteFromRequest return ($null -ne $route) } -function Find-PodeRoute -{ +function Find-PodeRoute { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [ValidateSet('CONNECT', 'DELETE', 'GET', 'HEAD', 'MERGE', 'OPTIONS', 'PATCH', 'POST', 'PUT', 'TRACE', 'STATIC', 'SIGNAL', '*')] [string] $Method, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $Path, @@ -69,11 +67,11 @@ function Find-PodeRoute # otherwise, match the path to routes on regex (first match only) $valid = @(foreach ($key in $_method.Keys) { - if ($Path -imatch "^$($key)$") { - $key - break - } - })[0] + if ($Path -imatch "^$($key)$") { + $key + break + } + })[0] if ($null -eq $valid) { return $null @@ -88,10 +86,9 @@ function Find-PodeRoute return $found } -function Find-PodePublicRoute -{ +function Find-PodePublicRoute { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Path ) @@ -116,10 +113,9 @@ function Find-PodePublicRoute return $source } -function Find-PodeStaticRoute -{ +function Find-PodeStaticRoute { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Path, @@ -145,8 +141,7 @@ function Find-PodeStaticRoute } # if there's no file, we need to check defaults - if (!$found.Download -and !(Test-PodePathIsFile $file) -and (Get-PodeCount @($found.Defaults)) -gt 0) - { + if (!$found.Download -and !(Test-PodePathIsFile $file) -and (Get-PodeCount @($found.Defaults)) -gt 0) { if ((Get-PodeCount @($found.Defaults)) -eq 1) { $file = [System.IO.Path]::Combine($file, @($found.Defaults)[0]) } @@ -178,18 +173,17 @@ function Find-PodeStaticRoute # return the route details return @{ Content = @{ - Source = $source + Source = $source IsDownload = $download IsCachable = (Test-PodeRouteValidForCaching -Path $Path) } - Route = $found + Route = $found } } -function Find-PodeSignalRoute -{ +function Find-PodeSignalRoute { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Path, @@ -202,10 +196,9 @@ function Find-PodeSignalRoute return (Find-PodeRoute -Method 'signal' -Path $Path -EndpointName $EndpointName) } -function Test-PodeRouteValidForCaching -{ +function Test-PodeRouteValidForCaching { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Path ) @@ -228,8 +221,7 @@ function Test-PodeRouteValidForCaching return $caching } -function Get-PodeRouteByUrl -{ +function Get-PodeRouteByUrl { param( [Parameter()] [hashtable[]] @@ -249,8 +241,7 @@ function Get-PodeRouteByUrl return (Get-PodeRoutesByUrl -Routes $Routes -EndpointName $EndpointName) } -function Get-PodeRoutesByUrl -{ +function Get-PodeRoutesByUrl { param( [Parameter()] [hashtable[]] @@ -280,10 +271,9 @@ function Get-PodeRoutesByUrl return $null } -function ConvertTo-PodeOpenApiRoutePath -{ +function ConvertTo-PodeOpenApiRoutePath { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Path ) @@ -291,10 +281,9 @@ function ConvertTo-PodeOpenApiRoutePath return (Resolve-PodePlaceholders -Path $Path -Pattern '\:(?[\w]+)' -Prepend '{' -Append '}') } -function Update-PodeRouteSlashes -{ +function Update-PodeRouteSlashes { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Path, @@ -321,19 +310,17 @@ function Update-PodeRouteSlashes return $Path } -function Split-PodeRouteQuery -{ +function Split-PodeRouteQuery { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Path ) - return ($Path -isplit "\?")[0] + return ($Path -isplit '\?')[0] } -function ConvertTo-PodeRouteRegex -{ +function ConvertTo-PodeRouteRegex { param( [Parameter()] [string] @@ -353,8 +340,7 @@ function ConvertTo-PodeRouteRegex return $Path } -function Get-PodeStaticRouteDefaults -{ +function Get-PodeStaticRouteDefaults { if (!(Test-PodeIsEmpty $PodeContext.Server.Web.Static.Defaults)) { return @($PodeContext.Server.Web.Static.Defaults) } @@ -367,14 +353,13 @@ function Get-PodeStaticRouteDefaults ) } -function Test-PodeRouteInternal -{ +function Test-PodeRouteInternal { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Method, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Path, @@ -427,8 +412,7 @@ function Test-PodeRouteInternal throw "[$($Method)] $($Path): Already defined for $($_url)" } -function Convert-PodeFunctionVerbToHttpMethod -{ +function Convert-PodeFunctionVerbToHttpMethod { param( [Parameter()] [string] @@ -445,10 +429,9 @@ function Convert-PodeFunctionVerbToHttpMethod } } -function Find-PodeRouteTransferEncoding -{ +function Find-PodeRouteTransferEncoding { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Path, @@ -467,8 +450,8 @@ function Find-PodeRouteTransferEncoding # find type by pattern from settings $matched = ($PodeContext.Server.Web.TransferEncoding.Routes.Keys | Where-Object { - $Path -imatch $_ - } | Select-Object -First 1) + $Path -imatch $_ + } | Select-Object -First 1) # if we get a match, set it if (!(Test-PodeIsEmpty $matched)) { @@ -478,10 +461,9 @@ function Find-PodeRouteTransferEncoding return $TransferEncoding } -function Find-PodeRouteContentType -{ +function Find-PodeRouteContentType { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Path, @@ -500,8 +482,8 @@ function Find-PodeRouteContentType # find type by pattern from settings $matched = ($PodeContext.Server.Web.ContentType.Routes.Keys | Where-Object { - $Path -imatch $_ - } | Select-Object -First 1) + $Path -imatch $_ + } | Select-Object -First 1) # if we get a match, set it if (!(Test-PodeIsEmpty $matched)) { @@ -511,15 +493,14 @@ function Find-PodeRouteContentType return $ContentType } -function ConvertTo-PodeMiddleware -{ +function ConvertTo-PodeMiddleware { [OutputType([hashtable[]])] param( [Parameter()] [object[]] $Middleware, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [System.Management.Automation.SessionState] $PSSession ) @@ -545,7 +526,7 @@ function ConvertTo-PodeMiddleware # if middleware is hashtable, ensure the keys are valid (logic is a scriptblock) if ($mid -is [hashtable]) { if ($null -eq $mid.Logic) { - throw "A Hashtable Middleware supplied has no Logic defined" + throw 'A Hashtable Middleware supplied has no Logic defined' } if ($mid.Logic -isnot [scriptblock]) { @@ -556,27 +537,26 @@ function ConvertTo-PodeMiddleware # if we have middleware, convert scriptblocks to hashtables $converted = @(for ($i = 0; $i -lt $Middleware.Length; $i++) { - if ($null -eq $Middleware[$i]) { - continue - } + if ($null -eq $Middleware[$i]) { + continue + } - if ($Middleware[$i] -is [scriptblock]) { - $_script, $_usingVars = Convert-PodeScopedVariables -ScriptBlock $Middleware[$i] -PSSession $PSSession + if ($Middleware[$i] -is [scriptblock]) { + $_script, $_usingVars = Convert-PodeScopedVariables -ScriptBlock $Middleware[$i] -PSSession $PSSession - $Middleware[$i] = @{ - Logic = $_script - UsingVariables = $_usingVars + $Middleware[$i] = @{ + Logic = $_script + UsingVariables = $_usingVars + } } - } - $Middleware[$i] - }) + $Middleware[$i] + }) return $converted } -function Get-PodeRouteIfExistsPreference -{ +function Get-PodeRouteIfExistsPreference { # from route groups $groupPref = $RouteGroup.IfExists if (![string]::IsNullOrWhiteSpace($groupPref) -and ($groupPref -ine 'default')) { diff --git a/src/Private/Schedules.ps1 b/src/Private/Schedules.ps1 index 1cdfe2354..bcdea1092 100644 --- a/src/Private/Schedules.ps1 +++ b/src/Private/Schedules.ps1 @@ -1,7 +1,6 @@ -function Find-PodeSchedule -{ +function Find-PodeSchedule { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $Name @@ -10,13 +9,11 @@ function Find-PodeSchedule return $PodeContext.Schedules.Items[$Name] } -function Test-PodeSchedulesExist -{ +function Test-PodeSchedulesExist { return (($null -ne $PodeContext.Schedules) -and (($PodeContext.Schedules.Enabled) -or ($PodeContext.Schedules.Items.Count -gt 0))) } -function Start-PodeScheduleRunspace -{ +function Start-PodeScheduleRunspace { if (!(Test-PodeSchedulesExist)) { return } @@ -58,8 +55,7 @@ function Start-PodeScheduleRunspace # first, sleep for a period of time to get to 00 seconds (start of minute) Start-Sleep -Seconds (60 - [DateTime]::Now.Second) - while (!$PodeContext.Tokens.Cancellation.IsCancellationRequested) - { + while (!$PodeContext.Tokens.Cancellation.IsCancellationRequested) { $_now = [DateTime]::Now # select the schedules that need triggering @@ -84,8 +80,7 @@ function Start-PodeScheduleRunspace Add-PodeRunspace -Type Main -ScriptBlock $script -NoProfile } -function Close-PodeScheduleInternal -{ +function Close-PodeScheduleInternal { param( [Parameter()] [hashtable] @@ -100,17 +95,16 @@ function Close-PodeScheduleInternal $null = $PodeContext.Schedules.Processes.Remove($Process.ID) } -function Complete-PodeInternalSchedules -{ +function Complete-PodeInternalSchedules { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [datetime] $Now ) # add any schedules to remove that have exceeded their end time $Schedules = @($PodeContext.Schedules.Items.Values | - Where-Object { (($null -ne $_.EndTime) -and ($_.EndTime -lt $Now)) }) + Where-Object { (($null -ne $_.EndTime) -and ($_.EndTime -lt $Now)) }) if (($null -eq $Schedules) -or ($Schedules.Length -eq 0)) { return @@ -122,10 +116,9 @@ function Complete-PodeInternalSchedules } } -function Invoke-PodeInternalSchedule -{ +function Invoke-PodeInternalSchedule { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] $Schedule ) @@ -160,10 +153,9 @@ function Invoke-PodeInternalSchedule Invoke-PodeInternalScheduleLogic -Schedule $Schedule } -function Invoke-PodeInternalScheduleLogic -{ +function Invoke-PodeInternalScheduleLogic { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] $Schedule, [Parameter()] @@ -176,7 +168,7 @@ function Invoke-PodeInternalScheduleLogic $parameters = @{ Event = @{ Lockable = $PodeContext.Threading.Lockables.Global - Sender = $Schedule + Sender = $Schedule } } @@ -203,7 +195,7 @@ function Invoke-PodeInternalScheduleLogic $runspace = Add-PodeRunspace -Type Schedules -ScriptBlock (($Schedule.Script).GetNewClosure()) -Parameters $parameters -PassThru $PodeContext.Schedules.Processes[$name] = @{ - ID = $name + ID = $name Schedule = $Schedule.Name Runspace = $runspace } diff --git a/src/Private/Secrets.ps1 b/src/Private/Secrets.ps1 index 596f1610b..ebf15abce 100644 --- a/src/Private/Secrets.ps1 +++ b/src/Private/Secrets.ps1 @@ -1,11 +1,10 @@ -function Initialize-PodeSecretVault -{ +function Initialize-PodeSecretVault { param( - [Parameter(Mandatory=$true, ValueFromPipeline=$true)] + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [hashtable] $VaultConfig, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [scriptblock] $ScriptBlock ) @@ -13,10 +12,9 @@ function Initialize-PodeSecretVault Invoke-PodeScriptBlock -ScriptBlock $ScriptBlock -Splat -Arguments @($VaultConfig.Parameters) } -function Register-PodeSecretManagementVault -{ +function Register-PodeSecretManagementVault { param( - [Parameter(Mandatory=$true, ValueFromPipeline=$true)] + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [hashtable] $VaultConfig, @@ -24,7 +22,7 @@ function Register-PodeSecretManagementVault [string] $VaultName, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $ModuleName ) @@ -43,19 +41,18 @@ function Register-PodeSecretManagementVault # all is good, so set the config $VaultConfig['SecretManagement'] = @{ - VaultName = $VaultName + VaultName = $VaultName ModuleName = $ModuleName } } -function Register-PodeSecretCustomVault -{ +function Register-PodeSecretCustomVault { param( - [Parameter(Mandatory=$true, ValueFromPipeline=$true)] + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [hashtable] $VaultConfig, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [scriptblock] $ScriptBlock, @@ -83,18 +80,17 @@ function Register-PodeSecretCustomVault # all is good, so set the config $VaultConfig['Custom'] = @{ - Read = $ScriptBlock - Unlock = $UnlockScriptBlock - Remove = $RemoveScriptBlock - Set = $SetScriptBlock + Read = $ScriptBlock + Unlock = $UnlockScriptBlock + Remove = $RemoveScriptBlock + Set = $SetScriptBlock Unregister = $UnregisterScriptBlock } } -function Unlock-PodeSecretManagementVault -{ +function Unlock-PodeSecretManagementVault { param( - [Parameter(Mandatory=$true, ValueFromPipeline=$true)] + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [hashtable] $VaultConfig ) @@ -115,10 +111,9 @@ function Unlock-PodeSecretManagementVault return $null } -function Unlock-PodeSecretCustomVault -{ +function Unlock-PodeSecretCustomVault { param( - [Parameter(Mandatory=$true, ValueFromPipeline=$true)] + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [hashtable] $VaultConfig ) @@ -135,9 +130,9 @@ function Unlock-PodeSecretCustomVault # unlock the vault, and get back an expiry $expiry = (Invoke-PodeScriptBlock -ScriptBlock $VaultConfig.Custom.Unlock -Splat -Return -Arguments @( - $VaultConfig.Parameters, + $VaultConfig.Parameters, (ConvertFrom-SecureString -SecureString $VaultConfig.Unlock.Secret -AsPlainText) - )) + )) # return expiry if given, otherwise check interval if ($null -ne $expiry) { @@ -151,10 +146,9 @@ function Unlock-PodeSecretCustomVault return $null } -function Unregister-PodeSecretManagementVault -{ +function Unregister-PodeSecretManagementVault { param( - [Parameter(Mandatory=$true, ValueFromPipeline=$true)] + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [hashtable] $VaultConfig ) @@ -168,10 +162,9 @@ function Unregister-PodeSecretManagementVault $null = Unregister-SecretVault -Name $VaultConfig.SecretManagement.VaultName -Confirm:$false -ErrorAction Stop } -function Unregister-PodeSecretCustomVault -{ +function Unregister-PodeSecretCustomVault { param( - [Parameter(Mandatory=$true, ValueFromPipeline=$true)] + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [hashtable] $VaultConfig ) @@ -192,14 +185,13 @@ function Unregister-PodeSecretCustomVault ) } -function Get-PodeSecretManagementKey -{ +function Get-PodeSecretManagementKey { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Vault, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Key ) @@ -211,14 +203,13 @@ function Get-PodeSecretManagementKey return (Get-Secret -Name $Key -Vault $_vault.SecretManagement.VaultName -AsPlainText -ErrorAction Stop) } -function Get-PodeSecretCustomKey -{ +function Get-PodeSecretCustomKey { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Vault, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Key, @@ -232,23 +223,22 @@ function Get-PodeSecretCustomKey # fetch the secret return (Invoke-PodeScriptBlock -ScriptBlock $_vault.Custom.Read -Splat -Return -Arguments (@( - $_vault.Parameters, - $Key - ) + $ArgumentList)) + $_vault.Parameters, + $Key + ) + $ArgumentList)) } -function Set-PodeSecretManagementKey -{ +function Set-PodeSecretManagementKey { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Vault, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Key, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [object] $Value, @@ -264,18 +254,17 @@ function Set-PodeSecretManagementKey $null = Set-Secret -Name $Key -Secret $Value -Vault $_vault.SecretManagement.VaultName -Metadata $Metadata -Confirm:$false -ErrorAction Stop } -function Set-PodeSecretCustomKey -{ +function Set-PodeSecretCustomKey { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Vault, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Key, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [object] $Value, @@ -298,21 +287,20 @@ function Set-PodeSecretCustomKey # set the secret Invoke-PodeScriptBlock -ScriptBlock $_vault.Custom.Set -Splat -Arguments (@( - $_vault.Parameters, - $Key, - $Value, - $Metadata - ) + $ArgumentList) + $_vault.Parameters, + $Key, + $Value, + $Metadata + ) + $ArgumentList) } -function Remove-PodeSecretManagementKey -{ +function Remove-PodeSecretManagementKey { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Vault, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Key ) @@ -324,14 +312,13 @@ function Remove-PodeSecretManagementKey $null = Remove-Secret -Name $Key -Vault $_vault.SecretManagement.VaultName -Confirm:$false -ErrorAction Stop } -function Remove-PodeSecretCustomKey -{ +function Remove-PodeSecretCustomKey { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Vault, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Key, @@ -350,13 +337,12 @@ function Remove-PodeSecretCustomKey # remove the secret Invoke-PodeScriptBlock -ScriptBlock $_vault.Custom.Remove -Splat -Arguments (@( - $_vault.Parameters, - $Key - ) + $ArgumentList) + $_vault.Parameters, + $Key + ) + $ArgumentList) } -function Start-PodeSecretCacheHousekeeper -{ +function Start-PodeSecretCacheHousekeeper { if (Test-PodeTimer -Name '__pode_secrets_cache_expiry__') { return } @@ -375,8 +361,7 @@ function Start-PodeSecretCacheHousekeeper } } -function Start-PodeSecretVaultUnlocker -{ +function Start-PodeSecretVaultUnlocker { if (Test-PodeTimer -Name '__pode_secrets_vault_unlock__') { return } @@ -394,8 +379,7 @@ function Start-PodeSecretVaultUnlocker } } -function Unregister-PodeSecretVaults -{ +function Unregister-PodeSecretVaults { param( [switch] $ThrowError @@ -424,10 +408,9 @@ function Unregister-PodeSecretVaults } } -function Protect-PodeSecretValueType -{ +function Protect-PodeSecretValueType { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [object] $Value ) @@ -451,7 +434,7 @@ function Protect-PodeSecretValueType ($Value -is [byte[]]) -or ($Value -is [pscredential]) -or ($Value -is [System.Management.Automation.OrderedHashtable]) - )) { + )) { throw "Value to set secret to is of an invalid type. Expected either String, SecureString, HashTable, Byte[], or PSCredential. But got: $($Value.GetType().Name)" } diff --git a/src/Private/Security.ps1 b/src/Private/Security.ps1 index 17291deee..d8521cfb7 100644 --- a/src/Private/Security.ps1 +++ b/src/Private/Security.ps1 @@ -1,9 +1,8 @@ using namespace System.Security.Cryptography -function Test-PodeIPLimit -{ +function Test-PodeIPLimit { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [ValidateNotNull()] $IP ) @@ -23,7 +22,7 @@ function Test-PodeIPLimit $IP = @{ String = $IP.IPAddressToString Family = $IP.AddressFamily - Bytes = $IP.GetAddressBytes() + Bytes = $IP.GetAddressBytes() } # now @@ -33,17 +32,17 @@ function Test-PodeIPLimit $_active_ip = $active[$IP.String] if ($null -eq $_active_ip) { $_groups = @(foreach ($key in $active.Keys) { - if ($active[$key].Rule.Grouped) { - $active[$key] - } - }) + if ($active[$key].Rule.Grouped) { + $active[$key] + } + }) $_active_ip = @(foreach ($_group in $_groups) { - if (Test-PodeIPAddressInRange -IP $IP -LowerIP $_group.Rule.Lower -UpperIP $_group.Rule.Upper) { - $_group - break - } - })[0] + if (Test-PodeIPAddressInRange -IP $IP -LowerIP $_group.Rule.Lower -UpperIP $_group.Rule.Upper) { + $_group + break + } + })[0] } # the ip is active, or part of a grouped subnet @@ -73,11 +72,11 @@ function Test-PodeIPLimit else { # get the ip's rule $_rule_ip = @(foreach ($rule in $rules.Values) { - if (Test-PodeIPAddressInRange -IP $IP -LowerIP $rule.Lower -UpperIP $rule.Upper) { - $rule - break - } - })[0] + if (Test-PodeIPAddressInRange -IP $IP -LowerIP $rule.Lower -UpperIP $rule.Upper) { + $rule + break + } + })[0] # if ip not in rules, it's valid # (add to active list as always allowed - saves running where search everytime) @@ -95,8 +94,8 @@ function Test-PodeIPLimit $_ip = (Resolve-PodeValue -Check $_rule_ip.Grouped -TrueValue $_rule_ip.IP -FalseValue $IP.String) $active[$_ip] = @{ - Rule = $_rule_ip - Rate = 1 + Rule = $_rule_ip + Rate = 1 Expire = $now.AddSeconds($_rule_ip.Seconds) } @@ -105,10 +104,9 @@ function Test-PodeIPLimit } } -function Test-PodeRouteLimit -{ - param ( - [Parameter(Mandatory=$true)] +function Test-PodeRouteLimit { + param( + [Parameter(Mandatory = $true)] [ValidateNotNull()] [string] $Path @@ -172,8 +170,8 @@ function Test-PodeRouteLimit # add route to active list $active[$Path] = @{ - Rule = $_rule_route - Rate = 1 + Rule = $_rule_route + Rate = 1 Expire = $now.AddSeconds($_rule_route.Seconds) } @@ -182,9 +180,8 @@ function Test-PodeRouteLimit } } -function Test-PodeEndpointLimit -{ - param ( +function Test-PodeEndpointLimit { + param( [Parameter()] [string] $EndpointName @@ -252,8 +249,8 @@ function Test-PodeEndpointLimit # add endpoint to active list $active[$EndpointName] = @{ - Rule = $_rule_endpoint - Rate = 1 + Rule = $_rule_endpoint + Rate = 1 Expire = $now.AddSeconds($_rule_endpoint.Seconds) } @@ -262,10 +259,9 @@ function Test-PodeEndpointLimit } } -function Test-PodeIPAccess -{ +function Test-PodeIPAccess { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [ValidateNotNull()] $IP ) @@ -288,17 +284,17 @@ function Test-PodeIPAccess # get the ip address in bytes $IP = @{ Family = $IP.AddressFamily - Bytes = $IP.GetAddressBytes() + Bytes = $IP.GetAddressBytes() } # if value in allow, it's allowed if (!$alEmpty) { $match = @(foreach ($value in $allow.Values) { - if (Test-PodeIPAddressInRange -IP $IP -LowerIP $value.Lower -UpperIP $value.Upper) { - $value - break - } - })[0] + if (Test-PodeIPAddressInRange -IP $IP -LowerIP $value.Lower -UpperIP $value.Upper) { + $value + break + } + })[0] if ($null -ne $match) { return $true @@ -308,11 +304,11 @@ function Test-PodeIPAccess # if value in deny, it's disallowed if (!$dnEmpty) { $match = @(foreach ($value in $deny.Values) { - if (Test-PodeIPAddressInRange -IP $IP -LowerIP $value.Lower -UpperIP $value.Upper) { - $value - break - } - })[0] + if (Test-PodeIPAddressInRange -IP $IP -LowerIP $value.Lower -UpperIP $value.Upper) { + $value + break + } + })[0] if ($null -ne $match) { return $false @@ -328,19 +324,18 @@ function Test-PodeIPAccess return $true } -function Add-PodeIPLimit -{ - param ( - [Parameter(Mandatory=$true)] +function Add-PodeIPLimit { + param( + [Parameter(Mandatory = $true)] [ValidateNotNull()] [string] $IP, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [int] $Limit, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [int] $Seconds, @@ -392,34 +387,33 @@ function Add-PodeIPLimit # add limit rule for ip $rules.Add($IP, @{ - Limit = $Limit - Seconds = $Seconds - Grouped = [bool]$Group - IP = $IP - Lower = @{ - Family = $_tmpLo.AddressFamily - Bytes = $_tmpLo.GetAddressBytes() - } - Upper = @{ - Family = $_tmpHi.AddressFamily - Bytes = $_tmpHi.GetAddressBytes() - } - }) + Limit = $Limit + Seconds = $Seconds + Grouped = [bool]$Group + IP = $IP + Lower = @{ + Family = $_tmpLo.AddressFamily + Bytes = $_tmpLo.GetAddressBytes() + } + Upper = @{ + Family = $_tmpHi.AddressFamily + Bytes = $_tmpHi.GetAddressBytes() + } + }) } -function Add-PodeRouteLimit -{ - param ( - [Parameter(Mandatory=$true)] +function Add-PodeRouteLimit { + param( + [Parameter(Mandatory = $true)] [ValidateNotNull()] [string] $Path, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [int] $Limit, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [int] $Seconds, @@ -456,26 +450,25 @@ function Add-PodeRouteLimit # add limit rule for the route $rules.Add($Path, @{ - Limit = $Limit - Seconds = $Seconds - Grouped = [bool]$Group - Path = $Path - }) + Limit = $Limit + Seconds = $Seconds + Grouped = [bool]$Group + Path = $Path + }) } -function Add-PodeEndpointLimit -{ +function Add-PodeEndpointLimit { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [ValidateNotNull()] [string] $EndpointName, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [int] $Limit, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [int] $Seconds, @@ -537,22 +530,21 @@ function Add-PodeEndpointLimit # add limit rule for the endpoint $rules.Add($EndpointName, @{ - Limit = $Limit - Seconds = $Seconds - Grouped = [bool]$Group - EndpointName = $EndpointName - }) + Limit = $Limit + Seconds = $Seconds + Grouped = [bool]$Group + EndpointName = $EndpointName + }) } -function Add-PodeIPAccess -{ - param ( - [Parameter(Mandatory=$true)] +function Add-PodeIPAccess { + param( + [Parameter(Mandatory = $true)] [ValidateSet('Allow', 'Deny')] [string] $Access, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $IP ) @@ -600,19 +592,18 @@ function Add-PodeIPAccess # add access rule for ip $permType.Add($IP, @{ - Lower = @{ - Family = $_tmpLo.AddressFamily - Bytes = $_tmpLo.GetAddressBytes() - } - Upper = @{ - Family = $_tmpHi.AddressFamily - Bytes = $_tmpHi.GetAddressBytes() - } - }) + Lower = @{ + Family = $_tmpLo.AddressFamily + Bytes = $_tmpLo.GetAddressBytes() + } + Upper = @{ + Family = $_tmpHi.AddressFamily + Bytes = $_tmpHi.GetAddressBytes() + } + }) } -function Get-PodeCsrfToken -{ +function Get-PodeCsrfToken { # key name to search $key = $PodeContext.Server.Cookies.Csrf.Name @@ -635,9 +626,8 @@ function Get-PodeCsrfToken return $null } -function Test-PodeCsrfToken -{ - param ( +function Test-PodeCsrfToken { + param( [Parameter()] [string] $Secret, @@ -674,8 +664,7 @@ function Test-PodeCsrfToken return $true } -function New-PodeCsrfSecret -{ +function New-PodeCsrfSecret { # see if there's already a secret in session/cookie $secret = (Get-PodeCsrfSecret) if (!(Test-PodeIsEmpty $secret)) { @@ -688,16 +677,16 @@ function New-PodeCsrfSecret return $secret } -function Get-PodeCsrfSecret -{ +function Get-PodeCsrfSecret { # key name to get secret $key = $PodeContext.Server.Cookies.Csrf.Name # are we getting it from a cookie, or session? if ($PodeContext.Server.Cookies.Csrf.UseCookies) { - return (Get-PodeCookie ` + $cookie = Get-PodeCookie ` -Name $PodeContext.Server.Cookies.Csrf.Name ` - -Secret $PodeContext.Server.Cookies.Csrf.Secret).Value + -Secret $PodeContext.Server.Cookies.Csrf.Secret + return $cookie.Value } # on session @@ -706,10 +695,9 @@ function Get-PodeCsrfSecret } } -function Set-PodeCsrfSecret -{ - param ( - [Parameter(Mandatory=$true)] +function Set-PodeCsrfSecret { + param( + [Parameter(Mandatory = $true)] [string] $Secret ) @@ -731,14 +719,13 @@ function Set-PodeCsrfSecret } } -function Restore-PodeCsrfToken -{ - param ( - [Parameter(Mandatory=$true)] +function Restore-PodeCsrfToken { + param( + [Parameter(Mandatory = $true)] [string] $Secret, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Salt ) @@ -746,15 +733,13 @@ function Restore-PodeCsrfToken return "t:$($Salt).$(Invoke-PodeSHA256Hash -Value "$($Salt)-$($Secret)")" } -function Test-PodeCsrfConfigured -{ +function Test-PodeCsrfConfigured { return (!(Test-PodeIsEmpty $PodeContext.Server.Cookies.Csrf)) } -function Get-PodeCertificateByFile -{ +function Get-PodeCertificateByFile { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Certificate, @@ -783,10 +768,9 @@ function Get-PodeCertificateByFile return [X509Certificates.X509Certificate2]::new($path) } -function Get-PodeCertificateByPemFile -{ +function Get-PodeCertificateByPemFile { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Certificate, @@ -864,29 +848,28 @@ function Get-PodeCertificateByPemFile return $cert } -function Find-PodeCertificateInCertStore -{ +function Find-PodeCertificateInCertStore { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [X509Certificates.X509FindType] $FindType, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Query, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [X509Certificates.StoreName] $StoreName, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [X509Certificates.StoreLocation] $StoreLocation ) # fail if not windows if (!(Test-PodeIsWindows)) { - throw "Certificate Thumbprints/Name are only supported on Windows" + throw 'Certificate Thumbprints/Name are only supported on Windows' } # open the currentuser\my store @@ -912,54 +895,51 @@ function Find-PodeCertificateInCertStore return ([X509Certificates.X509Certificate2]($x509certs[0])) } -function Get-PodeCertificateByThumbprint -{ +function Get-PodeCertificateByThumbprint { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Thumbprint, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [X509Certificates.StoreName] $StoreName, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [X509Certificates.StoreLocation] $StoreLocation ) - return (Find-PodeCertificateInCertStore ` + return Find-PodeCertificateInCertStore ` -FindType ([X509Certificates.X509FindType]::FindByThumbprint) ` -Query $Thumbprint ` -StoreName $StoreName ` - -StoreLocation $StoreLocation) + -StoreLocation $StoreLocation } -function Get-PodeCertificateByName -{ +function Get-PodeCertificateByName { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [X509Certificates.StoreName] $StoreName, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [X509Certificates.StoreLocation] $StoreLocation ) - return (Find-PodeCertificateInCertStore ` + return Find-PodeCertificateInCertStore ` -FindType ([X509Certificates.X509FindType]::FindBySubjectName) ` -Query $Name ` -StoreName $StoreName ` - -StoreLocation $StoreLocation) + -StoreLocation $StoreLocation } -function New-PodeSelfSignedCertificate -{ +function New-PodeSelfSignedCertificate { $sanBuilder = [X509Certificates.SubjectAlternativeNameBuilder]::new() $null = $sanBuilder.AddIpAddress([ipaddress]::Loopback) $null = $sanBuilder.AddIpAddress([ipaddress]::IPv6Loopback) @@ -970,7 +950,7 @@ function New-PodeSelfSignedCertificate } $rsa = [RSA]::Create(2048) - $distinguishedName = [X500DistinguishedName]::new("CN=localhost") + $distinguishedName = [X500DistinguishedName]::new('CN=localhost') $req = [X509Certificates.CertificateRequest]::new( $distinguishedName, @@ -1021,10 +1001,9 @@ function New-PodeSelfSignedCertificate return $cert } -function Protect-PodeContentSecurityKeyword -{ +function Protect-PodeContentSecurityKeyword { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name, @@ -1069,26 +1048,25 @@ function Protect-PodeContentSecurityKeyword # build the value $values = @(foreach ($v in $Value) { - if ($keywords -icontains $v) { - "'$($v.ToLowerInvariant())'" - continue - } + if ($keywords -icontains $v) { + "'$($v.ToLowerInvariant())'" + continue + } - if ($schemes -icontains $v) { - "$($v.ToLowerInvariant()):" - continue - } + if ($schemes -icontains $v) { + "$($v.ToLowerInvariant()):" + continue + } - $v - }) + $v + }) return "$($Name) $($values -join ' ')" } -function Protect-PodePermissionsPolicyKeyword -{ +function Protect-PodePermissionsPolicyKeyword { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name, @@ -1126,13 +1104,13 @@ function Protect-PodePermissionsPolicyKeyword ) $values = @(foreach ($v in $Value) { - if ($keywords -icontains $v) { - $v - continue - } + if ($keywords -icontains $v) { + $v + continue + } - "`"$($v)`"" - }) + "`"$($v)`"" + }) return "$($Name)=($($values -join ' '))" } \ No newline at end of file diff --git a/src/Private/Server.ps1 b/src/Private/Server.ps1 index c292f8e21..295a62c25 100644 --- a/src/Private/Server.ps1 +++ b/src/Private/Server.ps1 @@ -1,6 +1,5 @@ -function Start-PodeInternalServer -{ - param ( +function Start-PodeInternalServer { + param( [Parameter()] $Request, @@ -8,8 +7,7 @@ function Start-PodeInternalServer $Browse ) - try - { + try { # setup temp drives for internal dirs Add-PodePSInbuiltDrives @@ -50,8 +48,7 @@ function Start-PodeInternalServer New-PodeRunspacePools Open-PodeRunspacePools - if (!$PodeContext.Server.IsServerless) - { + if (!$PodeContext.Server.IsServerless) { # start runspace for loggers Start-PodeLoggingRunspace @@ -81,8 +78,7 @@ function Start-PodeInternalServer # - serverless elseif ($PodeContext.Server.IsServerless) { - switch ($PodeContext.Server.ServerlessType.ToUpperInvariant()) - { + switch ($PodeContext.Server.ServerlessType.ToUpperInvariant()) { 'AZUREFUNCTIONS' { Start-PodeAzFuncServer -Data $Request } @@ -97,8 +93,7 @@ function Start-PodeInternalServer else { # start each server type foreach ($_type in $PodeContext.Server.Types) { - switch ($_type.ToUpperInvariant()) - { + switch ($_type.ToUpperInvariant()) { 'SMTP' { $endpoints += (Start-PodeSmtpServer) } @@ -135,6 +130,9 @@ function Start-PodeInternalServer # set the start time of the server (start and after restart) $PodeContext.Metrics.Server.StartTime = [datetime]::UtcNow + # run running event hooks + Invoke-PodeEvent -Type Running + # state what endpoints are being listened on if ($endpoints.Length -gt 0) { Write-PodeHost "Listening on the following $($endpoints.Length) endpoint(s) [$($PodeContext.Threads.General) thread(s)]:" -ForegroundColor Yellow @@ -148,10 +146,8 @@ function Start-PodeInternalServer } } -function Restart-PodeInternalServer -{ - try - { +function Restart-PodeInternalServer { + try { # inform restart Write-PodeHost 'Restarting server...' -NoNewline -ForegroundColor Cyan @@ -173,9 +169,12 @@ function Restart-PodeInternalServer # clear up timers, schedules and loggers $PodeContext.Server.Routes | Clear-PodeHashtableInnerKeys $PodeContext.Server.Handlers | Clear-PodeHashtableInnerKeys - $PodeContext.Server.Verbs | Clear-PodeHashtableInnerKeys $PodeContext.Server.Events | Clear-PodeHashtableInnerKeys + if ($null -ne $PodeContext.Server.Verbs) { + $PodeContext.Server.Verbs.Clear() + } + $PodeContext.Server.Views.Clear() $PodeContext.Timers.Items.Clear() $PodeContext.Server.Logging.Types.Clear() @@ -226,18 +225,19 @@ function Restart-PodeInternalServer # set view engine back to default $PodeContext.Server.ViewEngine = @{ - Type = 'html' - Extension = 'html' - ScriptBlock = $null + Type = 'html' + Extension = 'html' + ScriptBlock = $null UsingVariables = $null - IsDynamic = $false + IsDynamic = $false } # clear up cookie sessions $PodeContext.Server.Sessions.Clear() # clear up authentication methods - $PodeContext.Server.Authentications.Clear() + $PodeContext.Server.Authentications.Methods.Clear() + $PodeContext.Server.Authorisations.Methods.Clear() # clear up shared state $PodeContext.Server.State.Clear() @@ -268,7 +268,8 @@ function Restart-PodeInternalServer # reload the configuration $PodeContext.Server.Configuration = Open-PodeConfiguration -Context $PodeContext - Write-PodeHost " Done" -ForegroundColor Green + # done message + Write-PodeHost ' Done' -ForegroundColor Green # restart the server $PodeContext.Metrics.Server.RestartCount++ @@ -280,8 +281,7 @@ function Restart-PodeInternalServer } } -function Test-PodeServerKeepOpen -{ +function Test-PodeServerKeepOpen { # if we have any timers/schedules/fim - keep open if ((Test-PodeTimersExist) -or (Test-PodeSchedulesExist) -or (Test-PodeFileWatchersExist)) { return $true diff --git a/src/Private/Serverless.ps1 b/src/Private/Serverless.ps1 index 9d0b56de3..a7aac81ca 100644 --- a/src/Private/Serverless.ps1 +++ b/src/Private/Serverless.ps1 @@ -1,7 +1,6 @@ -function Start-PodeAzFuncServer -{ - param ( - [Parameter(Mandatory=$true)] +function Start-PodeAzFuncServer { + param( + [Parameter(Mandatory = $true)] $Data ) @@ -16,10 +15,8 @@ function Start-PodeAzFuncServer $PodeContext.Server.Middleware = ($inbuilt_middleware + $PodeContext.Server.Middleware) - try - { - try - { + try { + try { # get the request $request = $Data.Request @@ -30,33 +27,33 @@ function Start-PodeAzFuncServer # reset event data $global:WebEvent = @{ - OnEnd = @() - Auth = @{} - Response = $response - Request = $request - Lockable = $PodeContext.Threading.Lockables.Global - Path = [string]::Empty - Method = $request.Method.ToLowerInvariant() - Query = $request.Query - Endpoint = @{ + OnEnd = @() + Auth = @{} + Response = $response + Request = $request + Lockable = $PodeContext.Threading.Lockables.Global + Path = [string]::Empty + Method = $request.Method.ToLowerInvariant() + Query = $request.Query + Endpoint = @{ Protocol = ($request.Url -split '://')[0] - Address = $null - Name = $null + Address = $null + Name = $null } - ContentType = $null - ErrorType = $null - Cookies = @{} - PendingCookies = @{} - Parameters = $null - Data = $null - Files = $null - Streamed = $false - Route = $null - StaticContent = $null - Timestamp = [datetime]::UtcNow + ContentType = $null + ErrorType = $null + Cookies = @{} + PendingCookies = @{} + Parameters = $null + Data = $null + Files = $null + Streamed = $false + Route = $null + StaticContent = $null + Timestamp = [datetime]::UtcNow TransferEncoding = $null - AcceptEncoding = $null - Ranges = $null + AcceptEncoding = $null + Ranges = $null } $WebEvent.Endpoint.Address = ((Get-PodeHeader -Name 'host') -split ':')[0] @@ -82,8 +79,7 @@ function Start-PodeAzFuncServer # invoke global and route middleware if ((Invoke-PodeMiddleware -Middleware $PodeContext.Server.Middleware -Route $WebEvent.Path)) { - if ((Invoke-PodeMiddleware -Middleware $WebEvent.Route.Middleware)) - { + if ((Invoke-PodeMiddleware -Middleware $WebEvent.Route.Middleware)) { # invoke the route if ($null -ne $WebEvent.StaticContent) { if ($WebEvent.StaticContent.IsDownload) { @@ -123,10 +119,9 @@ function Start-PodeAzFuncServer } } -function Start-PodeAwsLambdaServer -{ - param ( - [Parameter(Mandatory=$true)] +function Start-PodeAwsLambdaServer { + param( + [Parameter(Mandatory = $true)] $Data ) @@ -141,49 +136,47 @@ function Start-PodeAwsLambdaServer $PodeContext.Server.Middleware = ($inbuilt_middleware + $PodeContext.Server.Middleware) - try - { - try - { + try { + try { # get the request $request = $Data # setup the response $response = @{ StatusCode = 200 - Headers = @{} - Body = [string]::Empty + Headers = @{} + Body = [string]::Empty } # reset event data $global:WebEvent = @{ - OnEnd = @() - Auth = @{} - Response = $response - Request = $request - Lockable = $PodeContext.Threading.Lockables.Global - Path = [System.Web.HttpUtility]::UrlDecode($request.path) - Method = $request.httpMethod.ToLowerInvariant() - Query = $request.queryStringParameters - Endpoint = @{ + OnEnd = @() + Auth = @{} + Response = $response + Request = $request + Lockable = $PodeContext.Threading.Lockables.Global + Path = [System.Web.HttpUtility]::UrlDecode($request.path) + Method = $request.httpMethod.ToLowerInvariant() + Query = $request.queryStringParameters + Endpoint = @{ Protocol = $null - Address = $null - Name = $null + Address = $null + Name = $null } - ContentType = $null - ErrorType = $null - Cookies = @{} - PendingCookies = @{} - Parameters = $null - Data = $null - Files = $null - Streamed = $false - Route = $null - StaticContent = $null - Timestamp = [datetime]::UtcNow + ContentType = $null + ErrorType = $null + Cookies = @{} + PendingCookies = @{} + Parameters = $null + Data = $null + Files = $null + Streamed = $false + Route = $null + StaticContent = $null + Timestamp = [datetime]::UtcNow TransferEncoding = $null - AcceptEncoding = $null - Ranges = $null + AcceptEncoding = $null + Ranges = $null } $WebEvent.Endpoint.Protocol = (Get-PodeHeader -Name 'X-Forwarded-Proto') @@ -195,8 +188,7 @@ function Start-PodeAwsLambdaServer # invoke global and route middleware if ((Invoke-PodeMiddleware -Middleware $PodeContext.Server.Middleware -Route $WebEvent.Path)) { - if ((Invoke-PodeMiddleware -Middleware $WebEvent.Route.Middleware)) - { + if ((Invoke-PodeMiddleware -Middleware $WebEvent.Route.Middleware)) { # invoke the route if ($null -ne $WebEvent.StaticContent) { if ($WebEvent.StaticContent.IsDownload) { @@ -233,10 +225,10 @@ function Start-PodeAwsLambdaServer } return (@{ - 'statusCode' = $response.StatusCode; - 'headers' = $response.Headers; - 'body' = $response.Body; - } | ConvertTo-Json -Depth 10 -Compress) + 'statusCode' = $response.StatusCode + 'headers' = $response.Headers + 'body' = $response.Body + } | ConvertTo-Json -Depth 10 -Compress) } catch { $_ | Write-PodeErrorLog diff --git a/src/Private/ServiceServer.ps1 b/src/Private/ServiceServer.ps1 index 540f93b7f..736f610fa 100644 --- a/src/Private/ServiceServer.ps1 +++ b/src/Private/ServiceServer.ps1 @@ -1,5 +1,4 @@ -function Start-PodeServiceServer -{ +function Start-PodeServiceServer { # ensure we have service handlers if (Test-PodeIsEmpty (Get-PodeHandler -Type Service)) { throw 'No Service handlers have been defined' @@ -10,10 +9,8 @@ function Start-PodeServiceServer # script for the looping server $serverScript = { - try - { - while (!$PodeContext.Tokens.Cancellation.IsCancellationRequested) - { + try { + while (!$PodeContext.Tokens.Cancellation.IsCancellationRequested) { # the event object $ServiceEvent = @{ Lockable = $PodeContext.Threading.Lockables.Global diff --git a/src/Private/Sessions.ps1 b/src/Private/Sessions.ps1 index 61ca339f4..61ed58f28 100644 --- a/src/Private/Sessions.ps1 +++ b/src/Private/Sessions.ps1 @@ -1,18 +1,16 @@ -function New-PodeSession -{ +function New-PodeSession { return @{ - Name = $PodeContext.Server.Sessions.Name - Id = (Invoke-PodeScriptBlock -ScriptBlock $PodeContext.Server.Sessions.GenerateId -Return) - Extend = $PodeContext.Server.Sessions.Info.Extend + Name = $PodeContext.Server.Sessions.Name + Id = (Invoke-PodeScriptBlock -ScriptBlock $PodeContext.Server.Sessions.GenerateId -Return) + Extend = $PodeContext.Server.Sessions.Info.Extend TimeStamp = [datetime]::UtcNow - Data = @{} + Data = @{} } } -function ConvertTo-PodeSessionStrictSecret -{ +function ConvertTo-PodeSessionStrictSecret { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Secret ) @@ -20,8 +18,7 @@ function ConvertTo-PodeSessionStrictSecret return "$($Secret);$($WebEvent.Request.UserAgent);$($WebEvent.Request.RemoteEndPoint.Address.IPAddressToString)" } -function Set-PodeSession -{ +function Set-PodeSession { if ($null -eq $WebEvent.Session) { throw 'there is no session available to set on the response' } @@ -50,8 +47,7 @@ function Set-PodeSession } } -function Get-PodeSession -{ +function Get-PodeSession { $secret = $PodeContext.Server.Sessions.Secret $value = $null $name = $PodeContext.Server.Sessions.Name @@ -95,18 +91,17 @@ function Get-PodeSession # generate the session data $data = @{ - Name = $name - Id = $value - Extend = $PodeContext.Server.Sessions.Info.Extend + Name = $name + Id = $value + Extend = $PodeContext.Server.Sessions.Info.Extend TimeStamp = $null - Data = @{} + Data = @{} } return $data } -function Revoke-PodeSession -{ +function Revoke-PodeSession { # do nothing if no current session if ($null -eq $WebEvent.Session) { return @@ -124,8 +119,7 @@ function Revoke-PodeSession $WebEvent.Session.Clear() } -function Set-PodeSessionDataHash -{ +function Set-PodeSessionDataHash { if ($null -eq $WebEvent.Session) { throw 'No session available to calculate data hash' } @@ -137,8 +131,7 @@ function Set-PodeSessionDataHash $WebEvent.Session.DataHash = (Invoke-PodeSHA256Hash -Value (ConvertTo-Json -InputObject $WebEvent.Session.Data.Clone() -Depth 10 -Compress)) } -function Test-PodeSessionDataHash -{ +function Test-PodeSessionDataHash { if ($null -eq $WebEvent.Session) { return $false } @@ -155,8 +148,7 @@ function Test-PodeSessionDataHash return ($WebEvent.Session.DataHash -eq $hash) } -function Set-PodeSessionHelpers -{ +function Set-PodeSessionHelpers { if ($null -eq $WebEvent.Session) { throw 'No session available to set helpers' } @@ -186,7 +178,7 @@ function Set-PodeSessionHelpers Metadata = @{ TimeStamp = $session.TimeStamp } - Data = $session.Data + Data = $session.Data } # save session data to store @@ -209,8 +201,7 @@ function Set-PodeSessionHelpers } } -function Get-PodeSessionInMemStore -{ +function Get-PodeSessionInMemStore { $store = New-Object -TypeName psobject # add in-mem storage @@ -242,7 +233,7 @@ function Get-PodeSessionInMemStore param($sessionId, $data, $expiry) $PodeContext.Server.Sessions.Store.Memory[$sessionId] = @{ - Data = $data + Data = $data Expiry = $expiry } } @@ -250,8 +241,7 @@ function Get-PodeSessionInMemStore return $store } -function Set-PodeSessionInMemClearDown -{ +function Set-PodeSessionInMemClearDown { # don't setup if serverless - as memory is short lived anyway if ($PodeContext.Server.IsServerless) { return @@ -274,18 +264,15 @@ function Set-PodeSessionInMemClearDown } } -function Test-PodeSessionsConfigured -{ +function Test-PodeSessionsConfigured { return (($null -ne $PodeContext.Server.Sessions) -and ($PodeContext.Server.Sessions.Count -gt 0)) } -function Test-PodeSessionsInUse -{ +function Test-PodeSessionsInUse { return (($null -ne $WebEvent.Session) -and ($WebEvent.Session.Count -gt 0)) } -function Get-PodeSessionData -{ +function Get-PodeSessionData { param( [Parameter()] [string] @@ -295,16 +282,14 @@ function Get-PodeSessionData return (Invoke-PodeScriptBlock -ScriptBlock $PodeContext.Server.Sessions.Store.Get -Arguments $SessionId -Return) } -function Get-PodeSessionMiddleware -{ +function Get-PodeSessionMiddleware { return { # if session already set, return if ($WebEvent.Session) { return $true } - try - { + try { # retrieve the current session from cookie/header $WebEvent.Session = Get-PodeSession diff --git a/src/Private/Setup.ps1 b/src/Private/Setup.ps1 index 24aa23643..340706864 100644 --- a/src/Private/Setup.ps1 +++ b/src/Private/Setup.ps1 @@ -1,6 +1,5 @@ -function Invoke-PodePackageScript -{ - param ( +function Invoke-PodePackageScript { + param( [Parameter()] [string] $ActionScript @@ -13,9 +12,8 @@ function Invoke-PodePackageScript Invoke-Expression -Command $ActionScript } -function Install-PodeLocalModules -{ - param ( +function Install-PodeLocalModules { + param( [Parameter()] $Modules = $null ) diff --git a/src/Private/SmtpServer.ps1 b/src/Private/SmtpServer.ps1 index eec5e486d..66ddf908c 100644 --- a/src/Private/SmtpServer.ps1 +++ b/src/Private/SmtpServer.ps1 @@ -1,7 +1,6 @@ using namespace Pode -function Start-PodeSmtpServer -{ +function Start-PodeSmtpServer { # ensure we have smtp handlers if (Test-PodeIsEmpty (Get-PodeHandler -Type Smtp)) { throw 'No SMTP handlers have been defined' @@ -18,19 +17,20 @@ function Start-PodeSmtpServer # the endpoint $_endpoint = @{ - Key = "$($_ip):$($_.Port)" - Address = $_ip - Hostname = $_.HostName - IsIPAddress = $_.IsIPAddress - Port = $_.Port - Certificate = $_.Certificate.Raw + Key = "$($_ip):$($_.Port)" + Address = $_ip + Hostname = $_.HostName + IsIPAddress = $_.IsIPAddress + Port = $_.Port + Certificate = $_.Certificate.Raw AllowClientCertificate = $_.Certificate.AllowClientCertificate - TlsMode = $_.Certificate.TlsMode - Url = $_.Url - Protocol = $_.Protocol - Type = $_.Type - Pool = $_.Runspace.PoolName - Acknowledge = $_.Tcp.Acknowledge + TlsMode = $_.Certificate.TlsMode + Url = $_.Url + Protocol = $_.Protocol + Type = $_.Type + Pool = $_.Runspace.PoolName + Acknowledge = $_.Tcp.Acknowledge + SslProtocols = $_.Ssl.Protocols } # add endpoint to list @@ -44,11 +44,10 @@ function Start-PodeSmtpServer $listener.RequestTimeout = $PodeContext.Server.Request.Timeout $listener.RequestBodySize = $PodeContext.Server.Request.BodySize - try - { + try { # register endpoints on the listener $endpoints | ForEach-Object { - $socket = [PodeSocket]::new($_.Address, $_.Port, $PodeContext.Server.Sockets.Ssl.Protocols, [PodeProtocolType]::Smtp, $_.Certificate, $_.AllowClientCertificate, $_.TlsMode) + $socket = [PodeSocket]::new($_.Address, $_.Port, $_.SslProtocols, [PodeProtocolType]::Smtp, $_.Certificate, $_.AllowClientCertificate, $_.TlsMode) $socket.ReceiveTimeout = $PodeContext.Server.Sockets.ReceiveTimeout $socket.AcknowledgeMessage = $_.Acknowledge @@ -71,50 +70,46 @@ function Start-PodeSmtpServer # script for listening out of for incoming requests $listenScript = { - param ( - [Parameter(Mandatory=$true)] + param( + [Parameter(Mandatory = $true)] [ValidateNotNull()] $Listener, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [int] $ThreadId ) - try - { - while ($Listener.IsConnected -and !$PodeContext.Tokens.Cancellation.IsCancellationRequested) - { + try { + while ($Listener.IsConnected -and !$PodeContext.Tokens.Cancellation.IsCancellationRequested) { # get email $context = (Wait-PodeTask -Task $Listener.GetContextAsync($PodeContext.Tokens.Cancellation.Token)) - try - { - try - { + try { + try { $Request = $context.Request $Response = $context.Response $SmtpEvent = @{ - Response = $Response - Request = $Request - Lockable = $PodeContext.Threading.Lockables.Global - Email = @{ - From = $Request.From - To = $Request.To - Data = $Request.RawBody - Headers = $Request.Headers - Subject = $Request.Subject - IsUrgent = $Request.IsUrgent - ContentType = $Request.ContentType + Response = $Response + Request = $Request + Lockable = $PodeContext.Threading.Lockables.Global + Email = @{ + From = $Request.From + To = $Request.To + Data = $Request.RawBody + Headers = $Request.Headers + Subject = $Request.Subject + IsUrgent = $Request.IsUrgent + ContentType = $Request.ContentType ContentEncoding = $Request.ContentEncoding - Attachments = $Request.Attachments - Body = $Request.Body + Attachments = $Request.Attachments + Body = $Request.Body } - Endpoint = @{ + Endpoint = @{ Protocol = $Request.Scheme - Address = $Request.Address - Name = $null + Address = $Request.Address + Name = $null } Timestamp = [datetime]::UtcNow } @@ -177,8 +172,8 @@ function Start-PodeSmtpServer # script to keep smtp server listening until cancelled $waitScript = { - param ( - [Parameter(Mandatory=$true)] + param( + [Parameter(Mandatory = $true)] [ValidateNotNull()] $Listener ) @@ -203,9 +198,9 @@ function Start-PodeSmtpServer # state where we're running return @(foreach ($endpoint in $endpoints) { - @{ - Url = $endpoint.Url - Pool = $endpoint.Pool - } - }) + @{ + Url = $endpoint.Url + Pool = $endpoint.Pool + } + }) } \ No newline at end of file diff --git a/src/Private/Streams.ps1 b/src/Private/Streams.ps1 index ac40aadcc..6e44da324 100644 --- a/src/Private/Streams.ps1 +++ b/src/Private/Streams.ps1 @@ -1,6 +1,5 @@ -function Read-PodeStreamToEnd -{ - param ( +function Read-PodeStreamToEnd { + param( [Parameter()] $Stream, @@ -13,14 +12,13 @@ function Read-PodeStreamToEnd } return (Use-PodeStream -Stream ([System.IO.StreamReader]::new($Stream, $Encoding)) { - return $args[0].ReadToEnd() - }) + return $args[0].ReadToEnd() + }) } -function Read-PodeByteLineFromByteArray -{ - param ( - [Parameter(Mandatory=$true)] +function Read-PodeByteLineFromByteArray { + param( + [Parameter(Mandatory = $true)] [byte[]] $Bytes, @@ -52,16 +50,15 @@ function Read-PodeByteLineFromByteArray # grab the portion of the bytes array - which is our line return @{ - Bytes = $Bytes[$StartIndex..$fIndex]; - StartIndex = $StartIndex; - EndIndex = $index; + Bytes = $Bytes[$StartIndex..$fIndex] + StartIndex = $StartIndex + EndIndex = $index } } -function Get-PodeByteLinesFromByteArray -{ - param ( - [Parameter(Mandatory=$true)] +function Get-PodeByteLinesFromByteArray { + param( + [Parameter(Mandatory = $true)] [byte[]] $Bytes, @@ -90,17 +87,16 @@ function Get-PodeByteLinesFromByteArray } # add the line, and get the next one - $lines += ,$Bytes[$index..$fIndex] + $lines += , $Bytes[$index..$fIndex] $index = $nextIndex + 1 } return $lines } -function ConvertFrom-PodeStreamToBytes -{ - param ( - [Parameter(Mandatory=$true)] +function ConvertFrom-PodeStreamToBytes { + param( + [Parameter(Mandatory = $true)] $Stream ) @@ -116,9 +112,8 @@ function ConvertFrom-PodeStreamToBytes return $ms.ToArray() } -function ConvertFrom-PodeValueToBytes -{ - param ( +function ConvertFrom-PodeValueToBytes { + param( [Parameter()] [string] $Value, @@ -130,9 +125,8 @@ function ConvertFrom-PodeValueToBytes return $Encoding.GetBytes($Value) } -function ConvertFrom-PodeBytesToString -{ - param ( +function ConvertFrom-PodeBytesToString { + param( [Parameter()] [byte[]] $Bytes, @@ -156,22 +150,20 @@ function ConvertFrom-PodeBytesToString return $value } -function Get-PodeNewLineBytes -{ - param ( +function Get-PodeNewLineBytes { + param( [Parameter()] $Encoding = [System.Text.Encoding]::UTF8 ) return @{ - NewLine = @($Encoding.GetBytes("`n"))[0]; - Return = @($Encoding.GetBytes("`r"))[0]; + NewLine = @($Encoding.GetBytes("`n"))[0] + Return = @($Encoding.GetBytes("`r"))[0] } } -function Test-PodeByteArrayIsBoundary -{ - param ( +function Test-PodeByteArrayIsBoundary { + param( [Parameter()] [byte[]] $Bytes, @@ -198,9 +190,8 @@ function Test-PodeByteArrayIsBoundary return (ConvertFrom-PodeBytesToString $Bytes $Encoding).StartsWith($Boundary) } -function Remove-PodeNewLineBytesFromArray -{ - param ( +function Remove-PodeNewLineBytesFromArray { + param( [Parameter()] $Bytes, diff --git a/src/Private/Tasks.ps1 b/src/Private/Tasks.ps1 index e12e377d8..2ac4245a0 100644 --- a/src/Private/Tasks.ps1 +++ b/src/Private/Tasks.ps1 @@ -1,10 +1,8 @@ -function Test-PodeTasksExist -{ +function Test-PodeTasksExist { return (($null -ne $PodeContext.Tasks) -and (($PodeContext.Tasks.Enabled) -or ($PodeContext.Tasks.Items.Count -gt 0))) } -function Start-PodeTaskHousekeeper -{ +function Start-PodeTaskHousekeeper { if (!(Test-PodeTasksExist)) { return } @@ -46,8 +44,7 @@ function Start-PodeTaskHousekeeper } } -function Close-PodeTaskInternal -{ +function Close-PodeTaskInternal { param( [Parameter()] [hashtable] @@ -63,10 +60,9 @@ function Close-PodeTaskInternal $null = $PodeContext.Tasks.Results.Remove($Result.ID) } -function Invoke-PodeInternalTask -{ +function Invoke-PodeInternalTask { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] $Task, [Parameter()] @@ -83,7 +79,7 @@ function Invoke-PodeInternalTask $parameters = @{ Event = @{ Lockable = $PodeContext.Threading.Lockables.Global - Sender = $Task + Sender = $Task } } @@ -118,13 +114,13 @@ function Invoke-PodeInternalTask } $PodeContext.Tasks.Results[$name] = @{ - ID = $name - Task = $Task.Name - Runspace = $runspace - Result = $result + ID = $name + Task = $Task.Name + Runspace = $runspace + Result = $result CompletedTime = $null - ExpireTime = $expireTime - Timeout = $Timeout + ExpireTime = $expireTime + Timeout = $Timeout } return $PodeContext.Tasks.Results[$name] @@ -134,12 +130,11 @@ function Invoke-PodeInternalTask } } -function Wait-PodeNetTaskInternal -{ +function Wait-PodeNetTaskInternal { [CmdletBinding()] [OutputType([object])] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [System.Threading.Tasks.Task] $Task, @@ -181,12 +176,11 @@ function Wait-PodeNetTaskInternal } } -function Wait-PodeTaskInternal -{ +function Wait-PodeTaskInternal { [CmdletBinding()] [OutputType([object])] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [hashtable] $Task, diff --git a/src/Private/TcpServer.ps1 b/src/Private/TcpServer.ps1 index d506184cb..640e6ca26 100644 --- a/src/Private/TcpServer.ps1 +++ b/src/Private/TcpServer.ps1 @@ -1,7 +1,6 @@ using namespace Pode -function Start-PodeTcpServer -{ +function Start-PodeTcpServer { # work out which endpoints to listen on $endpoints = @() @@ -13,20 +12,21 @@ function Start-PodeTcpServer # the endpoint $_endpoint = @{ - Key = "$($_ip):$($_.Port)" - Address = $_ip - Hostname = $_.HostName - IsIPAddress = $_.IsIPAddress - Port = $_.Port - Certificate = $_.Certificate.Raw + Key = "$($_ip):$($_.Port)" + Address = $_ip + Hostname = $_.HostName + IsIPAddress = $_.IsIPAddress + Port = $_.Port + Certificate = $_.Certificate.Raw AllowClientCertificate = $_.Certificate.AllowClientCertificate - TlsMode = $_.Certificate.TlsMode - Url = $_.Url - Protocol = $_.Protocol - Type = $_.Type - Pool = $_.Runspace.PoolName - Acknowledge = $_.Tcp.Acknowledge - CRLFMessageEnd = $_.Tcp.CRLFMessageEnd + TlsMode = $_.Certificate.TlsMode + Url = $_.Url + Protocol = $_.Protocol + Type = $_.Type + Pool = $_.Runspace.PoolName + Acknowledge = $_.Tcp.Acknowledge + CRLFMessageEnd = $_.Tcp.CRLFMessageEnd + SslProtocols = $_.Ssl.Protocols } # add endpoint to list @@ -40,11 +40,10 @@ function Start-PodeTcpServer $listener.RequestTimeout = $PodeContext.Server.Request.Timeout $listener.RequestBodySize = $PodeContext.Server.Request.BodySize - try - { + try { # register endpoints on the listener $endpoints | ForEach-Object { - $socket = [PodeSocket]::new($_.Address, $_.Port, $PodeContext.Server.Sockets.Ssl.Protocols, [PodeProtocolType]::Tcp, $_.Certificate, $_.AllowClientCertificate, $_.TlsMode) + $socket = [PodeSocket]::new($_.Address, $_.Port, $_.SslProtocols, [PodeProtocolType]::Tcp, $_.Certificate, $_.AllowClientCertificate, $_.TlsMode) $socket.ReceiveTimeout = $PodeContext.Server.Sockets.ReceiveTimeout $socket.AcknowledgeMessage = $_.Acknowledge $socket.CRLFMessageEnd = $_.CRLFMessageEnd @@ -68,41 +67,37 @@ function Start-PodeTcpServer # script for listening out of for incoming requests $listenScript = { - param ( - [Parameter(Mandatory=$true)] + param( + [Parameter(Mandatory = $true)] [ValidateNotNull()] $Listener, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [int] $ThreadId ) - try - { - while ($Listener.IsConnected -and !$PodeContext.Tokens.Cancellation.IsCancellationRequested) - { + try { + while ($Listener.IsConnected -and !$PodeContext.Tokens.Cancellation.IsCancellationRequested) { # get email $context = (Wait-PodeTask -Task $Listener.GetContextAsync($PodeContext.Tokens.Cancellation.Token)) - try - { - try - { + try { + try { $Request = $context.Request $Response = $context.Response $TcpEvent = @{ - Response = $Response - Request = $Request - Lockable = $PodeContext.Threading.Lockables.Global - Endpoint = @{ + Response = $Response + Request = $Request + Lockable = $PodeContext.Threading.Lockables.Global + Endpoint = @{ Protocol = $Request.Scheme - Address = $Request.Address - Name = $null + Address = $Request.Address + Name = $null } Parameters = $null - Timestamp = [datetime]::UtcNow + Timestamp = [datetime]::UtcNow } # endpoint name @@ -196,8 +191,8 @@ function Start-PodeTcpServer # script to keep tcp server listening until cancelled $waitScript = { - param ( - [Parameter(Mandatory=$true)] + param( + [Parameter(Mandatory = $true)] [ValidateNotNull()] $Listener ) @@ -222,9 +217,9 @@ function Start-PodeTcpServer # state where we're running return @(foreach ($endpoint in $endpoints) { - @{ - Url = $endpoint.Url - Pool = $endpoint.Pool - } - }) + @{ + Url = $endpoint.Url + Pool = $endpoint.Pool + } + }) } diff --git a/src/Private/Timers.ps1 b/src/Private/Timers.ps1 index c7181ae89..191d49685 100644 --- a/src/Private/Timers.ps1 +++ b/src/Private/Timers.ps1 @@ -1,7 +1,6 @@ -function Find-PodeTimer -{ +function Find-PodeTimer { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $Name @@ -10,20 +9,17 @@ function Find-PodeTimer return $PodeContext.Timers.Items[$Name] } -function Test-PodeTimersExist -{ +function Test-PodeTimersExist { return (($null -ne $PodeContext.Timers) -and (($PodeContext.Timers.Enabled) -or ($PodeContext.Timers.Items.Count -gt 0))) } -function Start-PodeTimerRunspace -{ +function Start-PodeTimerRunspace { if (!(Test-PodeTimersExist)) { return } $script = { - while (!$PodeContext.Tokens.Cancellation.IsCancellationRequested) - { + while (!$PodeContext.Tokens.Cancellation.IsCancellationRequested) { $_now = [DateTime]::Now # only run timers that haven't completed, and have a next trigger in the past @@ -65,10 +61,9 @@ function Start-PodeTimerRunspace Add-PodeRunspace -Type Main -ScriptBlock $script } -function Invoke-PodeInternalTimer -{ +function Invoke-PodeInternalTimer { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] $Timer, [Parameter()] @@ -79,7 +74,7 @@ function Invoke-PodeInternalTimer try { $global:TimerEvent = @{ Lockable = $PodeContext.Threading.Lockables.Global - Sender = $Timer + Sender = $Timer } # add main timer args diff --git a/src/Private/Verbs.ps1 b/src/Private/Verbs.ps1 index af077b1b2..002e61571 100644 --- a/src/Private/Verbs.ps1 +++ b/src/Private/Verbs.ps1 @@ -1,7 +1,6 @@ -function Find-PodeVerb -{ +function Find-PodeVerb { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $Verb, @@ -19,11 +18,11 @@ function Find-PodeVerb # otherwise, match regex on the verbs (first match only) $valid = @(foreach ($key in $PodeContext.Server.Verbs.Keys) { - if (($key -ine '*') -and ($Verb -imatch "^$($key)$")) { - $key - break - } - })[0] + if (($key -ine '*') -and ($Verb -imatch "^$($key)$")) { + $key + break + } + })[0] if ($null -eq $valid) { return $null @@ -38,8 +37,7 @@ function Find-PodeVerb return $found } -function Get-PodeVerbByLiteral -{ +function Get-PodeVerbByLiteral { param( [Parameter()] [hashtable[]] @@ -59,8 +57,7 @@ function Get-PodeVerbByLiteral return (Get-PodeVerbsByLiteral -Verbs $Verbs -EndpointName $EndpointName) } -function Get-PodeVerbsByLiteral -{ +function Get-PodeVerbsByLiteral { param( [Parameter()] [hashtable[]] @@ -90,10 +87,9 @@ function Get-PodeVerbsByLiteral return $null } -function Test-PodeVerbAndError -{ +function Test-PodeVerbAndError { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Verb, diff --git a/src/Private/WebSockets.ps1 b/src/Private/WebSockets.ps1 index fc21c187d..df3beff39 100644 --- a/src/Private/WebSockets.ps1 +++ b/src/Private/WebSockets.ps1 @@ -1,14 +1,12 @@ using namespace Pode -function Test-PodeWebSocketsExist -{ +function Test-PodeWebSocketsExist { return (($null -ne $PodeContext.Server.WebSockets) -and (($PodeContext.Server.WebSockets.Enabled) -or ($PodeContext.Server.WebSockets.Connections.Count -gt 0))) } -function Find-PodeWebSocket -{ +function Find-PodeWebSocket { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name ) @@ -16,8 +14,7 @@ function Find-PodeWebSocket return $PodeContext.Server.WebSockets.Connections[$Name] } -function New-PodeWebSocketReceiver -{ +function New-PodeWebSocketReceiver { if ($null -ne $PodeContext.Server.WebSockets.Receiver) { return } @@ -37,8 +34,7 @@ function New-PodeWebSocketReceiver } } -function Start-PodeWebSocketRunspace -{ +function Start-PodeWebSocketRunspace { if (!(Test-PodeWebSocketsExist)) { return } @@ -46,31 +42,27 @@ function Start-PodeWebSocketRunspace # script for listening out of for incoming requests $receiveScript = { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [ValidateNotNull()] $Receiver, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [int] $ThreadId ) - try - { - while ($Receiver.IsConnected -and !$PodeContext.Tokens.Cancellation.IsCancellationRequested) - { + try { + while ($Receiver.IsConnected -and !$PodeContext.Tokens.Cancellation.IsCancellationRequested) { # get request $request = (Wait-PodeTask -Task $Receiver.GetWebSocketRequestAsync($PodeContext.Tokens.Cancellation.Token)) - try - { - try - { + try { + try { $WsEvent = @{ - Request = $request - Data = $null - Files = $null - Lockable = $PodeContext.Threading.Lockables.Global + Request = $request + Data = $null + Files = $null + Lockable = $PodeContext.Threading.Lockables.Global Timestamp = [datetime]::UtcNow } @@ -117,7 +109,7 @@ function Start-PodeWebSocketRunspace # script to keep websocket server receiving until cancelled $waitScript = { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [ValidateNotNull()] $Receiver ) diff --git a/src/Public/Access.ps1 b/src/Public/Access.ps1 new file mode 100644 index 000000000..90bed1981 --- /dev/null +++ b/src/Public/Access.ps1 @@ -0,0 +1,692 @@ +<# +.SYNOPSIS +Create a new type of Access scheme. + +.DESCRIPTION +Create a new type of Access scheme, which retrieves the destination/resource's authorisation values which a user needs for access. + +.PARAMETER Type +The inbuilt Type of Access this method is for: Role, Group, Scope, User. + +.PARAMETER Custom +If supplied, the access Scheme will be flagged as using Custom logic. + +.PARAMETER ScriptBlock +An optional ScriptBlock for retrieving authorisation values for the authenticated user, useful if the values reside in an external data store. +This, or Path, is mandatory if using a Custom scheme. + +.PARAMETER ArgumentList +An optional array of arguments to supply to the ScriptBlock. + +.PARAMETER Path +An optional property Path within the $WebEvent.Auth.User object to extract authorisation values. +The default Path is based on the Access Type, either Roles; Groups; Scopes; or Username. +This, or ScriptBlock, is mandatory if using a Custom scheme. + +.EXAMPLE +$role_access = New-PodeAccessScheme -Type Role + +.EXAMPLE +$group_access = New-PodeAccessScheme -Type Group -Path 'Metadata.Groups' + +.EXAMPLE +$scope_access = New-PodeAccessScheme -Type Scope -Scriptblock { param($user) return @(Get-ExampleAccess -Username $user.Username) } + +.EXAMPLE +$custom_access = New-PodeAccessScheme -Custom -Path 'CustomProp' +#> +function New-PodeAccessScheme { + [CmdletBinding(DefaultParameterSetName = 'Type_Path')] + param( + [Parameter(Mandatory = $true, ParameterSetName = 'Type_Scriptblock')] + [Parameter(Mandatory = $true, ParameterSetName = 'Type_Path')] + [ValidateSet('Role', 'Group', 'Scope', 'User')] + [string] + $Type, + + [Parameter(Mandatory = $true, ParameterSetName = 'Custom_Scriptblock')] + [Parameter(Mandatory = $true, ParameterSetName = 'Custom_Path')] + [switch] + $Custom, + + [Parameter(Mandatory = $true, ParameterSetName = 'Custom_Scriptblock')] + [Parameter(ParameterSetName = 'Type_Scriptblock')] + [scriptblock] + $ScriptBlock, + + [Parameter(ParameterSetName = 'Custom_Scriptblock')] + [Parameter(ParameterSetName = 'Type_Scriptblock')] + [object[]] + $ArgumentList, + + [Parameter(Mandatory = $true, ParameterSetName = 'Custom_Path')] + [Parameter(ParameterSetName = 'Type_Path')] + [string] + $Path + ) + + # for custom access a validator is mandatory + if ($Custom) { + if ([string]::IsNullOrWhiteSpace($Path) -and (Test-PodeIsEmpty $ScriptBlock)) { + throw 'A Path or ScriptBlock is required for sourcing the Custom access values' + } + } + + # parse using variables in scriptblock + $scriptObj = $null + if (!(Test-PodeIsEmpty $ScriptBlock)) { + $ScriptBlock, $usingScriptVars = Convert-PodeScopedVariables -ScriptBlock $ScriptBlock -PSSession $PSCmdlet.SessionState + $scriptObj = @{ + Script = $ScriptBlock + UsingVariables = $usingScriptVars + } + } + + # default path + if (!$Custom -and (Test-PodeIsEmpty $ScriptBlock) -and [string]::IsNullOrWhiteSpace($Path)) { + if ($Type -ieq 'user') { + $Path = 'Username' + } + else { + $Path = "$($Type)s" + } + } + + # return scheme + return @{ + Type = $Type + IsCustom = $Custom.IsPresent + ScriptBlock = $scriptObj + Arguments = $ArgumentList + Path = $Path + } +} + +<# +.SYNOPSIS +Add an authorisation Access method. + +.DESCRIPTION +Add an authorisation Access method for use with Authentication methods, which will authorise access to Routes. +Or they can be used independant of Authentication/Routes for custom scenarios. + +.PARAMETER Name +A unique Name for the Access method. + +.PARAMETER Scheme +The access Scheme to use for retrieving credentials (From New-PodeAccessScheme). + +.PARAMETER ScriptBlock +An optional Scriptblock, which can be used to invoke custom validation logic to verify authorisation. + +.PARAMETER ArgumentList +An optional array of arguments to supply to the ScriptBlock. + +.PARAMETER Match +An optional inbuilt Match method to use when verifying access to a Route, this only applies when no custom Validator scriptblock is supplied. (Default: One) +"One" will allow access if the User has at least one of the Route's access values. +"All" will allow access only if the User has all the values. +"None" will allow access only if the User has none of the values. + +.EXAMPLE +New-PodeAccessScheme -Type Role | Add-PodeAccess -Name 'Example' + +.EXAMPLE +New-PodeAccessScheme -Type Group -Path 'Metadata.Groups' | Add-PodeAccess -Name 'Example' -Match All + +.EXAMPLE +New-PodeAccessScheme -Type Scope -Scriptblock { param($user) return @(Get-ExampleAccess -Username $user.Username) } | Add-PodeAccess -Name 'Example' + +.EXAMPLE +New-PodeAccessScheme -Custom -Path 'CustomProp' | Add-PodeAccess -Name 'Example' -ScriptBlock { param($userAccess, $customAccess) return $userAccess.Country -ieq $customAccess.Country } +#> +function Add-PodeAccess { + [CmdletBinding(DefaultParameterSetName = 'Match')] + param( + [Parameter(Mandatory = $true)] + [string] + $Name, + + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] + [hashtable] + $Scheme, + + [Parameter(Mandatory = $true, ParameterSetName = 'ScriptBlock')] + [scriptblock] + $ScriptBlock, + + [Parameter(ParameterSetName = 'ScriptBlock')] + [object[]] + $ArgumentList, + + [Parameter(ParameterSetName = 'Match')] + [ValidateSet('All', 'One', 'None')] + [string] + $Match = 'One' + ) + + # check name unique + if (Test-PodeAccessExists -Name $Name) { + throw "Access method already defined: $($Name)" + } + + # parse using variables in validator scriptblock + $scriptObj = $null + if (!(Test-PodeIsEmpty $ScriptBlock)) { + $ScriptBlock, $usingScriptVars = Convert-PodeScopedVariables -ScriptBlock $ScriptBlock -PSSession $PSCmdlet.SessionState + $scriptObj = @{ + Script = $ScriptBlock + UsingVariables = $usingScriptVars + } + } + + # add access object + $PodeContext.Server.Authorisations.Methods[$Name] = @{ + Name = $Name + Scheme = $Scheme + ScriptBlock = $scriptObj + Arguments = $ArgumentList + Match = $Match.ToLowerInvariant() + Cache = @{} + Merged = $false + Parent = $null + } +} + +<# +.SYNOPSIS +Let's you merge multiple Access methods together, into a "single" Access method. + +.DESCRIPTION +Let's you merge multiple Access methods together, into a "single" Access method. +You can specify if only One or All of the methods need to pass to allow access, and you can also +merge other merged Access methods for more advanced scenarios. + +.PARAMETER Name +A unique Name for the Access method. + +.PARAMETER Access +Mutliple Access method Names to be merged. + +.PARAMETER Valid +How many of the Access methods are required to be valid, One or All. (Default: One) + +.EXAMPLE +Merge-PodeAccess -Name MergedAccess -Access RbacAccess, GbacAccess -Valid All +#> +function Merge-PodeAccess { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [string] + $Name, + + [Parameter(Mandatory = $true)] + [string[]] + $Access, + + [Parameter()] + [ValidateSet('One', 'All')] + [string] + $Valid = 'One' + ) + + # ensure the name doesn't already exist + if (Test-PodeAccessExists -Name $Name) { + throw "Access method already defined: $($Name)" + } + + # ensure all the access methods exist + foreach ($accName in $Access) { + if (!(Test-PodeAccessExists -Name $accName)) { + throw "Access method does not exist for merging: $($accName)" + } + } + + # set parent access + foreach ($accName in $Access) { + $PodeContext.Server.Authorisations.Methods[$accName].Parent = $Name + } + + # add auth method to server + $PodeContext.Server.Authorisations.Methods[$Name] = @{ + Name = $Name + Access = @($Access) + PassOne = ($Valid -ieq 'one') + Cache = @{} + Merged = $true + Parent = $null + } +} + +<# +.SYNOPSIS +Assigns Custom Access value(s) to a Route. + +.DESCRIPTION +Assigns Custom Access value(s) to a Route. + +.PARAMETER Route +The Route to assign the Custom Access value(s). + +.PARAMETER Name +The Name of the Access method the Custom Access value(s) are for. + +.PARAMETER Value +The Custom Access Value(s) + +.EXAMPLE +Add-PodeRoute -Method Get -Path '/users' -ScriptBlock {} -PassThru | Add-PodeAccessCustom -Name 'Example' -Value @{ Country = 'UK' } +#> +function Add-PodeAccessCustom { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] + [hashtable[]] + $Route, + + [Parameter(Mandatory = $true)] + [string] + $Name, + + [Parameter(Mandatory = $true)] + [object[]] + $Value + ) + + begin { + $routes = @() + } + + process { + $routes += $Route + } + + end { + foreach ($r in $routes) { + if ($r.AccessMeta.Custom.ContainsKey($Name)) { + throw "Route '[$($r.Method)] $($r.Path)' already contains Custom Access with name '$($Name)'" + } + + $r.AccessMeta.Custom[$Name] = $Value + } + } +} + +<# +.SYNOPSIS +Get one or more Access methods. + +.DESCRIPTION +Get one or more Access methods. + +.PARAMETER Name +The Name of the Access method. If no name supplied, all methods will be returned. + +.EXAMPLE +$methods = Get-PodeAccess + +.EXAMPLE +$methods = Get-PodeAccess -Name 'Example' + +.EXAMPLE +$methods = Get-PodeAccess -Name 'Example1', 'Example2' +#> +function Get-PodeAccess { + [CmdletBinding()] + param( + [Parameter()] + [string[]] + $Name + ) + + # return all if no Name + if ([string]::IsNullOrEmpty($Name) -or ($Name.Length -eq 0)) { + return $PodeContext.Server.Authorisations.Methods.Values + } + + # return filtered + return @(foreach ($n in $Name) { + $PodeContext.Server.Authorisations.Methods[$n] + }) +} + +<# +.SYNOPSIS +Test if an Access method exists. + +.DESCRIPTION +Test if an Access method exists. + +.PARAMETER Name +The Name of the Access method. + +.EXAMPLE +if (Test-PodeAccessExists -Name 'Example') { } +#> +function Test-PodeAccessExists { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [string] + $Name + ) + + return $PodeContext.Server.Authorisations.Methods.ContainsKey($Name) +} + +<# +.SYNOPSIS +Test access values for a Source/Destination against an Access method. + +.DESCRIPTION +Test access values for a Source/Destination against an Access method. + +.PARAMETER Name +The Name of the Access method to use to verify the access. + +.PARAMETER Source +An array of Source access values to pass to the Access method for verification against the Destination access values. (ie: User) + +.PARAMETER Destination +An array of Destination access values to pass to the Access method for verification. (ie: Route) + +.PARAMETER ArgumentList +An optional array of arguments to supply to the Access Scheme's ScriptBlock for retrieving access values. + +.EXAMPLE +if (Test-PodeAccess -Name 'Example' -Source 'Developer' -Destination 'Admin') { } +#> +function Test-PodeAccess { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [string] + $Name, + + [Parameter()] + [object[]] + $Source = $null, + + [Parameter()] + [object[]] + $Destination = $null, + + [Parameter()] + [object[]] + $ArgumentList = $null + ) + + # get the access method + $access = $PodeContext.Server.Authorisations.Methods[$Name] + + # authorised if no destination values + if (($null -eq $Destination) -or ($Destination.Length -eq 0)) { + return $true + } + + # if we have no source values, invoke the scriptblock + if (($null -eq $Source) -or ($Source.Length -eq 0)) { + if ($null -ne $access.Scheme.ScriptBlock) { + $_args = $ArgumentList + @($access.Scheme.Arguments) + $_args = @(Get-PodeScriptblockArguments -ArgumentList $_args -UsingVariables $access.Scheme.Scriptblock.UsingVariables) + $Source = Invoke-PodeScriptBlock -ScriptBlock $access.Scheme.Scriptblock.Script -Arguments $_args -Return -Splat + } + } + + # check for custom validator, or use default match logic + if ($null -ne $access.ScriptBlock) { + $_args = @(, $Source) + @(, $Destination) + @($access.Arguments) + $_args = @(Get-PodeScriptblockArguments -ArgumentList $_args -UsingVariables $access.ScriptBlock.UsingVariables) + return [bool](Invoke-PodeScriptBlock -ScriptBlock $access.ScriptBlock.Script -Arguments $_args -Return -Splat) + } + + # not authorised if no source values + if (($access.Match -ne 'none') -and (($null -eq $Source) -or ($Source.Length -eq 0))) { + return $false + } + + # one or all match? + else { + switch ($access.Match) { + 'one' { + foreach ($item in $Source) { + if ($item -iin $Destination) { + return $true + } + } + } + + 'all' { + foreach ($item in $Destination) { + if ($item -inotin $Source) { + return $false + } + } + + return $true + } + + 'none' { + foreach ($item in $Source) { + if ($item -iin $Destination) { + return $false + } + } + + return $true + } + } + } + + # default is not authorised + return $false +} + +<# +.SYNOPSIS +Test the currently authenticated User's access against the supplied values. + +.DESCRIPTION +Test the currently authenticated User's access against the supplied values. This will be the user in a WebEvent object. + +.PARAMETER Name +The Name of the Access method to use to verify the access. + +.PARAMETER Value +An array of access values to pass to the Access method for verification against the User. + +.EXAMPLE +if (Test-PodeAccessUser -Name 'Example' -Value 'Developer', 'QA') { } +#> +function Test-PodeAccessUser { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [string] + $Name, + + [Parameter(Mandatory = $true)] + [object[]] + $Value + ) + + # get the access method + $access = $PodeContext.Server.Authorisations.Methods[$Name] + + # get the user + $user = $WebEvent.Auth.User + + # if there's no scriptblock, try the Path fallback + if ($null -eq $access.Scheme.Scriptblock) { + $userAccess = $user + foreach ($atom in $access.Scheme.Path.Split('.')) { + $userAccess = $userAccess.($atom) + } + } + + # otherwise, invoke scriptblock + else { + $_args = @($user) + @($access.Scheme.Arguments) + $_args = @(Get-PodeScriptblockArguments -ArgumentList $_args -UsingVariables $access.Scheme.Scriptblock.UsingVariables) + $userAccess = Invoke-PodeScriptBlock -ScriptBlock $access.Scheme.Scriptblock.Script -Arguments $_args -Return -Splat + } + + # is the user authorised? + return (Test-PodeAccess -Name $Name -Source $userAccess -Destination $Value) +} + +<# +.SYNOPSIS +Test the currently authenticated User's access against the access values supplied for the current Route. + +.DESCRIPTION +Test the currently authenticated User's access against the access values supplied for the current Route. + +.PARAMETER Name +The Name of the Access method to use to verify the access. + +.EXAMPLE +if (Test-PodeAccessRoute -Name 'Example') { } +#> +function Test-PodeAccessRoute { + param( + [Parameter(Mandatory = $true)] + [string] + $Name + ) + + # get the access method + $access = $PodeContext.Server.Authorisations.Methods[$Name] + + # get route access values + if ($access.Scheme.IsCustom) { + $routeAccess = $WebEvent.Route.AccessMeta.Custom[$access.Name] + } + else { + $routeAccess = $WebEvent.Route.AccessMeta[$access.Scheme.Type] + } + + # if no values then skip + if (($null -eq $routeAccess) -or ($routeAccess.Length -eq 0)) { + return $true + } + + # tests values against user + return (Test-PodeAccessUser -Name $Name -Value $routeAccess) +} + +<# +.SYNOPSIS +Remove a specific Access method. + +.DESCRIPTION +Remove a specific Access method. + +.PARAMETER Name +The Name of the Access method. + +.EXAMPLE +Remove-PodeAccess -Name 'RBAC' +#> +function Remove-PodeAccess { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] + [string] + $Name + ) + + $null = $PodeContext.Server.Authorisations.Methods.Remove($Name) +} + +<# +.SYNOPSIS +Clear all defined Access methods. + +.DESCRIPTION +Clear all defined Access methods. + +.EXAMPLE +Clear-PodeAccess +#> +function Clear-PodeAccess { + [CmdletBinding()] + param() + + $PodeContext.Server.Authorisations.Methods.Clear() +} + +<# +.SYNOPSIS +Adds an access method as global middleware. + +.DESCRIPTION +Adds an access method as global middleware. + +.PARAMETER Name +The Name of the Middleware. + +.PARAMETER Access +The Name of the Access method to use. + +.PARAMETER Route +A Route path for which Routes this Middleware should only be invoked against. + +.EXAMPLE +Add-PodeAccessMiddleware -Name 'GlobalAccess' -Access AccessName + +.EXAMPLE +Add-PodeAccessMiddleware -Name 'GlobalAccess' -Access AccessName -Route '/api/*' +#> +function Add-PodeAccessMiddleware { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [string] + $Name, + + [Parameter(Mandatory = $true)] + [string] + $Access, + + [Parameter()] + [string] + $Route + ) + + if (!(Test-PodeAccessExists -Name $Access)) { + throw "Access method does not exist: $($Access)" + } + + Get-PodeAccessMiddlewareScript | + New-PodeMiddleware -ArgumentList @{ Name = $Access } | + Add-PodeMiddleware -Name $Name -Route $Route +} + +<# +.SYNOPSIS +Automatically loads access ps1 files + +.DESCRIPTION +Automatically loads access ps1 files from either an /access folder, or a custom folder. Saves space dot-sourcing them all one-by-one. + +.PARAMETER Path +Optional Path to a folder containing ps1 files, can be relative or literal. + +.EXAMPLE +Use-PodeAccess + +.EXAMPLE +Use-PodeAccess -Path './my-access' +#> +function Use-PodeAccess { + [CmdletBinding()] + param( + [Parameter()] + [string] + $Path + ) + + Use-PodeFolder -Path $Path -DefaultPath 'access' +} \ No newline at end of file diff --git a/src/Public/Authentication.ps1 b/src/Public/Authentication.ps1 index 81f0e1259..0451f5a80 100644 --- a/src/Public/Authentication.ps1 +++ b/src/Public/Authentication.ps1 @@ -1,9 +1,9 @@ <# .SYNOPSIS -Create a new type of Authentication. +Create a new type of Authentication scheme. .DESCRIPTION -Create a new type of Authentication, which is used to parse the Request for user credentials for validating. +Create a new type of Authentication scheme, which is used to parse the Request for user credentials for validating. .PARAMETER Basic If supplied, will use the inbuilt Basic Authentication credentials retriever. @@ -119,57 +119,56 @@ $form_auth = New-PodeAuthScheme -Form -UsernameField 'Email' .EXAMPLE $custom_auth = New-PodeAuthScheme -Custom -ScriptBlock { /* logic */ } #> -function New-PodeAuthScheme -{ - [CmdletBinding(DefaultParameterSetName='Basic')] +function New-PodeAuthScheme { + [CmdletBinding(DefaultParameterSetName = 'Basic')] [OutputType([hashtable])] - param ( - [Parameter(ParameterSetName='Basic')] + param( + [Parameter(ParameterSetName = 'Basic')] [switch] $Basic, - [Parameter(ParameterSetName='Basic')] + [Parameter(ParameterSetName = 'Basic')] [string] $Encoding = 'ISO-8859-1', - [Parameter(ParameterSetName='Basic')] - [Parameter(ParameterSetName='Bearer')] - [Parameter(ParameterSetName='Digest')] + [Parameter(ParameterSetName = 'Basic')] + [Parameter(ParameterSetName = 'Bearer')] + [Parameter(ParameterSetName = 'Digest')] [string] $HeaderTag, - [Parameter(ParameterSetName='Form')] + [Parameter(ParameterSetName = 'Form')] [switch] $Form, - [Parameter(ParameterSetName='Form')] + [Parameter(ParameterSetName = 'Form')] [string] $UsernameField = 'username', - [Parameter(ParameterSetName='Form')] + [Parameter(ParameterSetName = 'Form')] [string] $PasswordField = 'password', - [Parameter(ParameterSetName='Custom')] + [Parameter(ParameterSetName = 'Custom')] [switch] $Custom, - [Parameter(Mandatory=$true, ParameterSetName='Custom')] + [Parameter(Mandatory = $true, ParameterSetName = 'Custom')] [ValidateScript({ - if (Test-PodeIsEmpty $_) { - throw "A non-empty ScriptBlock is required for the Custom authentication scheme" - } + if (Test-PodeIsEmpty $_) { + throw 'A non-empty ScriptBlock is required for the Custom authentication scheme' + } - return $true - })] + return $true + })] [scriptblock] $ScriptBlock, - [Parameter(ParameterSetName='Custom')] + [Parameter(ParameterSetName = 'Custom')] [hashtable] $ArgumentList, - [Parameter(ParameterSetName='Custom')] + [Parameter(ParameterSetName = 'Custom')] [string] $Name, @@ -177,7 +176,7 @@ function New-PodeAuthScheme [string] $Realm, - [Parameter(ParameterSetName='Custom')] + [Parameter(ParameterSetName = 'Custom')] [ValidateSet('ApiKey', 'Http', 'OAuth2', 'OpenIdConnect')] [string] $Type = 'Http', @@ -186,98 +185,98 @@ function New-PodeAuthScheme [object[]] $Middleware, - [Parameter(ParameterSetName='Custom')] + [Parameter(ParameterSetName = 'Custom')] [scriptblock] - $PostValidator, + $PostValidator = $null, - [Parameter(ParameterSetName='Digest')] + [Parameter(ParameterSetName = 'Digest')] [switch] $Digest, - [Parameter(ParameterSetName='Bearer')] + [Parameter(ParameterSetName = 'Bearer')] [switch] $Bearer, - [Parameter(ParameterSetName='ClientCertificate')] + [Parameter(ParameterSetName = 'ClientCertificate')] [switch] $ClientCertificate, - [Parameter(ParameterSetName='OAuth2', Mandatory=$true)] + [Parameter(ParameterSetName = 'OAuth2', Mandatory = $true)] [string] $ClientId, - [Parameter(ParameterSetName='OAuth2')] + [Parameter(ParameterSetName = 'OAuth2')] [string] $ClientSecret, - [Parameter(ParameterSetName='OAuth2')] + [Parameter(ParameterSetName = 'OAuth2')] [string] $RedirectUrl, - [Parameter(ParameterSetName='OAuth2')] + [Parameter(ParameterSetName = 'OAuth2')] [string] $AuthoriseUrl, - [Parameter(ParameterSetName='OAuth2', Mandatory=$true)] + [Parameter(ParameterSetName = 'OAuth2', Mandatory = $true)] [string] $TokenUrl, - [Parameter(ParameterSetName='OAuth2')] + [Parameter(ParameterSetName = 'OAuth2')] [string] $UserUrl, - [Parameter(ParameterSetName='OAuth2')] + [Parameter(ParameterSetName = 'OAuth2')] [ValidateSet('Get', 'Post')] [string] $UserUrlMethod = 'Post', - [Parameter(ParameterSetName='OAuth2')] + [Parameter(ParameterSetName = 'OAuth2')] [ValidateSet('plain', 'S256')] [string] $CodeChallengeMethod = 'S256', - [Parameter(ParameterSetName='OAuth2')] + [Parameter(ParameterSetName = 'OAuth2')] [switch] $UsePKCE, - [Parameter(ParameterSetName='OAuth2')] + [Parameter(ParameterSetName = 'OAuth2')] [switch] $OAuth2, - [Parameter(ParameterSetName='ApiKey')] + [Parameter(ParameterSetName = 'ApiKey')] [switch] $ApiKey, - [Parameter(ParameterSetName='ApiKey')] + [Parameter(ParameterSetName = 'ApiKey')] [ValidateSet('Header', 'Query', 'Cookie')] [string] $Location = 'Header', - [Parameter(ParameterSetName='ApiKey')] + [Parameter(ParameterSetName = 'ApiKey')] [string] $LocationName, - [Parameter(ParameterSetName='Bearer')] - [Parameter(ParameterSetName='OAuth2')] + [Parameter(ParameterSetName = 'Bearer')] + [Parameter(ParameterSetName = 'OAuth2')] [string[]] $Scope, - [Parameter(ValueFromPipeline=$true)] + [Parameter(ValueFromPipeline = $true)] [hashtable] $InnerScheme, - [Parameter(ParameterSetName='Basic')] - [Parameter(ParameterSetName='Form')] + [Parameter(ParameterSetName = 'Basic')] + [Parameter(ParameterSetName = 'Form')] [switch] $AsCredential, - [Parameter(ParameterSetName='Bearer')] - [Parameter(ParameterSetName='ApiKey')] + [Parameter(ParameterSetName = 'Bearer')] + [Parameter(ParameterSetName = 'ApiKey')] [switch] $AsJWT, - [Parameter(ParameterSetName='Bearer')] - [Parameter(ParameterSetName='ApiKey')] + [Parameter(ParameterSetName = 'Bearer')] + [Parameter(ParameterSetName = 'ApiKey')] [string] $Secret ) @@ -292,19 +291,19 @@ function New-PodeAuthScheme switch ($PSCmdlet.ParameterSetName.ToLowerInvariant()) { 'basic' { return @{ - Name = (Protect-PodeValue -Value $HeaderTag -Default 'Basic') - Realm = (Protect-PodeValue -Value $Realm -Default $_realm) - ScriptBlock = @{ - Script = (Get-PodeAuthBasicType) + Name = (Protect-PodeValue -Value $HeaderTag -Default 'Basic') + Realm = (Protect-PodeValue -Value $Realm -Default $_realm) + ScriptBlock = @{ + Script = (Get-PodeAuthBasicType) UsingVariables = $null } PostValidator = $null - Middleware = $Middleware - InnerScheme = $InnerScheme - Scheme = 'http' - Arguments = @{ - HeaderTag = (Protect-PodeValue -Value $HeaderTag -Default 'Basic') - Encoding = (Protect-PodeValue -Value $Encoding -Default 'ISO-8859-1') + Middleware = $Middleware + InnerScheme = $InnerScheme + Scheme = 'http' + Arguments = @{ + HeaderTag = (Protect-PodeValue -Value $HeaderTag -Default 'Basic') + Encoding = (Protect-PodeValue -Value $Encoding -Default 'ISO-8859-1') AsCredential = $AsCredential } } @@ -312,36 +311,36 @@ function New-PodeAuthScheme 'clientcertificate' { return @{ - Name = 'Mutual' - Realm = (Protect-PodeValue -Value $Realm -Default $_realm) - ScriptBlock = @{ - Script = (Get-PodeAuthClientCertificateType) + Name = 'Mutual' + Realm = (Protect-PodeValue -Value $Realm -Default $_realm) + ScriptBlock = @{ + Script = (Get-PodeAuthClientCertificateType) UsingVariables = $null } PostValidator = $null - Middleware = $Middleware - InnerScheme = $InnerScheme - Scheme = 'http' - Arguments = @{} + Middleware = $Middleware + InnerScheme = $InnerScheme + Scheme = 'http' + Arguments = @{} } } 'digest' { return @{ - Name = 'Digest' - Realm = (Protect-PodeValue -Value $Realm -Default $_realm) - ScriptBlock = @{ - Script = (Get-PodeAuthDigestType) + Name = 'Digest' + Realm = (Protect-PodeValue -Value $Realm -Default $_realm) + ScriptBlock = @{ + Script = (Get-PodeAuthDigestType) UsingVariables = $null } PostValidator = @{ - Script = (Get-PodeAuthDigestPostValidator) + Script = (Get-PodeAuthDigestPostValidator) UsingVariables = $null } - Middleware = $Middleware - InnerScheme = $InnerScheme - Scheme = 'http' - Arguments = @{ + Middleware = $Middleware + InnerScheme = $InnerScheme + Scheme = 'http' + Arguments = @{ HeaderTag = (Protect-PodeValue -Value $HeaderTag -Default 'Digest') } } @@ -354,42 +353,42 @@ function New-PodeAuthScheme } return @{ - Name = 'Bearer' - Realm = (Protect-PodeValue -Value $Realm -Default $_realm) - ScriptBlock = @{ - Script = (Get-PodeAuthBearerType) + Name = 'Bearer' + Realm = (Protect-PodeValue -Value $Realm -Default $_realm) + ScriptBlock = @{ + Script = (Get-PodeAuthBearerType) UsingVariables = $null } PostValidator = @{ - Script = (Get-PodeAuthBearerPostValidator) + Script = (Get-PodeAuthBearerPostValidator) UsingVariables = $null } - Middleware = $Middleware - Scheme = 'http' - InnerScheme = $InnerScheme - Arguments = @{ + Middleware = $Middleware + Scheme = 'http' + InnerScheme = $InnerScheme + Arguments = @{ HeaderTag = (Protect-PodeValue -Value $HeaderTag -Default 'Bearer') - Scopes = $Scope - AsJWT = $AsJWT - Secret = $secretBytes + Scopes = $Scope + AsJWT = $AsJWT + Secret = $secretBytes } } } 'form' { return @{ - Name = 'Form' - Realm = (Protect-PodeValue -Value $Realm -Default $_realm) - ScriptBlock = @{ - Script = (Get-PodeAuthFormType) + Name = 'Form' + Realm = (Protect-PodeValue -Value $Realm -Default $_realm) + ScriptBlock = @{ + Script = (Get-PodeAuthFormType) UsingVariables = $null } PostValidator = $null - Middleware = $Middleware - InnerScheme = $InnerScheme - Scheme = 'http' - Arguments = @{ - Fields = @{ + Middleware = $Middleware + InnerScheme = $InnerScheme + Scheme = 'http' + Arguments = @{ + Fields = @{ Username = (Protect-PodeValue -Value $UsernameField -Default 'username') Password = (Protect-PodeValue -Value $PasswordField -Default 'password') } @@ -404,7 +403,7 @@ function New-PodeAuthScheme } if (($null -eq $InnerScheme) -and [string]::IsNullOrWhiteSpace($AuthoriseUrl)) { - throw "OAuth2 requires an Authorise URL to be supplied" + throw 'OAuth2 requires an Authorise URL to be supplied' } if ($UsePKCE -and !(Test-PodeSessionsConfigured)) { @@ -412,38 +411,38 @@ function New-PodeAuthScheme } if (!$UsePKCE -and [string]::IsNullOrEmpty($ClientSecret)) { - throw "OAuth2 requires a Client Secret when not using PKCE" + throw 'OAuth2 requires a Client Secret when not using PKCE' } return @{ - Name = 'OAuth2' - Realm = (Protect-PodeValue -Value $Realm -Default $_realm) - ScriptBlock = @{ - Script = (Get-PodeAuthOAuth2Type) + Name = 'OAuth2' + Realm = (Protect-PodeValue -Value $Realm -Default $_realm) + ScriptBlock = @{ + Script = (Get-PodeAuthOAuth2Type) UsingVariables = $null } PostValidator = $null - Middleware = $Middleware - Scheme = 'oauth2' - InnerScheme = $InnerScheme - Arguments = @{ + Middleware = $Middleware + Scheme = 'oauth2' + InnerScheme = $InnerScheme + Arguments = @{ Scopes = $Scope - PKCE = @{ - Enabled = $UsePKCE + PKCE = @{ + Enabled = $UsePKCE CodeChallenge = @{ Method = $CodeChallengeMethod } } Client = @{ - ID = $ClientId + ID = $ClientId Secret = $ClientSecret } - Urls = @{ - Redirect = $RedirectUrl + Urls = @{ + Redirect = $RedirectUrl Authorise = $AuthoriseUrl - Token = $TokenUrl - User = @{ - Url = $UserUrl + Token = $TokenUrl + User = @{ + Url = $UserUrl Method = (Protect-PodeValue -Value $UserUrlMethod -Default 'Post') } } @@ -455,10 +454,10 @@ function New-PodeAuthScheme # set default location name if ([string]::IsNullOrWhiteSpace($LocationName)) { $LocationName = (@{ - Header = 'X-API-KEY' - Query = 'api_key' - Cookie = 'X-API-KEY' - })[$Location] + Header = 'X-API-KEY' + Query = 'api_key' + Cookie = 'X-API-KEY' + })[$Location] } $secretBytes = $null @@ -467,21 +466,21 @@ function New-PodeAuthScheme } return @{ - Name = 'ApiKey' - Realm = (Protect-PodeValue -Value $Realm -Default $_realm) - ScriptBlock = @{ - Script = (Get-PodeAuthApiKeyType) + Name = 'ApiKey' + Realm = (Protect-PodeValue -Value $Realm -Default $_realm) + ScriptBlock = @{ + Script = (Get-PodeAuthApiKeyType) UsingVariables = $null } PostValidator = $null - Middleware = $Middleware - InnerScheme = $InnerScheme - Scheme = 'apiKey' - Arguments = @{ - Location = $Location + Middleware = $Middleware + InnerScheme = $InnerScheme + Scheme = 'apiKey' + Arguments = @{ + Location = $Location LocationName = $LocationName - AsJWT = $AsJWT - Secret = $secretBytes + AsJWT = $AsJWT + Secret = $secretBytes } } } @@ -489,25 +488,25 @@ function New-PodeAuthScheme 'custom' { $ScriptBlock, $usingScriptVars = Convert-PodeScopedVariables -ScriptBlock $ScriptBlock -PSSession $PSCmdlet.SessionState - if (!(Test-PodeIsEmpty $PostValidator)) { + if ($null -ne $PostValidator) { $PostValidator, $usingPostVars = Convert-PodeScopedVariables -ScriptBlock $PostValidator -PSSession $PSCmdlet.SessionState } return @{ - Name = $Name - Realm = (Protect-PodeValue -Value $Realm -Default $_realm) - InnerScheme = $InnerScheme - Scheme = $Type.ToLowerInvariant() - ScriptBlock = @{ - Script = $ScriptBlock + Name = $Name + Realm = (Protect-PodeValue -Value $Realm -Default $_realm) + InnerScheme = $InnerScheme + Scheme = $Type.ToLowerInvariant() + ScriptBlock = @{ + Script = $ScriptBlock UsingVariables = $usingScriptVars } PostValidator = @{ - Script = $PostValidator + Script = $PostValidator UsingVariables = $usingPostVars } - Middleware = $Middleware - Arguments = $ArgumentList + Middleware = $Middleware + Arguments = $ArgumentList } } } @@ -547,8 +546,7 @@ New-PodeAuthAzureADScheme -Tenant 123-456-678 -ClientId some_id -ClientSecret 12 .EXAMPLE New-PodeAuthAzureADScheme -Tenant 123-456-678 -ClientId some_id -UsePKCE #> -function New-PodeAuthAzureADScheme -{ +function New-PodeAuthAzureADScheme { [CmdletBinding()] param( [Parameter()] @@ -556,7 +554,7 @@ function New-PodeAuthAzureADScheme [string] $Tenant = 'common', - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $ClientId, @@ -568,7 +566,7 @@ function New-PodeAuthAzureADScheme [string] $RedirectUrl, - [Parameter(ValueFromPipeline=$true)] + [Parameter(ValueFromPipeline = $true)] [hashtable] $InnerScheme, @@ -580,17 +578,17 @@ function New-PodeAuthAzureADScheme $UsePKCE ) - return (New-PodeAuthScheme ` + return New-PodeAuthScheme ` -OAuth2 ` -ClientId $ClientId ` -ClientSecret $ClientSecret ` -AuthoriseUrl "https://login.microsoftonline.com/$($Tenant)/oauth2/v2.0/authorize" ` -TokenUrl "https://login.microsoftonline.com/$($Tenant)/oauth2/v2.0/token" ` - -UserUrl "https://graph.microsoft.com/oidc/userinfo" ` + -UserUrl 'https://graph.microsoft.com/oidc/userinfo' ` -RedirectUrl $RedirectUrl ` -InnerScheme $InnerScheme ` -Middleware $Middleware ` - -UsePKCE:$UsePKCE) + -UsePKCE:$UsePKCE } <# @@ -621,11 +619,10 @@ New-PodeAuthTwitterScheme -ClientId some_id -ClientSecret 1234.abc .EXAMPLE New-PodeAuthTwitterScheme -ClientId some_id -UsePKCE #> -function New-PodeAuthTwitterScheme -{ +function New-PodeAuthTwitterScheme { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $ClientId, @@ -645,18 +642,18 @@ function New-PodeAuthTwitterScheme $UsePKCE ) - return (New-PodeAuthScheme ` + return New-PodeAuthScheme ` -OAuth2 ` -ClientId $ClientId ` -ClientSecret $ClientSecret ` - -AuthoriseUrl "https://twitter.com/i/oauth2/authorize" ` - -TokenUrl "https://api.twitter.com/2/oauth2/token" ` - -UserUrl "https://api.twitter.com/2/users/me" ` + -AuthoriseUrl 'https://twitter.com/i/oauth2/authorize' ` + -TokenUrl 'https://api.twitter.com/2/oauth2/token' ` + -UserUrl 'https://api.twitter.com/2/users/me' ` -UserUrlMethod 'Get' ` -RedirectUrl $RedirectUrl ` -Middleware $Middleware ` -Scope 'tweet.read', 'users.read' ` - -UsePKCE:$UsePKCE) + -UsePKCE:$UsePKCE } <# @@ -670,7 +667,7 @@ Adds a custom Authentication method for verifying users. A unique Name for the Authentication method. .PARAMETER Scheme -The Scheme to use for retrieving credentials (From New-PodeAuthScheme). +The authentication Scheme to use for retrieving credentials (From New-PodeAuthScheme). .PARAMETER ScriptBlock The ScriptBlock defining logic that retrieves and verifys a user. @@ -696,26 +693,25 @@ If supplied, successful authentication from a login page will redirect back to t .EXAMPLE New-PodeAuthScheme -Form | Add-PodeAuth -Name 'Main' -ScriptBlock { /* logic */ } #> -function Add-PodeAuth -{ +function Add-PodeAuth { [CmdletBinding()] - param ( - [Parameter(Mandatory=$true)] + param( + [Parameter(Mandatory = $true)] [string] $Name, - [Parameter(Mandatory=$true, ValueFromPipeline=$true)] + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [hashtable] $Scheme, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [ValidateScript({ - if (Test-PodeIsEmpty $_) { - throw "A non-empty ScriptBlock is required for the authentication method" - } + if (Test-PodeIsEmpty $_) { + throw 'A non-empty ScriptBlock is required for the authentication method' + } - return $true - })] + return $true + })] [scriptblock] $ScriptBlock, @@ -743,13 +739,13 @@ function Add-PodeAuth ) # ensure the name doesn't already exist - if (Test-PodeAuth -Name $Name) { + if (Test-PodeAuthExists -Name $Name) { throw "Authentication method already defined: $($Name)" } # ensure the Scheme contains a scriptblock if (Test-PodeIsEmpty $Scheme.ScriptBlock) { - throw "The supplied Scheme for the '$($Name)' authentication validator requires a valid ScriptBlock" + throw "The supplied '$($Scheme.Name)' Scheme for the '$($Name)' authentication validator requires a valid ScriptBlock" } # if we're using sessions, ensure sessions have been setup @@ -761,30 +757,223 @@ function Add-PodeAuth $ScriptBlock, $usingVars = Convert-PodeScopedVariables -ScriptBlock $ScriptBlock -PSSession $PSCmdlet.SessionState # add auth method to server - $PodeContext.Server.Authentications[$Name] = @{ - Scheme = $Scheme - ScriptBlock = $ScriptBlock + $PodeContext.Server.Authentications.Methods[$Name] = @{ + Name = $Name + Scheme = $Scheme + ScriptBlock = $ScriptBlock UsingVariables = $usingVars - Arguments = $ArgumentList - Sessionless = $Sessionless - Failure = @{ - Url = $FailureUrl + Arguments = $ArgumentList + Sessionless = $Sessionless.IsPresent + Failure = @{ + Url = $FailureUrl Message = $FailureMessage } - Success = @{ - Url = $SuccessUrl - UseOrigin = $SuccessUseOrigin + Success = @{ + Url = $SuccessUrl + UseOrigin = $SuccessUseOrigin.IsPresent } + Cache = @{} + Merged = $false + Parent = $null } # if the scheme is oauth2, and there's no redirect, set up a default one - if (($Scheme.Name -ieq 'oauth2') -and ($null -eq $Scheme.InnerScheme) -and [string]::IsNullOrWhiteSpace($Scheme.Arguments.Urls.Redirect)) { + if (($Scheme.Name -ieq 'oauth2') -and ($null -eq $Scheme.InnerScheme) -and [string]::IsNullOrWhiteSpace($Scheme.Arguments.Urls.Redirect)) { $path = '/oauth2/callback' $Scheme.Arguments.Urls.Redirect = $path Add-PodeRoute -Method Get -Path $path -Authentication $Name } } +<# +.SYNOPSIS +Lets you merge multiple Authentication methods together, into a "single" Authentication method. + +.DESCRIPTION +Lets you merge multiple Authentication methods together, into a "single" Authentication method. +You can specify if only One or All of the methods need to pass to allow access, and you can also +merge other merged Authentication methods for more advanced scenarios. + +.PARAMETER Name +A unique Name for the Authentication method. + +.PARAMETER Authentication +Multiple Autentication method Names to be merged. + +.PARAMETER Valid +How many of the Authentication methods are required to be valid, One or All. (Default: One) + +.PARAMETER ScriptBlock +This is mandatory when Valid is All. A scriptblock to merge the mutliple users/headers returned by valid authentications into 1 user/header objects. +This scriptblock will receive a hashtable of all result objects returned from Authentication methods. The key for the hashtable will be the authentication names that passed. + +.PARAMETER Default +The Default Authentication method to use as a fallback for Failure URLs and other settings. + +.PARAMETER FailureUrl +The URL to redirect to when authentication fails. +This will be used as fallback for the merged Authentication methods if not set on them. + +.PARAMETER FailureMessage +An override Message to throw when authentication fails. +This will be used as fallback for the merged Authentication methods if not set on them. + +.PARAMETER SuccessUrl +The URL to redirect to when authentication succeeds when logging in. +This will be used as fallback for the merged Authentication methods if not set on them. + +.PARAMETER Sessionless +If supplied, authenticated users will not be stored in sessions, and sessions will not be used. +This will be used as fallback for the merged Authentication methods if not set on them. + +.PARAMETER SuccessUseOrigin +If supplied, successful authentication from a login page will redirect back to the originating page instead of the FailureUrl. +This will be used as fallback for the merged Authentication methods if not set on them. + +.EXAMPLE +Merge-PodeAuth -Name MergedAuth -Authentication ApiTokenAuth, BasicAuth -Valid All + +.EXAMPLE +Merge-PodeAuth -Name MergedAuth -Authentication ApiTokenAuth, BasicAuth -FailureUrl 'http://localhost:8080/login' +#> +function Merge-PodeAuth { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [string] + $Name, + + [Parameter(Mandatory = $true)] + [Alias('Auth')] + [string[]] + $Authentication, + + [Parameter()] + [ValidateSet('One', 'All')] + [string] + $Valid = 'One', + + [Parameter()] + [scriptblock] + $ScriptBlock, + + [Parameter()] + [string] + $Default, + + [Parameter()] + [string] + $FailureUrl, + + [Parameter()] + [string] + $FailureMessage, + + [Parameter()] + [string] + $SuccessUrl, + + [switch] + $Sessionless, + + [switch] + $SuccessUseOrigin + ) + + # ensure the name doesn't already exist + if (Test-PodeAuthExists -Name $Name) { + throw "Authentication method already defined: $($Name)" + } + + # ensure all the auth methods exist + foreach ($authName in $Authentication) { + if (!(Test-PodeAuthExists -Name $authName)) { + throw "Authentication method does not exist for merging: $($authName)" + } + } + + # ensure the default is in the auth list + if (![string]::IsNullOrEmpty($Default) -and ($Default -inotin @($Authentication))) { + throw "the Default Authentication '$($Default)' is not in the Authentication list supplied" + } + + # set default + if ([string]::IsNullOrEmpty($Default)) { + $Default = $Authentication[0] + } + + # get auth for default + $tmpAuth = $PodeContext.Server.Authentications.Methods[$Default] + + # check sessionless from default + if (!$Sessionless) { + $Sessionless = $tmpAuth.Sessionless + } + + # if we're using sessions, ensure sessions have been setup + if (!$Sessionless -and !(Test-PodeSessionsConfigured)) { + throw 'Sessions are required to use session persistent authentication' + } + + # check failure url from default + if ([string]::IsNullOrEmpty($FailureUrl)) { + $FailureUrl = $tmpAuth.Failure.Url + } + + # check failure message from default + if ([string]::IsNullOrEmpty($FailureMessage)) { + $FailureMessage = $tmpAuth.Failure.Message + } + + # check success url from default + if ([string]::IsNullOrEmpty($SuccessUrl)) { + $SuccessUrl = $tmpAuth.Success.Url + } + + # check success use origin from default + if (!$SuccessUseOrigin) { + $SuccessUseOrigin = $tmpAuth.Success.UseOrigin + } + + # deal with using vars in scriptblock + if ($Valid -ieq 'all') { + if ($null -eq $ScriptBlock) { + throw 'A Scriptblock for merging multiple authenticated users into 1 object is required When Valid is All' + } + + $ScriptBlock, $usingVars = Convert-PodeScopedVariables -ScriptBlock $ScriptBlock -PSSession $PSCmdlet.SessionState + } + + # set parent auth + foreach ($authName in $Authentication) { + $PodeContext.Server.Authentications.Methods[$authName].Parent = $Name + } + + # add auth method to server + $PodeContext.Server.Authentications.Methods[$Name] = @{ + Name = $Name + Authentications = @($Authentication) + PassOne = ($Valid -ieq 'one') + ScriptBlock = @{ + Script = $ScriptBlock + UsingVariables = $usingVars + } + Default = $Default + Sessionless = $Sessionless.IsPresent + Failure = @{ + Url = $FailureUrl + Message = $FailureMessage + } + Success = @{ + Url = $SuccessUrl + UseOrigin = $SuccessUseOrigin.IsPresent + } + Cache = @{} + Merged = $true + Parent = $null + } +} + <# .SYNOPSIS Gets an Authentication method. @@ -798,22 +987,103 @@ The Name of an Authentication method. .EXAMPLE Get-PodeAuth -Name 'Main' #> -function Get-PodeAuth -{ +function Get-PodeAuth { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name ) # ensure the name exists - if (!(Test-PodeAuth -Name $Name)) { + if (!(Test-PodeAuthExists -Name $Name)) { throw "Authentication method not defined: $($Name)" } # get auth method - return $PodeContext.Server.Authentications[$Name] + return $PodeContext.Server.Authentications.Methods[$Name] +} + +<# +.SYNOPSIS +Test if an Authentication method exists. + +.DESCRIPTION +Test if an Authentication method exists. + +.PARAMETER Name +The Name of the Authentication method. + +.EXAMPLE +if (Test-PodeAuthExists -Name BasicAuth) { ... } +#> +function Test-PodeAuthExists { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [string] + $Name + ) + + return $PodeContext.Server.Authentications.Methods.ContainsKey($Name) +} + +<# +.SYNOPSIS +Test and invoke an Authentication method to verify a user. + +.DESCRIPTION +Test and invoke an Authentication method to verify a user. This will verify a user's credentials on the request. +When testing OAuth2 methods, the first attempt will trigger a redirect to the provider and $false will be returned. + +.PARAMETER Name +The Name of the Authentication method. + +.PARAMETER IgnoreSession +If supplied, authentication will be re-verified on each call even if a valid session exists on the request. + +.EXAMPLE +if (Test-PodeAuth -Name 'BasicAuth') { ... } + +.EXAMPLE +if (Test-PodeAuth -Name 'FormAuth' -IgnoreSession) { ... } +#> +function Test-PodeAuth { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [string] + $Name, + + [switch] + $IgnoreSession + ) + + # if the session already has a user/isAuth'd, then skip auth - or allow anon + if (!$IgnoreSession -and (Test-PodeSessionsInUse) -and (Test-PodeAuthUser)) { + return $true + } + + try { + $result = Invoke-PodeAuthValidation -Name $Name + } + catch { + $_ | Write-PodeErrorLog + return $false + } + + # did the auth force a redirect? + if ($result.Redirected) { + return $false + } + + # if auth failed, set appropriate response headers/redirects + if (!$result.Success) { + return $false + } + + # successful auth + return $true } <# @@ -889,15 +1159,14 @@ New-PodeAuthScheme -Form | Add-PodeAuthWindowsAd -Name 'WinAuth' -NoGroups .EXAMPLE New-PodeAuthScheme -Form | Add-PodeAuthWindowsAd -Name 'UnixAuth' -Server 'testdomain.company.com' -Domain 'testdomain' #> -function Add-PodeAuthWindowsAd -{ - [CmdletBinding(DefaultParameterSetName='Groups')] - param ( - [Parameter(Mandatory=$true)] +function Add-PodeAuthWindowsAd { + [CmdletBinding(DefaultParameterSetName = 'Groups')] + param( + [Parameter(Mandatory = $true)] [string] $Name, - [Parameter(Mandatory=$true, ValueFromPipeline=$true)] + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [hashtable] $Scheme, @@ -914,7 +1183,7 @@ function Add-PodeAuthWindowsAd [string] $SearchBase, - [Parameter(ParameterSetName='Groups')] + [Parameter(ParameterSetName = 'Groups')] [string[]] $Groups, @@ -941,11 +1210,11 @@ function Add-PodeAuthWindowsAd [switch] $Sessionless, - [Parameter(ParameterSetName='NoGroups')] + [Parameter(ParameterSetName = 'NoGroups')] [switch] $NoGroups, - [Parameter(ParameterSetName='Groups')] + [Parameter(ParameterSetName = 'Groups')] [switch] $DirectGroups, @@ -963,7 +1232,7 @@ function Add-PodeAuthWindowsAd ) # ensure the name doesn't already exist - if (Test-PodeAuth -Name $Name) { + if (Test-PodeAuthExists -Name $Name) { throw "Windows AD Authentication method already defined: $($Name)" } @@ -1002,33 +1271,168 @@ function Add-PodeAuthWindowsAd } # add Windows AD auth method to server - $PodeContext.Server.Authentications[$Name] = @{ - Scheme = $Scheme + $PodeContext.Server.Authentications.Methods[$Name] = @{ + Scheme = $Scheme ScriptBlock = (Get-PodeAuthWindowsADMethod) - Arguments = @{ - Server = $Fqdn - Domain = $Domain - SearchBase = $SearchBase - Users = $Users - Groups = $Groups - NoGroups = $NoGroups - DirectGroups = $DirectGroups + Arguments = @{ + Server = $Fqdn + Domain = $Domain + SearchBase = $SearchBase + Users = $Users + Groups = $Groups + NoGroups = $NoGroups + DirectGroups = $DirectGroups KeepCredential = $KeepCredential - Provider = (Get-PodeAuthADProvider -OpenLDAP:$OpenLDAP -ADModule:$ADModule) - ScriptBlock = @{ - Script = $ScriptBlock + Provider = (Get-PodeAuthADProvider -OpenLDAP:$OpenLDAP -ADModule:$ADModule) + ScriptBlock = @{ + Script = $ScriptBlock UsingVariables = $usingVars } } Sessionless = $Sessionless - Failure = @{ - Url = $FailureUrl + Failure = @{ + Url = $FailureUrl Message = $FailureMessage } - Success = @{ - Url = $SuccessUrl + Success = @{ + Url = $SuccessUrl UseOrigin = $SuccessUseOrigin } + Cache = @{} + Merged = $false + Parent = $null + } +} + +<# +.SYNOPSIS +Adds the inbuilt Session Authentication method for verifying an authenticated session is present on Requests. + +.DESCRIPTION +Adds the inbuilt Session Authentication method for verifying an authenticated session is present on Requests. + +.PARAMETER Name +A unique Name for the Authentication method. + +.PARAMETER FailureUrl +The URL to redirect to when authentication fails. + +.PARAMETER FailureMessage +An override Message to throw when authentication fails. + +.PARAMETER SuccessUrl +The URL to redirect to when authentication succeeds when logging in. + +.PARAMETER ScriptBlock +Optional ScriptBlock that is passed the found user object for further validation. + +.PARAMETER Middleware +An array of ScriptBlocks for optional Middleware to run before the Scheme's scriptblock. + +.PARAMETER SuccessUseOrigin +If supplied, successful authentication from a login page will redirect back to the originating page instead of the FailureUrl. + +.EXAMPLE +Add-PodeAuthSession -Name 'SessionAuth' -FailureUrl '/login' +#> +function Add-PodeAuthSession { + [CmdletBinding(DefaultParameterSetName = 'Groups')] + param( + [Parameter(Mandatory = $true)] + [string] + $Name, + + [Parameter()] + [string] + $FailureUrl, + + [Parameter()] + [string] + $FailureMessage, + + [Parameter()] + [string] + $SuccessUrl, + + [Parameter()] + [scriptblock] + $ScriptBlock, + + [Parameter()] + [object[]] + $Middleware, + + [switch] + $SuccessUseOrigin + ) + + # if sessions haven't been setup, error + if (!(Test-PodeSessionsConfigured)) { + throw 'Sessions have not been configured' + } + + # ensure the name doesn't already exist + if (Test-PodeAuthExists -Name $Name) { + throw "Authentication method already defined: $($Name)" + } + + # if we have a scriptblock, deal with using vars + if ($null -ne $ScriptBlock) { + $ScriptBlock, $usingVars = Convert-PodeScopedVariables -ScriptBlock $ScriptBlock -PSSession $PSCmdlet.SessionState + } + + # create the auth scheme for getting the session + $scheme = New-PodeAuthScheme -Custom -Middleware $Middleware -ScriptBlock { + param($options) + + # 401 if sessions not used + if (!(Test-PodeSessionsInUse)) { + Revoke-PodeSession + return @{ + Message = 'Sessions are not being used' + Code = 401 + } + } + + # 401 if no authenticated user + if (!(Test-PodeAuthUser)) { + Revoke-PodeSession + return @{ + Message = 'Session not authenticated' + Code = 401 + } + } + + # return user + return @($WebEvent.Session.Data.Auth) + } + + # add a custom auth method to return user back + $method = { + param($user, $options) + $result = @{ User = $user } + + # call additional scriptblock if supplied + if ($null -ne $options.ScriptBlock.Script) { + $result = Invoke-PodeAuthInbuiltScriptBlock -User $result.User -ScriptBlock $options.ScriptBlock.Script -UsingVariables $options.ScriptBlock.UsingVariables + } + + # return user back + return $result + } + + $scheme | Add-PodeAuth ` + -Name $Name ` + -ScriptBlock $method ` + -FailureUrl $FailureUrl ` + -FailureMessage $FailureMessage ` + -SuccessUrl $SuccessUrl ` + -SuccessUseOrigin:$SuccessUseOrigin ` + -ArgumentList @{ + ScriptBlock = @{ + Script = $ScriptBlock + UsingVariables = $usingVars + } } } @@ -1045,16 +1449,15 @@ The Name of the Authentication method. .EXAMPLE Remove-PodeAuth -Name 'Login' #> -function Remove-PodeAuth -{ +function Remove-PodeAuth { [CmdletBinding()] - param ( - [Parameter(Mandatory=$true, ValueFromPipeline=$true)] + param( + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [string] $Name ) - $null = $PodeContext.Server.Authentications.Remove($Name) + $null = $PodeContext.Server.Authentications.Methods.Remove($Name) } <# @@ -1067,12 +1470,11 @@ Clear all defined Authentication methods. .EXAMPLE Clear-PodeAuth #> -function Clear-PodeAuth -{ +function Clear-PodeAuth { [CmdletBinding()] param() - $PodeContext.Server.Authentications.Clear() + $PodeContext.Server.Authentications.Methods.Clear() } <# @@ -1097,15 +1499,14 @@ Add-PodeAuthMiddleware -Name 'GlobalAuth' -Authentication AuthName .EXAMPLE Add-PodeAuthMiddleware -Name 'GlobalAuth' -Authentication AuthName -Route '/api/*' #> -function Add-PodeAuthMiddleware -{ +function Add-PodeAuthMiddleware { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [Alias('Auth')] [string] $Authentication, @@ -1115,7 +1516,7 @@ function Add-PodeAuthMiddleware $Route ) - if (!(Test-PodeAuth -Name $Authentication)) { + if (!(Test-PodeAuthExists -Name $Authentication)) { throw "Authentication method does not exist: $($Authentication)" } @@ -1184,15 +1585,14 @@ Add-PodeAuthIIS -Name 'IISAuth' -Groups @('Developers') .EXAMPLE Add-PodeAuthIIS -Name 'IISAuth' -NoGroups #> -function Add-PodeAuthIIS -{ - [CmdletBinding(DefaultParameterSetName='Groups')] - param ( - [Parameter(Mandatory=$true)] +function Add-PodeAuthIIS { + [CmdletBinding(DefaultParameterSetName = 'Groups')] + param( + [Parameter(Mandatory = $true)] [string] $Name, - [Parameter(ParameterSetName='Groups')] + [Parameter(ParameterSetName = 'Groups')] [string[]] $Groups, @@ -1223,11 +1623,11 @@ function Add-PodeAuthIIS [switch] $Sessionless, - [Parameter(ParameterSetName='NoGroups')] + [Parameter(ParameterSetName = 'NoGroups')] [switch] $NoGroups, - [Parameter(ParameterSetName='Groups')] + [Parameter(ParameterSetName = 'Groups')] [switch] $DirectGroups, @@ -1243,11 +1643,11 @@ function Add-PodeAuthIIS # ensure we're on Windows! if (!(Test-PodeIsWindows)) { - throw "IIS Authentication support is for Windows only" + throw 'IIS Authentication support is for Windows only' } # ensure the name doesn't already exist - if (Test-PodeAuth -Name $Name) { + if (Test-PodeAuthExists -Name $Name) { throw "IIS Authentication method already defined: $($Name)" } @@ -1271,7 +1671,7 @@ function Add-PodeAuthIIS if (!(Test-PodeHeader -Name $header)) { return @{ Message = "No $($header) header found" - Code = 401 + Code = 401 } } @@ -1292,17 +1692,17 @@ function Add-PodeAuthIIS -Sessionless:$Sessionless ` -SuccessUseOrigin:$SuccessUseOrigin ` -ArgumentList @{ - Users = $Users - Groups = $Groups - NoGroups = $NoGroups - DirectGroups = $DirectGroups - Provider = (Get-PodeAuthADProvider -ADModule:$ADModule) - NoLocalCheck = $NoLocalCheck - ScriptBlock = @{ - Script = $ScriptBlock - UsingVariables = $usingVars - } + Users = $Users + Groups = $Groups + NoGroups = $NoGroups + DirectGroups = $DirectGroups + Provider = (Get-PodeAuthADProvider -ADModule:$ADModule) + NoLocalCheck = $NoLocalCheck + ScriptBlock = @{ + Script = $ScriptBlock + UsingVariables = $usingVars } + } } <# @@ -1354,15 +1754,14 @@ New-PodeAuthScheme -Form | Add-PodeAuthUserFile -Name 'Login' .EXAMPLE New-PodeAuthScheme -Form | Add-PodeAuthUserFile -Name 'Login' -FilePath './custom/path/users.json' #> -function Add-PodeAuthUserFile -{ +function Add-PodeAuthUserFile { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name, - [Parameter(Mandatory=$true, ValueFromPipeline=$true)] + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [hashtable] $Scheme, @@ -1378,7 +1777,7 @@ function Add-PodeAuthUserFile [string[]] $Users, - [Parameter(ParameterSetName='Hmac')] + [Parameter(ParameterSetName = 'Hmac')] [string] $HmacSecret, @@ -1406,7 +1805,7 @@ function Add-PodeAuthUserFile ) # ensure the name doesn't already exist - if (Test-PodeAuth -Name $Name) { + if (Test-PodeAuthExists -Name $Name) { throw "User File Authentication method already defined: $($Name)" } @@ -1439,28 +1838,31 @@ function Add-PodeAuthUserFile } # add Windows AD auth method to server - $PodeContext.Server.Authentications[$Name] = @{ - Scheme = $Scheme + $PodeContext.Server.Authentications.Methods[$Name] = @{ + Scheme = $Scheme ScriptBlock = (Get-PodeAuthUserFileMethod) - Arguments = @{ - FilePath = $FilePath - Users = $Users - Groups = $Groups - HmacSecret = $HmacSecret + Arguments = @{ + FilePath = $FilePath + Users = $Users + Groups = $Groups + HmacSecret = $HmacSecret ScriptBlock = @{ - Script = $ScriptBlock + Script = $ScriptBlock UsingVariables = $usingVars } } Sessionless = $Sessionless - Failure = @{ - Url = $FailureUrl + Failure = @{ + Url = $FailureUrl Message = $FailureMessage } - Success = @{ - Url = $SuccessUrl + Success = @{ + Url = $SuccessUrl UseOrigin = $SuccessUseOrigin } + Cache = @{} + Merged = $false + Parent = $null } } @@ -1513,19 +1915,18 @@ New-PodeAuthScheme -Basic | Add-PodeAuthWindowsLocal -Name 'WinAuth' -Groups @(' .EXAMPLE New-PodeAuthScheme -Form | Add-PodeAuthWindowsLocal -Name 'WinAuth' -NoGroups #> -function Add-PodeAuthWindowsLocal -{ - [CmdletBinding(DefaultParameterSetName='Groups')] +function Add-PodeAuthWindowsLocal { + [CmdletBinding(DefaultParameterSetName = 'Groups')] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name, - [Parameter(Mandatory=$true, ValueFromPipeline=$true)] + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [hashtable] $Scheme, - [Parameter(ParameterSetName='Groups')] + [Parameter(ParameterSetName = 'Groups')] [string[]] $Groups, @@ -1552,7 +1953,7 @@ function Add-PodeAuthWindowsLocal [switch] $Sessionless, - [Parameter(ParameterSetName='NoGroups')] + [Parameter(ParameterSetName = 'NoGroups')] [switch] $NoGroups, @@ -1562,11 +1963,11 @@ function Add-PodeAuthWindowsLocal # ensure we're on Windows! if (!(Test-PodeIsWindows)) { - throw "Windows Local Authentication support is for Windows only" + throw 'Windows Local Authentication support is for Windows only' } # ensure the name doesn't already exist - if (Test-PodeAuth -Name $Name) { + if (Test-PodeAuthExists -Name $Name) { throw "Windows Local Authentication method already defined: $($Name)" } @@ -1586,27 +1987,30 @@ function Add-PodeAuthWindowsLocal } # add Windows Local auth method to server - $PodeContext.Server.Authentications[$Name] = @{ - Scheme = $Scheme + $PodeContext.Server.Authentications.Methods[$Name] = @{ + Scheme = $Scheme ScriptBlock = (Get-PodeAuthWindowsLocalMethod) - Arguments = @{ - Users = $Users - Groups = $Groups - NoGroups = $NoGroups + Arguments = @{ + Users = $Users + Groups = $Groups + NoGroups = $NoGroups ScriptBlock = @{ - Script = $ScriptBlock + Script = $ScriptBlock UsingVariables = $usingVars } } Sessionless = $Sessionless - Failure = @{ - Url = $FailureUrl + Failure = @{ + Url = $FailureUrl Message = $FailureMessage } - Success = @{ - Url = $SuccessUrl + Success = @{ + Url = $SuccessUrl UseOrigin = $SuccessUseOrigin } + Cache = @{} + Merged = $false + Parent = $null } } @@ -1632,15 +2036,14 @@ ConvertTo-PodeJwt -Header @{ alg = 'none' } -Payload @{ sub = '123'; name = 'Joh .EXAMPLE ConvertTo-PodeJwt -Header @{ alg = 'hs256' } -Payload @{ sub = '123'; name = 'John' } -Secret 'abc' #> -function ConvertTo-PodeJwt -{ +function ConvertTo-PodeJwt { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [hashtable] $Header, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [hashtable] $Payload, @@ -1650,7 +2053,7 @@ function ConvertTo-PodeJwt # validate header if ([string]::IsNullOrWhiteSpace($Header.alg)) { - throw "No algorithm supplied in JWT Header" + throw 'No algorithm supplied in JWT Header' } # convert the header @@ -1694,18 +2097,17 @@ Skip signature verification, and return the decoded payload. .EXAMPLE ConvertFrom-PodeJwt -Token "eyJ0eXAiOiJKV1QiLCJhbGciOiJoczI1NiJ9.eyJleHAiOjE2MjI1NTMyMTQsIm5hbWUiOiJKb2huIERvZSIsInN1YiI6IjEyMyJ9.LP-O8OKwix91a-SZwVK35gEClLZQmsORbW0un2Z4RkY" #> -function ConvertFrom-PodeJwt -{ - [CmdletBinding(DefaultParameterSetName='Secret')] +function ConvertFrom-PodeJwt { + [CmdletBinding(DefaultParameterSetName = 'Secret')] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Token, - [Parameter(ParameterSetName='Signed')] + [Parameter(ParameterSetName = 'Signed')] $Secret = $null, - [Parameter(ParameterSetName='Ignore')] + [Parameter(ParameterSetName = 'Ignore')] [switch] $IgnoreSignature ) @@ -1715,13 +2117,13 @@ function ConvertFrom-PodeJwt # check number of parts (should be 3) if ($parts.Length -ne 3) { - throw "Invalid JWT supplied" + throw 'Invalid JWT supplied' } # convert to header $header = ConvertFrom-PodeJwtBase64Value -Value $parts[0] if ([string]::IsNullOrWhiteSpace($header.alg)) { - throw "Invalid JWT header algorithm supplied" + throw 'Invalid JWT header algorithm supplied' } # convert to payload @@ -1742,7 +2144,7 @@ function ConvertFrom-PodeJwt } if (![string]::IsNullOrWhiteSpace($signature) -and $isNoneAlg) { - throw "Expected no JWT signature to be supplied" + throw 'Expected no JWT signature to be supplied' } if ($isNoneAlg -and ($null -ne $Secret) -and ($Secret.Length -gt 0)) { @@ -1762,13 +2164,57 @@ function ConvertFrom-PodeJwt $sig = New-PodeJwtSignature -Algorithm $header.alg -Token $sig -SecretBytes $Secret if ($sig -ne $parts[2]) { - throw "Invalid JWT signature supplied" + throw 'Invalid JWT signature supplied' } # it's valid return the payload! return $payload } +<# +.SYNOPSIS +Validates JSON Web Tokens (JWT) claims. + +.DESCRIPTION +Validates JSON Web Tokens (JWT) claims. Checks time related claims: 'exp' and 'nbf'. + +.PARAMETER Payload +Object containing JWT claims. Some of them are: + - exp (expiration time) + - nbf (not before) + +.EXAMPLE +Test-PodeJwt @{exp = 2696258821 } + +.EXAMPLE +Test-PodeJwt -Payload @{nbf = 1696258821 } +#> +function Test-PodeJwt { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [pscustomobject] + $Payload + ) + + $now = [datetime]::UtcNow + $unixStart = [datetime]::new(1970, 1, 1, 0, 0, [DateTimeKind]::Utc) + + # validate expiry + if (![string]::IsNullOrWhiteSpace($Payload.exp)) { + if ($now -gt $unixStart.AddSeconds($Payload.exp)) { + throw 'The JWT has expired' + } + } + + # validate not-before + if (![string]::IsNullOrWhiteSpace($Payload.nbf)) { + if ($now -lt $unixStart.AddSeconds($Payload.nbf)) { + throw 'The JWT is not yet valid for use' + } + } +} + <# .SYNOPSIS Automatically loads auth ps1 files @@ -1785,8 +2231,7 @@ Use-PodeAuth .EXAMPLE Use-PodeAuth -Path './my-auth' #> -function Use-PodeAuth -{ +function Use-PodeAuth { [CmdletBinding()] param( [Parameter()] @@ -1834,11 +2279,10 @@ ConvertFrom-PodeOIDCDiscovery -Url 'https://accounts.google.com/.well-known/open .EXAMPLE ConvertFrom-PodeOIDCDiscovery -Url 'https://accounts.google.com' -ClientId some_id -UsePKCE #> -function ConvertFrom-PodeOIDCDiscovery -{ +function ConvertFrom-PodeOIDCDiscovery { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Url, @@ -1846,7 +2290,7 @@ function ConvertFrom-PodeOIDCDiscovery [string[]] $Scope, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $ClientId, @@ -1858,7 +2302,7 @@ function ConvertFrom-PodeOIDCDiscovery [string] $RedirectUrl, - [Parameter(ValueFromPipeline=$true)] + [Parameter(ValueFromPipeline = $true)] [hashtable] $InnerScheme, @@ -1892,10 +2336,10 @@ function ConvertFrom-PodeOIDCDiscovery if (($null -ne $Scope) -and ($Scope.Length -gt 0)) { $scopes = @(foreach ($s in $Scope) { - if ($s -iin $config.scopes_supported) { - $s - } - }) + if ($s -iin $config.scopes_supported) { + $s + } + }) } # pkce code challenge method @@ -1904,7 +2348,7 @@ function ConvertFrom-PodeOIDCDiscovery $codeMethod = 'plain' } - return (New-PodeAuthScheme ` + return New-PodeAuthScheme ` -OAuth2 ` -ClientId $ClientId ` -ClientSecret $ClientSecret ` @@ -1916,7 +2360,7 @@ function ConvertFrom-PodeOIDCDiscovery -InnerScheme $InnerScheme ` -Middleware $Middleware ` -CodeChallengeMethod $codeMethod ` - -UsePKCE:$UsePKCE) + -UsePKCE:$UsePKCE } <# @@ -1926,16 +2370,71 @@ Test whether the current WebEvent or Session has an authenticated user. .DESCRIPTION Test whether the current WebEvent or Session has an authenticated user. Returns true if there is an authenticated user. +.PARAMETER IgnoreSession +If supplied, only the Auth object in the WebEvent will be checked and the Session will be skipped. + .EXAMPLE if (Test-PodeAuthUser) { ... } #> -function Test-PodeAuthUser -{ +function Test-PodeAuthUser { [CmdletBinding()] - param() + param( + [switch] + $IgnoreSession + ) + + # auth middleware + if (($null -ne $WebEvent.Auth) -and $WebEvent.Auth.IsAuthenticated) { + $auth = $WebEvent.Auth + } + + # session? + elseif (!$IgnoreSession -and ($null -ne $WebEvent.Session.Data.Auth) -and $WebEvent.Session.Data.Auth.IsAuthenticated) { + $auth = $WebEvent.Session.Data.Auth + } + + # null? + if (($null -eq $auth) -or ($null -eq $auth.User)) { + return $false + } - return ( - (($null -ne $WebEvent.Auth.User) -and $WebEvent.Auth.IsAuthenticated) -or - (($null -ne $WebEvent.Session.Data.Auth.User) -and $WebEvent.Session.Data.Auth.IsAuthenticated) + return ($null -ne $auth.User) +} + +<# +.SYNOPSIS +Get the authenticated user from the WebEvent or Session. + +.DESCRIPTION +Get the authenticated user from the WebEvent or Session. This is similar to calling $Webevent.Auth.User. + +.PARAMETER IgnoreSession +If supplied, only the Auth object in the WebEvent will be used and the Session will be skipped. + +.EXAMPLE +$user = Get-PodeAuthUser +#> +function Get-PodeAuthUser { + [CmdletBinding()] + param( + [switch] + $IgnoreSession ) + + # auth middleware + if (($null -ne $WebEvent.Auth) -and $WebEvent.Auth.IsAuthenticated) { + $auth = $WebEvent.Auth + } + + # session? + elseif (!$IgnoreSession -and ($null -ne $WebEvent.Session.Data.Auth) -and $WebEvent.Session.Data.Auth.IsAuthenticated) { + $auth = $WebEvent.Session.Data.Auth + } + + # null? + if (($null -eq $auth) -or ($null -eq $auth.User)) { + return $null + } + + return $auth.User } \ No newline at end of file diff --git a/src/Public/AutoImport.ps1 b/src/Public/AutoImport.ps1 index 6fef4b004..a02005c60 100644 --- a/src/Public/AutoImport.ps1 +++ b/src/Public/AutoImport.ps1 @@ -11,11 +11,10 @@ The Name(s) of modules to export. .EXAMPLE Export-PodeModule -Name Mod1, Mod2 #> -function Export-PodeModule -{ +function Export-PodeModule { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string[]] $Name ) @@ -36,11 +35,10 @@ The Name(s) of snapins to export. .EXAMPLE Export-PodeSnapin -Name Mod1, Mod2 #> -function Export-PodeSnapin -{ +function Export-PodeSnapin { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string[]] $Name ) @@ -66,11 +64,10 @@ The Name(s) of functions to export. .EXAMPLE Export-PodeFunction -Name Mod1, Mod2 #> -function Export-PodeFunction -{ +function Export-PodeFunction { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string[]] $Name ) @@ -94,11 +91,10 @@ The Type of the Secret Vault to import - only option currently is SecretManageme .EXAMPLE Export-PodeSecretVault -Name Vault1, Vault2 #> -function Export-PodeSecretVault -{ +function Export-PodeSecretVault { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string[]] $Name, diff --git a/src/Public/Cookies.ps1 b/src/Public/Cookies.ps1 index edb51c37d..d319d442a 100644 --- a/src/Public/Cookies.ps1 +++ b/src/Public/Cookies.ps1 @@ -38,12 +38,11 @@ Set-PodeCookie -Name 'Views' -Value 2 -Secret 'hunter2' .EXAMPLE Set-PodeCookie -Name 'Views' -Value 2 -Duration 3600 #> -function Set-PodeCookie -{ - [CmdletBinding(DefaultParameterSetName='Duration')] +function Set-PodeCookie { + [CmdletBinding(DefaultParameterSetName = 'Duration')] [OutputType([hashtable])] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name, @@ -55,11 +54,11 @@ function Set-PodeCookie [string] $Secret, - [Parameter(ParameterSetName='Duration')] + [Parameter(ParameterSetName = 'Duration')] [int] $Duration = 0, - [Parameter(ParameterSetName='ExpiryDate')] + [Parameter(ParameterSetName = 'ExpiryDate')] [datetime] $ExpiryDate, @@ -124,12 +123,11 @@ Get-PodeCookie -Name 'Views' .EXAMPLE Get-PodeCookie -Name 'Views' -Secret 'hunter2' #> -function Get-PodeCookie -{ +function Get-PodeCookie { [CmdletBinding()] [OutputType([hashtable])] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name, @@ -181,11 +179,10 @@ Get-PodeCookieValue -Name 'Views' .EXAMPLE Get-PodeCookieValue -Name 'Views' -Secret 'hunter2' #> -function Get-PodeCookieValue -{ +function Get-PodeCookieValue { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name, @@ -215,12 +212,11 @@ The name of the cookie to test for on the Request. .EXAMPLE Test-PodeCookie -Name 'Views' #> -function Test-PodeCookie -{ +function Test-PodeCookie { [CmdletBinding()] [OutputType([bool])] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name ) @@ -242,11 +238,10 @@ The name of the cookie to be removed. .EXAMPLE Remove-PodeCookie -Name 'Views' #> -function Remove-PodeCookie -{ +function Remove-PodeCookie { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name ) @@ -283,12 +278,11 @@ A secret to use for attempting to unsign the cookie's value. .EXAMPLE Test-PodeCookieSigned -Name 'Views' -Secret 'hunter2' #> -function Test-PodeCookieSigned -{ +function Test-PodeCookieSigned { [CmdletBinding()] [OutputType([bool])] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name, @@ -328,20 +322,19 @@ Update-PodeCookieExpiry -Name 'Views' -Duration 1800 .EXAMPLE Update-PodeCookieExpiry -Name 'Views' -ExpiryDate ([datetime]::UtcNow.AddSeconds(1800)) #> -function Update-PodeCookieExpiry -{ - [CmdletBinding(DefaultParameterSetName='Duration')] +function Update-PodeCookieExpiry { + [CmdletBinding(DefaultParameterSetName = 'Duration')] [OutputType([hashtable])] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name, - [Parameter(ParameterSetName='Duration')] + [Parameter(ParameterSetName = 'Duration')] [int] $Duration = 0, - [Parameter(ParameterSetName='ExpiryDate')] + [Parameter(ParameterSetName = 'ExpiryDate')] [datetime] $ExpiryDate ) @@ -394,19 +387,18 @@ Set-PodeCookieSecret -Name 'my-secret' -Value 'shhhh!' .EXAMPLE Set-PodeCookieSecret -Value 'hunter2' -Global #> -function Set-PodeCookieSecret -{ +function Set-PodeCookieSecret { [CmdletBinding()] param( - [Parameter(Mandatory=$true, ParameterSetName='General')] + [Parameter(Mandatory = $true, ParameterSetName = 'General')] [string] $Name, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Value, - [Parameter(ParameterSetName='Global')] + [Parameter(ParameterSetName = 'Global')] [switch] $Global ) @@ -437,16 +429,15 @@ Get-PodeCookieSecret -Name 'my-secret' .EXAMPLE Get-PodeCookieSecret -Global #> -function Get-PodeCookieSecret -{ +function Get-PodeCookieSecret { [CmdletBinding()] [OutputType([string])] param( - [Parameter(Mandatory=$true, ParameterSetName='General')] + [Parameter(Mandatory = $true, ParameterSetName = 'General')] [string] $Name, - [Parameter(ParameterSetName='Global')] + [Parameter(ParameterSetName = 'Global')] [switch] $Global ) diff --git a/src/Public/Core.ps1 b/src/Public/Core.ps1 index cf8871625..86a09c7e4 100644 --- a/src/Public/Core.ps1 +++ b/src/Public/Core.ps1 @@ -62,15 +62,14 @@ Start-PodeServer -Interval 10 { /* logic */ } .EXAMPLE Start-PodeServer -Request $LambdaInput -ServerlessType AwsLambda { /* logic */ } #> -function Start-PodeServer -{ - [CmdletBinding(DefaultParameterSetName='Script')] - param ( - [Parameter(Mandatory=$true, ValueFromPipeline=$true, Position=0, ParameterSetName='Script')] +function Start-PodeServer { + [CmdletBinding(DefaultParameterSetName = 'Script')] + param( + [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0, ParameterSetName = 'Script')] [scriptblock] $ScriptBlock, - [Parameter(Mandatory=$true, ParameterSetName='File')] + [Parameter(Mandatory = $true, ParameterSetName = 'File')] [string] $FilePath, @@ -121,7 +120,7 @@ function Start-PodeServer [switch] $Browse, - [Parameter(ParameterSetName='File')] + [Parameter(ParameterSetName = 'File')] [switch] $CurrentPath ) @@ -243,8 +242,7 @@ Closes the Pode server. .EXAMPLE Close-PodeServer #> -function Close-PodeServer -{ +function Close-PodeServer { [CmdletBinding()] param() @@ -261,8 +259,7 @@ Restarts the Pode server. .EXAMPLE Restart-PodeServer #> -function Restart-PodeServer -{ +function Restart-PodeServer { [CmdletBinding()] param() @@ -324,8 +321,7 @@ Start-PodeStaticServer -Address '127.0.0.3' -Port 8000 .EXAMPLE Start-PodeStaticServer -Path '/installers' -DownloadOnly #> -function Start-PodeStaticServer -{ +function Start-PodeStaticServer { [CmdletBinding()] param( [Parameter()] @@ -423,11 +419,10 @@ pode build .EXAMPLE pode start #> -function Pode -{ +function Pode { [CmdletBinding()] - param ( - [Parameter(Mandatory=$true)] + param( + [Parameter(Mandatory = $true)] [ValidateSet('init', 'test', 'start', 'install', 'build')] [Alias('a')] [string] @@ -445,18 +440,18 @@ function Pode # default config data that's used to populate on init $map = @{ - 'name' = $name; - 'version' = '1.0.0'; - 'description' = ''; - 'main' = './server.ps1'; - 'scripts' = @{ - 'start' = './server.ps1'; - 'install' = 'yarn install --force --ignore-scripts --modules-folder pode_modules'; - "build" = 'psake'; - 'test' = 'invoke-pester ./tests/*.ps1' - }; - 'author' = ''; - 'license' = 'MIT'; + 'name' = $name + 'version' = '1.0.0' + 'description' = '' + 'main' = './server.ps1' + 'scripts' = @{ + 'start' = './server.ps1' + 'install' = 'yarn install --force --ignore-scripts --modules-folder pode_modules' + 'build' = 'psake' + 'test' = 'invoke-pester ./tests/*.ps1' + } + 'author' = '' + 'license' = 'MIT' } # check and load config if already exists @@ -490,8 +485,7 @@ function Pode } } - switch ($Action.ToLowerInvariant()) - { + switch ($Action.ToLowerInvariant()) { 'init' { $v = Read-Host -Prompt "name ($($map.name))" if (![string]::IsNullOrWhiteSpace($v)) { $map.name = $v } @@ -499,12 +493,12 @@ function Pode $v = Read-Host -Prompt "version ($($map.version))" if (![string]::IsNullOrWhiteSpace($v)) { $map.version = $v } - $map.description = Read-Host -Prompt "description" + $map.description = Read-Host -Prompt 'description' $v = Read-Host -Prompt "entry point ($($map.main))" if (![string]::IsNullOrWhiteSpace($v)) { $map.main = $v; $map.scripts.start = $v } - $map.author = Read-Host -Prompt "author" + $map.author = Read-Host -Prompt 'author' $v = Read-Host -Prompt "license ($($map.license))" if (![string]::IsNullOrWhiteSpace($v)) { $map.license = $v } @@ -573,11 +567,10 @@ Stops the Application from appearing on the taskbar. .EXAMPLE Show-PodeGui -Title 'MyApplication' -WindowState 'Maximized' #> -function Show-PodeGui -{ +function Show-PodeGui { [CmdletBinding()] - param ( - [Parameter(Mandatory=$true, ValueFromPipeline=$true)] + param( + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [string] $Title, @@ -722,6 +715,9 @@ A quick description of the Endpoint - normally used in OpenAPI. .PARAMETER Acknowledge An optional Acknowledge message to send to clients when they first connect, for TCP and SMTP endpoints only. +.PARAMETER SslProtocol +One or more optional SSL Protocols this endpoints supports. (Default: SSL3/TLS12 - Just TLS12 on MacOS). + .PARAMETER CRLFMessageEnd If supplied, TCP endpoints will expect incoming data to end with CRLF. @@ -758,10 +754,9 @@ Add-PodeEndpoint -Address 127.0.0.2 -Hostname dev.pode.com -Port 8443 -Protocol .EXAMPLE Add-PodeEndpoint -Address live.pode.com -Protocol Https -CertificateThumbprint '2A9467F7D3940243D6C07DE61E7FCCE292' #> -function Add-PodeEndpoint -{ - [CmdletBinding(DefaultParameterSetName='Default')] - param ( +function Add-PodeEndpoint { + [CmdletBinding(DefaultParameterSetName = 'Default')] + param( [Parameter()] [string] $Address = 'localhost', @@ -779,45 +774,45 @@ function Add-PodeEndpoint [string] $Protocol, - [Parameter(Mandatory=$true, ParameterSetName='CertFile')] + [Parameter(Mandatory = $true, ParameterSetName = 'CertFile')] [string] $Certificate = $null, - [Parameter(ParameterSetName='CertFile')] + [Parameter(ParameterSetName = 'CertFile')] [string] $CertificatePassword = $null, - [Parameter(ParameterSetName='CertFile')] + [Parameter(ParameterSetName = 'CertFile')] [string] $CertificateKey = $null, - [Parameter(Mandatory=$true, ParameterSetName='CertThumb')] + [Parameter(Mandatory = $true, ParameterSetName = 'CertThumb')] [string] $CertificateThumbprint, - [Parameter(Mandatory=$true, ParameterSetName='CertName')] + [Parameter(Mandatory = $true, ParameterSetName = 'CertName')] [string] $CertificateName, - [Parameter(ParameterSetName='CertName')] - [Parameter(ParameterSetName='CertThumb')] + [Parameter(ParameterSetName = 'CertName')] + [Parameter(ParameterSetName = 'CertThumb')] [System.Security.Cryptography.X509Certificates.StoreName] $CertificateStoreName = 'My', - [Parameter(ParameterSetName='CertName')] - [Parameter(ParameterSetName='CertThumb')] + [Parameter(ParameterSetName = 'CertName')] + [Parameter(ParameterSetName = 'CertThumb')] [System.Security.Cryptography.X509Certificates.StoreLocation] $CertificateStoreLocation = 'CurrentUser', - [Parameter(Mandatory=$true, ParameterSetName='CertRaw')] + [Parameter(Mandatory = $true, ParameterSetName = 'CertRaw')] [X509Certificate] $X509Certificate = $null, - [Parameter(ParameterSetName='CertFile')] - [Parameter(ParameterSetName='CertThumb')] - [Parameter(ParameterSetName='CertName')] - [Parameter(ParameterSetName='CertRaw')] - [Parameter(ParameterSetName='CertSelf')] + [Parameter(ParameterSetName = 'CertFile')] + [Parameter(ParameterSetName = 'CertThumb')] + [Parameter(ParameterSetName = 'CertName')] + [Parameter(ParameterSetName = 'CertRaw')] + [Parameter(ParameterSetName = 'CertSelf')] [ValidateSet('Implicit', 'Explicit')] [string] $TlsMode = 'Implicit', @@ -838,13 +833,18 @@ function Add-PodeEndpoint [string] $Acknowledge, + [Parameter()] + [ValidateSet('Ssl2', 'Ssl3', 'Tls', 'Tls11', 'Tls12', 'Tls13')] + [string[]] + $SslProtocol = $null, + [switch] $CRLFMessageEnd, [switch] $Force, - [Parameter(ParameterSetName='CertSelf')] + [Parameter(ParameterSetName = 'CertSelf')] [switch] $SelfSigned, @@ -866,7 +866,7 @@ function Add-PodeEndpoint # if RedirectTo is supplied, then a Name is mandatory if (![string]::IsNullOrWhiteSpace($RedirectTo) -and [string]::IsNullOrWhiteSpace($Name)) { - throw "A Name is required for the endpoint if the RedirectTo parameter is supplied" + throw 'A Name is required for the endpoint if the RedirectTo parameter is supplied' } # get the type of endpoint @@ -917,54 +917,62 @@ function Add-PodeEndpoint # protocol must be https for client certs, or hosted behind a proxy like iis if (($Protocol -ine 'https') -and !(Test-PodeIsHosted) -and $AllowClientCertificate) { - throw "Client certificates are only supported on HTTPS endpoints" + throw 'Client certificates are only supported on HTTPS endpoints' } # explicit tls is only supported for smtp/tcp if (($type -inotin @('smtp', 'tcp')) -and ($TlsMode -ieq 'explicit')) { - throw "The Explicit TLS mode is only supported on SMTPS and TCPS endpoints" + throw 'The Explicit TLS mode is only supported on SMTPS and TCPS endpoints' } # ack message is only for smtp/tcp if (($type -inotin @('smtp', 'tcp')) -and ![string]::IsNullOrEmpty($Acknowledge)) { - throw "The Acknowledge message is only supported on SMTP and TCP endpoints" + throw 'The Acknowledge message is only supported on SMTP and TCP endpoints' } # crlf message end is only for tcp if (($type -ine 'tcp') -and $CRLFMessageEnd) { - throw "The CRLF message end check is only supported on TCP endpoints" + throw 'The CRLF message end check is only supported on TCP endpoints' } # new endpoint object $obj = @{ - Name = $Name - Description = $Description - Address = $null - RawAddress = $null - Port = $null - IsIPAddress = $true - HostName = $Hostname + Name = $Name + Description = $Description + Address = $null + RawAddress = $null + Port = $null + IsIPAddress = $true + HostName = $Hostname FriendlyName = $Hostname - Url = $null - Ssl = (@('https', 'wss', 'smtps', 'tcps') -icontains $Protocol) - Protocol = $Protocol.ToLowerInvariant() - Type = $type.ToLowerInvariant() - Runspace = @{ + Url = $null + Ssl = @{ + Enabled = (@('https', 'wss', 'smtps', 'tcps') -icontains $Protocol) + Protocols = $PodeContext.Server.Sockets.Ssl.Protocols + } + Protocol = $Protocol.ToLowerInvariant() + Type = $type.ToLowerInvariant() + Runspace = @{ PoolName = (Get-PodeEndpointRunspacePoolName -Protocol $Protocol) } - Default = $Default.IsPresent - Certificate = @{ - Raw = $X509Certificate - SelfSigned = $SelfSigned + Default = $Default.IsPresent + Certificate = @{ + Raw = $X509Certificate + SelfSigned = $SelfSigned AllowClientCertificate = $AllowClientCertificate - TlsMode = $TlsMode + TlsMode = $TlsMode } - Tcp = @{ - Acknowledge = $Acknowledge + Tcp = @{ + Acknowledge = $Acknowledge CRLFMessageEnd = $CRLFMessageEnd } } + # set ssl protocols + if (!(Test-PodeIsEmpty $SslProtocol)) { + $obj.Ssl.Protocols = (ConvertTo-PodeSslProtocols -Protocols $SslProtocol) + } + # set the ip for the context (force to localhost for IIS) $obj.Address = (Get-PodeIPAddress $_endpoint.Host) $obj.IsIPAddress = [string]::IsNullOrWhiteSpace($obj.HostName) @@ -999,18 +1007,17 @@ function Add-PodeEndpoint # has this endpoint been added before? (for http/https we can just not add it again) $exists = ($PodeContext.Server.Endpoints.Values | Where-Object { - ($_.FriendlyName -ieq $obj.FriendlyName) -and ($_.Port -eq $obj.Port) -and ($_.Ssl -eq $obj.Ssl) -and ($_.Type -ieq $obj.Type) - } | Measure-Object).Count + ($_.FriendlyName -ieq $obj.FriendlyName) -and ($_.Port -eq $obj.Port) -and ($_.Ssl.Enabled -eq $obj.Ssl.Enabled) -and ($_.Type -ieq $obj.Type) + } | Measure-Object).Count # if we're dealing with a certificate, attempt to import it if (!(Test-PodeIsHosted) -and ($PSCmdlet.ParameterSetName -ilike 'cert*')) { # fail if protocol is not https if (@('https', 'wss', 'smtps', 'tcps') -inotcontains $Protocol) { - throw "Certificate supplied for non-HTTPS/WSS endpoint" + throw 'Certificate supplied for non-HTTPS/WSS endpoint' } - switch ($PSCmdlet.ParameterSetName.ToLowerInvariant()) - { + switch ($PSCmdlet.ParameterSetName.ToLowerInvariant()) { 'certfile' { $obj.Certificate.Raw = Get-PodeCertificateByFile -Certificate $Certificate -Password $CertificatePassword -Key $CertificateKey } @@ -1103,8 +1110,7 @@ Get-PodeEndpoint -Protocol Http .EXAMPLE Get-PodeEndpoint -Name Admin, User #> -function Get-PodeEndpoint -{ +function Get-PodeEndpoint { [CmdletBinding()] param( [Parameter()] @@ -1147,23 +1153,23 @@ function Get-PodeEndpoint } $endpoints = @(foreach ($endpoint in $endpoints) { - if ($endpoint.Address.ToString() -ine $Address) { - continue - } + if ($endpoint.Address.ToString() -ine $Address) { + continue + } - $endpoint - }) + $endpoint + }) } # if we have a hostname, filter if (![string]::IsNullOrWhiteSpace($Hostname)) { $endpoints = @(foreach ($endpoint in $endpoints) { - if ($endpoint.Hostname.ToString() -ine $Hostname) { - continue - } + if ($endpoint.Hostname.ToString() -ine $Hostname) { + continue + } - $endpoint - }) + $endpoint + }) } # if we have a port, filter @@ -1177,12 +1183,12 @@ function Get-PodeEndpoint } $endpoints = @(foreach ($endpoint in $endpoints) { - if ($endpoint.Port -ne $Port) { - continue - } + if ($endpoint.Port -ne $Port) { + continue + } - $endpoint - }) + $endpoint + }) } # if we have a protocol, filter @@ -1192,25 +1198,25 @@ function Get-PodeEndpoint } $endpoints = @(foreach ($endpoint in $endpoints) { - if ($endpoint.Protocol -ine $Protocol) { - continue - } + if ($endpoint.Protocol -ine $Protocol) { + continue + } - $endpoint - }) + $endpoint + }) } # further filter by endpoint names if (($null -ne $Name) -and ($Name.Length -gt 0)) { $endpoints = @(foreach ($_name in $Name) { - foreach ($endpoint in $endpoints) { - if ($endpoint.Name -ine $_name) { - continue - } + foreach ($endpoint in $endpoints) { + if ($endpoint.Name -ine $_name) { + continue + } - $endpoint - } - }) + $endpoint + } + }) } # return diff --git a/src/Public/Events.ps1 b/src/Public/Events.ps1 index 07bc7de90..fe03becab 100644 --- a/src/Public/Events.ps1 +++ b/src/Public/Events.ps1 @@ -20,20 +20,19 @@ An array of arguments to supply to the ScriptBlock. .EXAMPLE Register-PodeEvent -Type Start -Name 'Event1' -ScriptBlock { } #> -function Register-PodeEvent -{ +function Register-PodeEvent { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] - [ValidateSet('Start', 'Terminate', 'Restart', 'Browser', 'Crash', 'Stop')] + [Parameter(Mandatory = $true)] + [ValidateSet('Start', 'Terminate', 'Restart', 'Browser', 'Crash', 'Stop', 'Running')] [string] $Type, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name, - [Parameter(Mandatory=$true, ValueFromPipeline=$true)] + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [scriptblock] $ScriptBlock, @@ -52,10 +51,10 @@ function Register-PodeEvent # add event $PodeContext.Server.Events[$Type][$Name] = @{ - Name = $Name - ScriptBlock = $ScriptBlock + Name = $Name + ScriptBlock = $ScriptBlock UsingVariables = $usingVars - Arguments = $ArgumentList + Arguments = $ArgumentList } } @@ -75,16 +74,15 @@ The Name of the event to unregister. .EXAMPLE Unregister-PodeEvent -Type Start -Name 'Event1' #> -function Unregister-PodeEvent -{ +function Unregister-PodeEvent { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] - [ValidateSet('Start', 'Terminate', 'Restart', 'Browser', 'Crash', 'Stop')] + [Parameter(Mandatory = $true)] + [ValidateSet('Start', 'Terminate', 'Restart', 'Browser', 'Crash', 'Stop', 'Running')] [string] $Type, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name ) @@ -114,16 +112,15 @@ The Name of the event to test. .EXAMPLE Test-PodeEvent -Type Start -Name 'Event1' #> -function Test-PodeEvent -{ +function Test-PodeEvent { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] - [ValidateSet('Start', 'Terminate', 'Restart', 'Browser', 'Crash', 'Stop')] + [Parameter(Mandatory = $true)] + [ValidateSet('Start', 'Terminate', 'Restart', 'Browser', 'Crash', 'Stop', 'Running')] [string] $Type, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name ) @@ -147,16 +144,15 @@ The Name of the event to retrieve. .EXAMPLE Get-PodeEvent -Type Start -Name 'Event1' #> -function Get-PodeEvent -{ +function Get-PodeEvent { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] - [ValidateSet('Start', 'Terminate', 'Restart', 'Browser', 'Crash', 'Stop')] + [Parameter(Mandatory = $true)] + [ValidateSet('Start', 'Terminate', 'Restart', 'Browser', 'Crash', 'Stop', 'Running')] [string] $Type, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name ) @@ -177,12 +173,11 @@ The Type of event to clear. .EXAMPLE Clear-PodeEvent -Type Start #> -function Clear-PodeEvent -{ +function Clear-PodeEvent { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] - [ValidateSet('Start', 'Terminate', 'Restart', 'Browser', 'Crash', 'Stop')] + [Parameter(Mandatory = $true)] + [ValidateSet('Start', 'Terminate', 'Restart', 'Browser', 'Crash', 'Stop', 'Running')] [string] $Type ) @@ -206,8 +201,7 @@ Use-PodeEvents .EXAMPLE Use-PodeEvents -Path './my-events' #> -function Use-PodeEvents -{ +function Use-PodeEvents { [CmdletBinding()] param( [Parameter()] diff --git a/src/Public/FileWatchers.ps1 b/src/Public/FileWatchers.ps1 index 6448849db..cce9fbd28 100644 --- a/src/Public/FileWatchers.ps1 +++ b/src/Public/FileWatchers.ps1 @@ -53,9 +53,8 @@ Add-PodeFileWatcher -Path '/temp/logs' -EventName Created -NotifyFilter Creation .EXAMPLE $watcher = Add-PodeFileWatcher -Path '/temp/logs' -Exclude *.txt -ScriptBlock {} -PassThru #> -function Add-PodeFileWatcher -{ - [CmdletBinding(DefaultParameterSetName='Script')] +function Add-PodeFileWatcher { + [CmdletBinding(DefaultParameterSetName = 'Script')] param( [Parameter()] [string] @@ -66,15 +65,15 @@ function Add-PodeFileWatcher [string[]] $EventName = @('Changed', 'Created', 'Deleted', 'Renamed'), - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Path, - [Parameter(Mandatory=$true, ParameterSetName='Script')] + [Parameter(Mandatory = $true, ParameterSetName = 'Script')] [scriptblock] $ScriptBlock, - [Parameter(Mandatory=$true, ParameterSetName='File')] + [Parameter(Mandatory = $true, ParameterSetName = 'File')] [string] $FilePath, @@ -165,22 +164,22 @@ function Add-PodeFileWatcher # add the file watcher $PodeContext.Fim.Items[$Name] = @{ - Name = $Name - Events = @($EventName) - Path = $Path - Placeholders = @{ - Path = $rgxPath + Name = $Name + Events = @($EventName) + Path = $Path + Placeholders = @{ + Path = $rgxPath Exist = $hasPlaceholders } - Script = $ScriptBlock - UsingVariables = $usingVars - Arguments = $ArgumentList - NotifyFilters = @($NotifyFilter) + Script = $ScriptBlock + UsingVariables = $usingVars + Arguments = $ArgumentList + NotifyFilters = @($NotifyFilter) IncludeSubdirectories = !$NoSubdirectories.IsPresent - InternalBufferSize = $InternalBufferSize - Exclude = $Exclude - Include = $Include - Paths = $paths + InternalBufferSize = $InternalBufferSize + Exclude = $Exclude + Include = $Include + Paths = $paths } # return? @@ -202,11 +201,10 @@ The Name of the File Watcher. .EXAMPLE if (Test-PodeFileWatcher -Name WatcherName) { } #> -function Test-PodeFileWatcher -{ +function Test-PodeFileWatcher { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name ) @@ -230,8 +228,7 @@ Get-PodeFileWatcher .EXAMPLE Get-PodeFileWatcher -Name Name1, Name2 #> -function Get-PodeFileWatcher -{ +function Get-PodeFileWatcher { [CmdletBinding()] param( [Parameter()] @@ -244,14 +241,14 @@ function Get-PodeFileWatcher # further filter by file watcher names if (($null -ne $Name) -and ($Name.Length -gt 0)) { $watchers = @(foreach ($_name in $Name) { - foreach ($watcher in $watchers) { - if ($watcher.Name -ine $_name) { - continue - } + foreach ($watcher in $watchers) { + if ($watcher.Name -ine $_name) { + continue + } - $watcher - } - }) + $watcher + } + }) } # return @@ -271,11 +268,10 @@ The Name of the File Watcher to be removed. .EXAMPLE Remove-PodeFileWatcher -Name 'Logs' #> -function Remove-PodeFileWatcher -{ +function Remove-PodeFileWatcher { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name ) @@ -293,8 +289,7 @@ Removes all File Watchers. .EXAMPLE Clear-PodeFileWatchers #> -function Clear-PodeFileWatchers -{ +function Clear-PodeFileWatchers { [CmdletBinding()] param() @@ -317,8 +312,7 @@ Use-PodeFileWatchers .EXAMPLE Use-PodeFileWatchers -Path './my-watchers' #> -function Use-PodeFileWatchers -{ +function Use-PodeFileWatchers { [CmdletBinding()] param( [Parameter()] diff --git a/src/Public/Flash.ps1 b/src/Public/Flash.ps1 index 805908861..b646c761e 100644 --- a/src/Public/Flash.ps1 +++ b/src/Public/Flash.ps1 @@ -15,15 +15,14 @@ The message to append. .EXAMPLE Add-PodeFlashMessage -Name 'error' -Message 'There was an error' #> -function Add-PodeFlashMessage -{ +function Add-PodeFlashMessage { [CmdletBinding()] - param ( - [Parameter(Mandatory=$true)] + param( + [Parameter(Mandatory = $true)] [string] $Name, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Message ) @@ -56,8 +55,7 @@ Clears all of the flash messages currently stored in the session. .EXAMPLE Clear-PodeFlashMessages #> -function Clear-PodeFlashMessages -{ +function Clear-PodeFlashMessages { [CmdletBinding()] param() @@ -86,12 +84,11 @@ The name of the flash messages to return. .EXAMPLE Get-PodeFlashMessage -Name 'error' #> -function Get-PodeFlashMessage -{ +function Get-PodeFlashMessage { [CmdletBinding()] [OutputType([string[]])] - param ( - [Parameter(Mandatory=$true)] + param( + [Parameter(Mandatory = $true)] [string] $Name ) @@ -126,8 +123,7 @@ Returns all of the names for each of the messages currently being stored. This d .EXAMPLE Get-PodeFlashMessageNames #> -function Get-PodeFlashMessageNames -{ +function Get-PodeFlashMessageNames { [CmdletBinding()] [OutputType([string[]])] param() @@ -158,11 +154,10 @@ The name of the flash messages to remove. .EXAMPLE Remove-PodeFlashMessage -Name 'error' #> -function Remove-PodeFlashMessage -{ +function Remove-PodeFlashMessage { [CmdletBinding()] - param ( - [Parameter(Mandatory=$true)] + param( + [Parameter(Mandatory = $true)] [string] $Name ) @@ -191,12 +186,11 @@ The name of the flash message to check. .EXAMPLE Test-PodeFlashMessage -Name 'error' #> -function Test-PodeFlashMessage -{ +function Test-PodeFlashMessage { [CmdletBinding()] [OutputType([bool])] - param ( - [Parameter(Mandatory=$true)] + param( + [Parameter(Mandatory = $true)] [string] $Name ) diff --git a/src/Public/Handlers.ps1 b/src/Public/Handlers.ps1 index f1a228ab4..eb418f2f0 100644 --- a/src/Public/Handlers.ps1 +++ b/src/Public/Handlers.ps1 @@ -29,24 +29,23 @@ Add-PodeHandler -Type Service -Name 'Looper' -ScriptBlock { /* logic */ } .EXAMPLE Add-PodeHandler -Type Smtp -Name 'Main' -ScriptBlock { /* logic */ } -ArgumentList 'arg1', 'arg2' #> -function Add-PodeHandler -{ - [CmdletBinding(DefaultParameterSetName='Script')] - param ( - [Parameter(Mandatory=$true)] +function Add-PodeHandler { + [CmdletBinding(DefaultParameterSetName = 'Script')] + param( + [Parameter(Mandatory = $true)] [ValidateSet('Service', 'Smtp')] [string] $Type, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name, - [Parameter(Mandatory=$true, ParameterSetName='Script')] + [Parameter(Mandatory = $true, ParameterSetName = 'Script')] [scriptblock] $ScriptBlock, - [Parameter(Mandatory=$true, ParameterSetName='File')] + [Parameter(Mandatory = $true, ParameterSetName = 'File')] [string] $FilePath, @@ -74,10 +73,10 @@ function Add-PodeHandler # add the handler Write-Verbose "Adding Handler: [$($Type)] $($Name)" $PodeContext.Server.Handlers[$Type][$Name] += @(@{ - Logic = $ScriptBlock - UsingVariables = $usingVars - Arguments = $ArgumentList - }) + Logic = $ScriptBlock + UsingVariables = $usingVars + Arguments = $ArgumentList + }) } <# @@ -96,16 +95,15 @@ The name of the Handler to be removed. .EXAMPLE Remove-PodeHandler -Type Smtp -Name 'Main' #> -function Remove-PodeHandler -{ +function Remove-PodeHandler { [CmdletBinding()] - param ( - [Parameter(Mandatory=$true)] + param( + [Parameter(Mandatory = $true)] [ValidateSet('Service', 'Smtp')] [string] $Type, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name ) @@ -132,10 +130,9 @@ The Type of Handlers to remove. .EXAMPLE Clear-PodeHandlers -Type Smtp #> -function Clear-PodeHandlers -{ +function Clear-PodeHandlers { [CmdletBinding()] - param ( + param( [Parameter()] [ValidateSet('', 'Service', 'Smtp')] [string] @@ -168,8 +165,7 @@ Use-PodeHandlers .EXAMPLE Use-PodeHandlers -Path './my-handlers' #> -function Use-PodeHandlers -{ +function Use-PodeHandlers { [CmdletBinding()] param( [Parameter()] diff --git a/src/Public/Headers.ps1 b/src/Public/Headers.ps1 index 76bbc6e04..223eeb8b9 100644 --- a/src/Public/Headers.ps1 +++ b/src/Public/Headers.ps1 @@ -17,15 +17,14 @@ If supplied, the secret with which to sign the header's value. .EXAMPLE Add-PodeHeader -Name 'X-AuthToken' -Value 'AA-BB-CC-33' #> -function Add-PodeHeader -{ +function Add-PodeHeader { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Value, @@ -64,11 +63,10 @@ If supplied, the secret with which to sign the header values. .EXAMPLE Add-PodeHeaderBulk -Values @{ Name1 = 'Value1'; Name2 = 'Value2' } #> -function Add-PodeHeaderBulk -{ +function Add-PodeHeaderBulk { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [hashtable] $Values, @@ -108,12 +106,11 @@ The name of the header to test. .EXAMPLE Test-PodeHeader -Name 'X-AuthToken' #> -function Test-PodeHeader -{ +function Test-PodeHeader { [CmdletBinding()] [OutputType([bool])] - param ( - [Parameter(Mandatory=$true)] + param( + [Parameter(Mandatory = $true)] [string] $Name ) @@ -138,12 +135,11 @@ The secret used to unsign the header's value. .EXAMPLE Get-PodeHeader -Name 'X-AuthToken' #> -function Get-PodeHeader -{ +function Get-PodeHeader { [CmdletBinding()] [OutputType([string])] - param ( - [Parameter(Mandatory=$true)] + param( + [Parameter(Mandatory = $true)] [string] $Name, @@ -182,15 +178,14 @@ If supplied, the secret with which to sign the header's value. .EXAMPLE Set-PodeHeader -Name 'X-AuthToken' -Value 'AA-BB-CC-33' #> -function Set-PodeHeader -{ +function Set-PodeHeader { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Value, @@ -229,11 +224,10 @@ If supplied, the secret with which to sign the header values. .EXAMPLE Set-PodeHeaderBulk -Values @{ Name1 = 'Value1'; Name2 = 'Value2' } #> -function Set-PodeHeaderBulk -{ +function Set-PodeHeaderBulk { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [hashtable] $Values, @@ -276,12 +270,11 @@ A secret to use for attempting to unsign the header's value. .EXAMPLE Test-PodeHeaderSigned -Name 'X-Header-Name' -Secret 'hunter2' #> -function Test-PodeHeaderSigned -{ +function Test-PodeHeaderSigned { [CmdletBinding()] [OutputType([bool])] - param ( - [Parameter(Mandatory=$true)] + param( + [Parameter(Mandatory = $true)] [string] $Name, diff --git a/src/Public/Logging.ps1 b/src/Public/Logging.ps1 index 820780033..801002f7b 100644 --- a/src/Public/Logging.ps1 +++ b/src/Public/Logging.ps1 @@ -59,40 +59,39 @@ $file_logging = New-PodeLoggingMethod -File -Path ./logs -Name 'requests' .EXAMPLE $custom_logging = New-PodeLoggingMethod -Custom -ScriptBlock { /* logic */ } #> -function New-PodeLoggingMethod -{ - [CmdletBinding(DefaultParameterSetName='Terminal')] +function New-PodeLoggingMethod { + [CmdletBinding(DefaultParameterSetName = 'Terminal')] [OutputType([hashtable])] - param ( - [Parameter(ParameterSetName='Terminal')] + param( + [Parameter(ParameterSetName = 'Terminal')] [switch] $Terminal, - [Parameter(ParameterSetName='File')] + [Parameter(ParameterSetName = 'File')] [switch] $File, - [Parameter(ParameterSetName='File')] + [Parameter(ParameterSetName = 'File')] [string] $Path = './logs', - [Parameter(Mandatory=$true, ParameterSetName='File')] + [Parameter(Mandatory = $true, ParameterSetName = 'File')] [string] $Name, - [Parameter(ParameterSetName='EventViewer')] + [Parameter(ParameterSetName = 'EventViewer')] [switch] $EventViewer, - [Parameter(ParameterSetName='EventViewer')] + [Parameter(ParameterSetName = 'EventViewer')] [string] $EventLogName = 'Application', - [Parameter(ParameterSetName='EventViewer')] + [Parameter(ParameterSetName = 'EventViewer')] [string] $Source = 'Pode', - [Parameter(ParameterSetName='EventViewer')] + [Parameter(ParameterSetName = 'EventViewer')] [int] $EventID = 0, @@ -104,55 +103,55 @@ function New-PodeLoggingMethod [int] $BatchTimeout = 0, - [Parameter(ParameterSetName='File')] + [Parameter(ParameterSetName = 'File')] [ValidateScript({ - if ($_ -lt 0) { - throw "MaxDays must be 0 or greater, but got: $($_)s" - } + if ($_ -lt 0) { + throw "MaxDays must be 0 or greater, but got: $($_)s" + } - return $true - })] + return $true + })] [int] $MaxDays = 0, - [Parameter(ParameterSetName='File')] + [Parameter(ParameterSetName = 'File')] [ValidateScript({ - if ($_ -lt 0) { - throw "MaxSize must be 0 or greater, but got: $($_)s" - } + if ($_ -lt 0) { + throw "MaxSize must be 0 or greater, but got: $($_)s" + } - return $true - })] + return $true + })] [int] $MaxSize = 0, - [Parameter(ParameterSetName='Custom')] + [Parameter(ParameterSetName = 'Custom')] [switch] $Custom, - [Parameter(Mandatory=$true, ParameterSetName='Custom')] + [Parameter(Mandatory = $true, ParameterSetName = 'Custom')] [ValidateScript({ - if (Test-PodeIsEmpty $_) { - throw "A non-empty ScriptBlock is required for the Custom logging output method" - } + if (Test-PodeIsEmpty $_) { + throw 'A non-empty ScriptBlock is required for the Custom logging output method' + } - return $true - })] + return $true + })] [scriptblock] $ScriptBlock, - [Parameter(ParameterSetName='Custom')] + [Parameter(ParameterSetName = 'Custom')] [object[]] $ArgumentList ) # batch details $batchInfo = @{ - Size = $Batch - Timeout = $BatchTimeout + Size = $Batch + Timeout = $BatchTimeout LastUpdate = $null - Items = @() - RawItems = @() + Items = @() + RawItems = @() } # return info on appropriate logging type @@ -160,8 +159,8 @@ function New-PodeLoggingMethod 'terminal' { return @{ ScriptBlock = (Get-PodeLoggingTerminalMethod) - Batch = $batchInfo - Arguments = @{} + Batch = $batchInfo + Arguments = @{} } } @@ -172,14 +171,14 @@ function New-PodeLoggingMethod return @{ ScriptBlock = (Get-PodeLoggingFileMethod) - Batch = $batchInfo - Arguments = @{ - Name = $Name - Path = $Path - MaxDays = $MaxDays - MaxSize = $MaxSize - FileId = 0 - Date = $null + Batch = $batchInfo + Arguments = @{ + Name = $Name + Path = $Path + MaxDays = $MaxDays + MaxSize = $MaxSize + FileId = 0 + Date = $null NextClearDown = [datetime]::Now.Date } } @@ -188,7 +187,7 @@ function New-PodeLoggingMethod 'eventviewer' { # only windows if (!(Test-PodeIsWindows)) { - throw "Event Viewer logging only supported on Windows" + throw 'Event Viewer logging only supported on Windows' } # create source @@ -198,11 +197,11 @@ function New-PodeLoggingMethod return @{ ScriptBlock = (Get-PodeLoggingEventViewerMethod) - Batch = $batchInfo - Arguments = @{ + Batch = $batchInfo + Arguments = @{ LogName = $EventLogName - Source = $Source - ID = $EventID + Source = $Source + ID = $EventID } } } @@ -211,10 +210,10 @@ function New-PodeLoggingMethod $ScriptBlock, $usingVars = Convert-PodeScopedVariables -ScriptBlock $ScriptBlock -PSSession $PSCmdlet.SessionState return @{ - ScriptBlock = $ScriptBlock + ScriptBlock = $ScriptBlock UsingVariables = $usingVars - Batch = $batchInfo - Arguments = $ArgumentList + Batch = $batchInfo + Arguments = $ArgumentList } } } @@ -231,20 +230,26 @@ Enables Request Logging using a supplied output method. .PARAMETER Method The Method to use for output the log entry (From New-PodeLoggingMethod). +.PARAMETER UsernameProperty +An optional property path within the $WebEvent.Auth.User object for the user's Username. (Default: Username). + .PARAMETER Raw If supplied, the log item returned will be the raw Request item as a hashtable and not a string (for Custom methods). .EXAMPLE New-PodeLoggingMethod -Terminal | Enable-PodeRequestLogging #> -function Enable-PodeRequestLogging -{ +function Enable-PodeRequestLogging { [CmdletBinding()] - param ( - [Parameter(Mandatory=$true, ValueFromPipeline=$true)] + param( + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [hashtable] $Method, + [Parameter()] + [string] + $UsernameProperty, + [switch] $Raw ) @@ -260,14 +265,22 @@ function Enable-PodeRequestLogging # ensure the Method contains a scriptblock if (Test-PodeIsEmpty $Method.ScriptBlock) { - throw "The supplied output Method for Request Logging requires a valid ScriptBlock" + throw 'The supplied output Method for Request Logging requires a valid ScriptBlock' + } + + # username property + if ([string]::IsNullOrWhiteSpace($UsernameProperty)) { + $UsernameProperty = 'Username' } # add the request logger $PodeContext.Server.Logging.Types[$name] = @{ - Method = $Method + Method = $Method ScriptBlock = (Get-PodeLoggingInbuiltType -Type Requests) - Arguments = @{ + Properties = @{ + Username = $UsernameProperty + } + Arguments = @{ Raw = $Raw } } @@ -283,8 +296,7 @@ Disables Request Logging. .EXAMPLE Disable-PodeRequestLogging #> -function Disable-PodeRequestLogging -{ +function Disable-PodeRequestLogging { [CmdletBinding()] param() @@ -310,11 +322,10 @@ If supplied, the log item returned will be the raw Error item as a hashtable and .EXAMPLE New-PodeLoggingMethod -Terminal | Enable-PodeErrorLogging #> -function Enable-PodeErrorLogging -{ +function Enable-PodeErrorLogging { [CmdletBinding()] - param ( - [Parameter(Mandatory=$true, ValueFromPipeline=$true)] + param( + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [hashtable] $Method, @@ -337,7 +348,7 @@ function Enable-PodeErrorLogging # ensure the Method contains a scriptblock if (Test-PodeIsEmpty $Method.ScriptBlock) { - throw "The supplied output Method for Error Logging requires a valid ScriptBlock" + throw 'The supplied output Method for Error Logging requires a valid ScriptBlock' } # all errors? @@ -347,10 +358,10 @@ function Enable-PodeErrorLogging # add the error logger $PodeContext.Server.Logging.Types[$name] = @{ - Method = $Method + Method = $Method ScriptBlock = (Get-PodeLoggingInbuiltType -Type Errors) - Arguments = @{ - Raw = $Raw + Arguments = @{ + Raw = $Raw Levels = $Levels } } @@ -366,8 +377,7 @@ Disables Error Logging. .EXAMPLE Disable-PodeErrorLogging #> -function Disable-PodeErrorLogging -{ +function Disable-PodeErrorLogging { [CmdletBinding()] param() @@ -396,26 +406,25 @@ An array of arguments to supply to the Custom Logger's ScriptBlock. .EXAMPLE New-PodeLoggingMethod -Terminal | Add-PodeLogger -Name 'Main' -ScriptBlock { /* logic */ } #> -function Add-PodeLogger -{ +function Add-PodeLogger { [CmdletBinding()] - param ( - [Parameter(Mandatory=$true)] + param( + [Parameter(Mandatory = $true)] [string] $Name, - [Parameter(Mandatory=$true, ValueFromPipeline=$true)] + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [hashtable] $Method, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [ValidateScript({ - if (Test-PodeIsEmpty $_) { - throw "A non-empty ScriptBlock is required for the logging method" - } + if (Test-PodeIsEmpty $_) { + throw 'A non-empty ScriptBlock is required for the logging method' + } - return $true - })] + return $true + })] [scriptblock] $ScriptBlock, @@ -439,10 +448,10 @@ function Add-PodeLogger # add logging method to server $PodeContext.Server.Logging.Types[$Name] = @{ - Method = $Method - ScriptBlock = $ScriptBlock + Method = $Method + ScriptBlock = $ScriptBlock UsingVariables = $usingVars - Arguments = $ArgumentList + Arguments = $ArgumentList } } @@ -459,11 +468,10 @@ The Name of the Logging method. .EXAMPLE Remove-PodeLogger -Name 'LogName' #> -function Remove-PodeLogger -{ +function Remove-PodeLogger { [CmdletBinding()] - param ( - [Parameter(Mandatory=$true, ValueFromPipeline=$true)] + param( + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [string] $Name ) @@ -481,8 +489,7 @@ Clears all Logging methods that have been configured. .EXAMPLE Clear-PodeLoggers #> -function Clear-PodeLoggers -{ +function Clear-PodeLoggers { [CmdletBinding()] param() @@ -514,15 +521,14 @@ try { /* logic */ } catch { $_ | Write-PodeErrorLog } .EXAMPLE [System.Exception]::new('error message') | Write-PodeErrorLog #> -function Write-PodeErrorLog -{ +function Write-PodeErrorLog { [CmdletBinding()] - param ( - [Parameter(Mandatory=$true, ValueFromPipeline=$true, ParameterSetName='Exception')] + param( + [Parameter(Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = 'Exception')] [System.Exception] $Exception, - [Parameter(Mandatory=$true, ValueFromPipeline=$true, ParameterSetName='Error')] + [Parameter(Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = 'Error')] [System.Management.Automation.ErrorRecord] $ErrorRecord, @@ -532,7 +538,7 @@ function Write-PodeErrorLog [string] $Level = 'Error', - [Parameter(ParameterSetName='Exception')] + [Parameter(ParameterSetName = 'Exception')] [switch] $CheckInnerException ) @@ -553,16 +559,16 @@ function Write-PodeErrorLog switch ($PSCmdlet.ParameterSetName.ToLowerInvariant()) { 'exception' { $item = @{ - Category = $Exception.Source - Message = $Exception.Message + Category = $Exception.Source + Message = $Exception.Message StackTrace = $Exception.StackTrace } } 'error' { $item = @{ - Category = $ErrorRecord.CategoryInfo.ToString() - Message = $ErrorRecord.Exception.Message + Category = $ErrorRecord.CategoryInfo.ToString() + Message = $ErrorRecord.Exception.Message StackTrace = $ErrorRecord.ScriptStackTrace } } @@ -576,9 +582,9 @@ function Write-PodeErrorLog # add the item to be processed $null = $PodeContext.LogsToProcess.Add(@{ - Name = $name - Item = $item - }) + Name = $name + Item = $item + }) # for exceptions, check the inner exception if ($CheckInnerException -and ($null -ne $Exception.InnerException) -and ![string]::IsNullOrWhiteSpace($Exception.InnerException.Message)) { @@ -602,15 +608,14 @@ The Object to write. .EXAMPLE $object | Write-PodeLog -Name 'LogName' #> -function Write-PodeLog -{ +function Write-PodeLog { [CmdletBinding()] - param ( - [Parameter(Mandatory=$true)] + param( + [Parameter(Mandatory = $true)] [string] $Name, - [Parameter(Mandatory=$true, ValueFromPipeline=$true)] + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [object] $InputObject ) @@ -622,9 +627,9 @@ function Write-PodeLog # add the item to be processed $null = $PodeContext.LogsToProcess.Add(@{ - Name = $Name - Item = $InputObject - }) + Name = $Name + Item = $InputObject + }) } <# @@ -641,11 +646,10 @@ The string Item to mask values. .EXAMPLE $value = Protect-PodeLogItem -Item 'Username=Morty, Password=Hunter2' #> -function Protect-PodeLogItem -{ +function Protect-PodeLogItem { [CmdletBinding()] param( - [Parameter(ValueFromPipeline=$true)] + [Parameter(ValueFromPipeline = $true)] [string] $Item ) @@ -699,8 +703,7 @@ Use-PodeLogging .EXAMPLE Use-PodeLogging -Path './my-logging' #> -function Use-PodeLogging -{ +function Use-PodeLogging { [CmdletBinding()] param( [Parameter()] diff --git a/src/Public/Metrics.ps1 b/src/Public/Metrics.ps1 index 5b5b2acd0..f30787caf 100644 --- a/src/Public/Metrics.ps1 +++ b/src/Public/Metrics.ps1 @@ -14,8 +14,7 @@ $currentUptime = Get-PodeServerUptime .EXAMPLE $totalUptime = Get-PodeServerUptime -Total #> -function Get-PodeServerUptime -{ +function Get-PodeServerUptime { [CmdletBinding()] param( [switch] @@ -40,8 +39,7 @@ Returns the number of times the server has restarted. .EXAMPLE $restarts = Get-PodeServerRestartCount #> -function Get-PodeServerRestartCount -{ +function Get-PodeServerRestartCount { [CmdletBinding()] param() @@ -70,15 +68,14 @@ $statusReqs = Get-PodeServerRequestMetric .EXAMPLE $404Reqs = Get-PodeServerRequestMetric -StatusCode 404 #> -function Get-PodeServerRequestMetric -{ - [CmdletBinding(DefaultParameterSetName='StatusCode')] +function Get-PodeServerRequestMetric { + [CmdletBinding(DefaultParameterSetName = 'StatusCode')] param( - [Parameter(ParameterSetName='StatusCode')] + [Parameter(ParameterSetName = 'StatusCode')] [int] $StatusCode = 0, - [Parameter(ParameterSetName='Total')] + [Parameter(ParameterSetName = 'Total')] [switch] $Total ) @@ -109,8 +106,7 @@ Returns the total number of Signal requests the Server has receieved. .EXAMPLE $totalReqs = Get-PodeServerSignalMetric #> -function Get-PodeServerSignalMetric -{ +function Get-PodeServerSignalMetric { [CmdletBinding()] param() @@ -133,8 +129,7 @@ Get-PodeServerActiveRequestMetric .EXAMPLE Get-PodeServerActiveRequestMetric -CountType Queued #> -function Get-PodeServerActiveRequestMetric -{ +function Get-PodeServerActiveRequestMetric { [CmdletBinding()] param( [Parameter()] @@ -177,8 +172,7 @@ Get-PodeServerActiveSignalMetric .EXAMPLE Get-PodeServerActiveSignalMetric -Type Client -CountType Queued #> -function Get-PodeServerActiveSignalMetric -{ +function Get-PodeServerActiveSignalMetric { [CmdletBinding()] param( [Parameter()] diff --git a/src/Public/Middleware.ps1 b/src/Public/Middleware.ps1 index b7a2b2e0e..9fb4c4809 100644 --- a/src/Public/Middleware.ps1 +++ b/src/Public/Middleware.ps1 @@ -20,21 +20,20 @@ Add-PodeAccessRule -Access Allow -Type IP -Values '127.0.0.1' .EXAMPLE Add-PodeAccessRule -Access Deny -Type IP -Values @('192.168.1.1', '10.10.1.0/24') #> -function Add-PodeAccessRule -{ +function Add-PodeAccessRule { [CmdletBinding()] - param ( - [Parameter(Mandatory=$true)] + param( + [Parameter(Mandatory = $true)] [ValidateSet('Allow', 'Deny')] [string] $Access, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [ValidateSet('IP')] [string] $Type, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string[]] $Values ) @@ -43,8 +42,7 @@ function Add-PodeAccessRule Test-PodeIsServerless -FunctionName 'Add-PodeAccessRule' -ThrowError # call the appropriate access method - switch ($Type.ToLowerInvariant()) - { + switch ($Type.ToLowerInvariant()) { 'ip' { foreach ($ip in $Values) { Add-PodeIPAccess -Access $Access -IP $ip @@ -84,24 +82,23 @@ Add-PodeLimitRule -Type IP -Values @('192.168.1.1', '10.10.1.0/24') -Limit 50 -S .EXAMPLE Add-PodeLimitRule -Type Route -Values '/downloads' -Limit 5 -Seconds 1 #> -function Add-PodeLimitRule -{ +function Add-PodeLimitRule { [CmdletBinding()] - param ( - [Parameter(Mandatory=$true)] + param( + [Parameter(Mandatory = $true)] [ValidateSet('IP', 'Route', 'Endpoint')] [string] $Type, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string[]] $Values, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [int] $Limit, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [int] $Seconds, @@ -110,10 +107,8 @@ function Add-PodeLimitRule ) # call the appropriate limit method - foreach ($value in $Values) - { - switch ($Type.ToLowerInvariant()) - { + foreach ($value in $Values) { + switch ($Type.ToLowerInvariant()) { 'ip' { Test-PodeIsServerless -FunctionName 'Add-PodeLimitRule' -ThrowError Add-PodeIPLimit -IP $value -Limit $Limit -Seconds $Seconds -Group:$Group @@ -140,8 +135,7 @@ Creates and returns a new secure token for use with CSRF. .EXAMPLE $token = New-PodeCsrfToken #> -function New-PodeCsrfToken -{ +function New-PodeCsrfToken { [CmdletBinding()] [OutputType([string])] param() @@ -170,8 +164,7 @@ Returns adhoc CSRF CSRF verification Middleware, for use on Routes. $csrf = Get-PodeCsrfMiddleware Add-PodeRoute -Method Get -Path '/cpu' -Middleware $csrf -ScriptBlock { /* logic */ } #> -function Get-PodeCsrfMiddleware -{ +function Get-PodeCsrfMiddleware { [CmdletBinding()] [OutputType([hashtable])] param() @@ -189,7 +182,7 @@ function Get-PodeCsrfMiddleware # verify the token on the request, if invalid, throw a 403 $token = Get-PodeCsrfToken - if (!(Test-PodeCsrfToken -Secret $secret -Token $token)){ + if (!(Test-PodeCsrfToken -Secret $secret -Token $token)) { Set-PodeResponseStatus -Code 403 -Description 'Invalid CSRF Token' return $false } @@ -223,10 +216,9 @@ Initialize-PodeCsrf -IgnoreMethods @('Get', 'Trace') .EXAMPLE Initialize-PodeCsrf -Secret 'some-secret' -UseCookies #> -function Initialize-PodeCsrf -{ +function Initialize-PodeCsrf { [CmdletBinding()] - param ( + param( [Parameter()] [ValidateSet('Connect', 'Delete', 'Get', 'Head', 'Merge', 'Options', 'Patch', 'Post', 'Put', 'Trace')] [string[]] @@ -261,9 +253,9 @@ function Initialize-PodeCsrf # set the options against the server context $PodeContext.Server.Cookies.Csrf = @{ - Name = 'pode.csrf' - UseCookies = $UseCookies - Secret = $Secret + Name = 'pode.csrf' + UseCookies = $UseCookies + Secret = $Secret IgnoredMethods = $IgnoreMethods } } @@ -290,20 +282,19 @@ Enable-PodeCsrfMiddleware -IgnoreMethods @('Get', 'Trace') .EXAMPLE Enable-PodeCsrfMiddleware -Secret 'some-secret' -UseCookies #> -function Enable-PodeCsrfMiddleware -{ +function Enable-PodeCsrfMiddleware { [CmdletBinding()] - param ( + param( [Parameter()] [ValidateSet('Connect', 'Delete', 'Get', 'Head', 'Merge', 'Options', 'Patch', 'Post', 'Put', 'Trace')] [string[]] $IgnoreMethods = @('Get', 'Head', 'Options', 'Trace'), - [Parameter(ParameterSetName='Cookies')] + [Parameter(ParameterSetName = 'Cookies')] [string] $Secret, - [Parameter(ParameterSetName='Cookies')] + [Parameter(ParameterSetName = 'Cookies')] [switch] $UseCookies ) @@ -324,7 +315,7 @@ function Enable-PodeCsrfMiddleware # verify the token on the request, if invalid, throw a 403 $token = Get-PodeCsrfToken - if (!(Test-PodeCsrfToken -Secret $secret -Token $token)){ + if (!(Test-PodeCsrfToken -Secret $secret -Token $token)) { Set-PodeResponseStatus -Code 403 -Description 'Invalid CSRF Token' return $false } @@ -352,16 +343,15 @@ The ScriptBlock that will parse the body content, and return the result. .EXAMPLE Add-PodeBodyParser -ContentType 'application/json' -ScriptBlock { param($body) /* parsing logic */ } #> -function Add-PodeBodyParser -{ +function Add-PodeBodyParser { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [ValidatePattern('^\w+\/[\w\.\+-]+$')] [string] $ContentType, - [Parameter(Mandatory=$true, ValueFromPipeline=$true)] + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [scriptblock] $ScriptBlock ) @@ -375,7 +365,7 @@ function Add-PodeBodyParser $ScriptBlock, $usingVars = Convert-PodeScopedVariables -ScriptBlock $ScriptBlock -PSSession $PSCmdlet.SessionState $PodeContext.Server.BodyParsers[$ContentType] = @{ - ScriptBlock = $ScriptBlock + ScriptBlock = $ScriptBlock UsingVariables = $usingVars } } @@ -393,11 +383,10 @@ The ContentType of the custom body parser. .EXAMPLE Remove-PodeBodyParser -ContentType 'application/json' #> -function Remove-PodeBodyParser -{ +function Remove-PodeBodyParser { [CmdletBinding()] param( - [Parameter(Mandatory=$true, ValueFromPipeline=$true)] + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [ValidatePattern('^\w+\/[\w\.\+-]+$')] [string] $ContentType @@ -442,19 +431,18 @@ Add-PodeMiddleware -Name 'BlockAgents' -ScriptBlock { /* logic */ } .EXAMPLE Add-PodeMiddleware -Name 'CheckEmailOnApi' -Route '/api/*' -ScriptBlock { /* logic */ } #> -function Add-PodeMiddleware -{ - [CmdletBinding(DefaultParameterSetName='Script')] +function Add-PodeMiddleware { + [CmdletBinding(DefaultParameterSetName = 'Script')] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name, - [Parameter(Mandatory=$true, ParameterSetName='Script')] + [Parameter(Mandatory = $true, ParameterSetName = 'Script')] [scriptblock] $ScriptBlock, - [Parameter(Mandatory=$true, ParameterSetName='Input', ValueFromPipeline=$true)] + [Parameter(Mandatory = $true, ParameterSetName = 'Input', ValueFromPipeline = $true)] [hashtable] $InputObject, @@ -475,10 +463,10 @@ function Add-PodeMiddleware # if it's a script - call New-PodeMiddleware if ($PSCmdlet.ParameterSetName -ieq 'script') { $InputObject = (New-PodeMiddlewareInternal ` - -ScriptBlock $ScriptBlock ` - -Route $Route ` - -ArgumentList $ArgumentList ` - -PSSession $PSCmdlet.SessionState) + -ScriptBlock $ScriptBlock ` + -Route $Route ` + -ArgumentList $ArgumentList ` + -PSSession $PSCmdlet.SessionState) } else { $Route = ConvertTo-PodeRouteRegex -Path $Route @@ -488,7 +476,7 @@ function Add-PodeMiddleware # ensure we have a script to run if (Test-PodeIsEmpty $InputObject.Logic) { - throw "[Middleware]: No logic supplied in ScriptBlock" + throw '[Middleware]: No logic supplied in ScriptBlock' } # set name, and override route/args @@ -520,12 +508,11 @@ Boolean. ScriptBlock should return $true to continue to the next middleware/rout .EXAMPLE New-PodeMiddleware -ScriptBlock { /* logic */ } -ArgumentList 'Email' | Add-PodeMiddleware -Name 'CheckEmail' #> -function New-PodeMiddleware -{ +function New-PodeMiddleware { [CmdletBinding()] [OutputType([hashtable])] param( - [Parameter(Mandatory=$true, ValueFromPipeline=$true)] + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [scriptblock] $ScriptBlock, @@ -538,11 +525,11 @@ function New-PodeMiddleware $ArgumentList ) - return (New-PodeMiddlewareInternal ` + return New-PodeMiddlewareInternal ` -ScriptBlock $ScriptBlock ` -Route $Route ` -ArgumentList $ArgumentList ` - -PSSession $PSCmdlet.SessionState) + -PSSession $PSCmdlet.SessionState } <# @@ -558,11 +545,10 @@ The Name of the Middleware to be removed. .EXAMPLE Remove-PodeMiddleware -Name 'Sessions' #> -function Remove-PodeMiddleware -{ +function Remove-PodeMiddleware { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name ) @@ -580,8 +566,7 @@ Removes all user defined Middleware. .EXAMPLE Clear-PodeMiddleware #> -function Clear-PodeMiddleware -{ +function Clear-PodeMiddleware { [CmdletBinding()] param() @@ -604,8 +589,7 @@ Use-PodeMiddleware .EXAMPLE Use-PodeMiddleware -Path './my-middleware' #> -function Use-PodeMiddleware -{ +function Use-PodeMiddleware { [CmdletBinding()] param( [Parameter()] diff --git a/src/Public/OpenApi.ps1 b/src/Public/OpenApi.ps1 index d8294dc43..32bc93256 100644 --- a/src/Public/OpenApi.ps1 +++ b/src/Public/OpenApi.ps1 @@ -35,8 +35,7 @@ Enable-PodeOpenApi -Title 'My API' -Version '1.0.0' -RouteFilter '/api/*' -Restr .EXAMPLE Enable-PodeOpenApi -Path '/docs/openapi' -Title 'My API' -Version '1.0.0' #> -function Enable-PodeOpenApi -{ +function Enable-PodeOpenApi { [CmdletBinding()] param( [Parameter()] @@ -44,7 +43,7 @@ function Enable-PodeOpenApi [string] $Path = '/openapi', - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Title, @@ -75,9 +74,9 @@ function Enable-PodeOpenApi $PodeContext.Server.OpenAPI.Path = $Path $meta = @{ - Version = $Version - Description = $Description - RouteFilter = $RouteFilter + Version = $Version + Description = $Description + RouteFilter = $RouteFilter RestrictRoutes = $RestrictRoutes } @@ -127,8 +126,7 @@ If supplied, only routes that are available on the Requests URI will be used to .EXAMPLE $def = Get-PodeOpenApiDefinition -RouteFilter '/api/*' #> -function Get-PodeOpenApiDefinition -{ +function Get-PodeOpenApiDefinition { [CmdletBinding()] param( [Parameter()] @@ -154,18 +152,18 @@ function Get-PodeOpenApiDefinition $Title = Protect-PodeValue -Value $Title -Default $PodeContext.Server.OpenAPI.Title if ([string]::IsNullOrWhiteSpace($Title)) { - throw "No Title supplied for OpenAPI definition" + throw 'No Title supplied for OpenAPI definition' } $Version = Protect-PodeValue -Value $Version -Default $PodeContext.Server.OpenAPI.Version if ([string]::IsNullOrWhiteSpace($Version)) { - throw "No Version supplied for OpenAPI definition" + throw 'No Version supplied for OpenAPI definition' } $Description = Protect-PodeValue -Value $Description -Default $PodeContext.Server.OpenAPI.Description # generate the openapi definition - return (Get-PodeOpenApiDefinitionInternal ` + return Get-PodeOpenApiDefinitionInternal ` -Title $Title ` -Version $Version ` -Description $Description ` @@ -173,7 +171,7 @@ function Get-PodeOpenApiDefinition -Protocol $WebEvent.Endpoint.Protocol ` -Address $WebEvent.Endpoint.Address ` -EndpointName $WebEvent.Endpoint.Name ` - -RestrictRoutes:$RestrictRoutes) + -RestrictRoutes:$RestrictRoutes } <# @@ -216,32 +214,31 @@ Add-PodeRoute -PassThru | Add-PodeOAResponse -StatusCode 200 -ContentSchemas @{ .EXAMPLE Add-PodeRoute -PassThru | Add-PodeOAResponse -StatusCode 200 -Reference 'OKResponse' #> -function Add-PodeOAResponse -{ - [CmdletBinding(DefaultParameterSetName='Schema')] +function Add-PodeOAResponse { + [CmdletBinding(DefaultParameterSetName = 'Schema')] param( - [Parameter(Mandatory=$true, ValueFromPipeline=$true)] + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [ValidateNotNullOrEmpty()] [hashtable[]] $Route, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [int] $StatusCode, - [Parameter(ParameterSetName='Schema')] + [Parameter(ParameterSetName = 'Schema')] [hashtable] $ContentSchemas, - [Parameter(ParameterSetName='Schema')] + [Parameter(ParameterSetName = 'Schema')] [hashtable] $HeaderSchemas, - [Parameter(ParameterSetName='Schema')] + [Parameter(ParameterSetName = 'Schema')] [string] $Description = $null, - [Parameter(Mandatory=$true, ParameterSetName='Reference')] + [Parameter(Mandatory = $true, ParameterSetName = 'Reference')] [string] $Reference, @@ -292,8 +289,8 @@ function Add-PodeOAResponse 'schema' { $r.OpenApi.Responses[$code] = @{ description = $Description - content = $content - headers = $headers + content = $content + headers = $headers } } @@ -335,16 +332,15 @@ Add-PodeRoute -PassThru | Remove-PodeOAResponse -StatusCode 200 .EXAMPLE Add-PodeRoute -PassThru | Remove-PodeOAResponse -StatusCode 201 -Default #> -function Remove-PodeOAResponse -{ +function Remove-PodeOAResponse { [CmdletBinding()] param( - [Parameter(Mandatory=$true, ValueFromPipeline=$true)] + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [ValidateNotNullOrEmpty()] [hashtable[]] $Route, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [int] $StatusCode, @@ -398,11 +394,10 @@ Add-PodeOAComponentResponse -Name 'OKResponse' -ContentSchemas @{ 'application/j .EXAMPLE Add-PodeOAComponentResponse -Name 'ErrorResponse' -ContentSchemas @{ 'application/json' = 'ErrorSchema' } #> -function Add-PodeOAComponentResponse -{ +function Add-PodeOAComponentResponse { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name, @@ -414,7 +409,7 @@ function Add-PodeOAComponentResponse [hashtable] $HeaderSchemas, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Description ) @@ -431,8 +426,8 @@ function Add-PodeOAComponentResponse $PodeContext.Server.OpenAPI.components.responses[$Name] = @{ description = $Description - content = $content - headers = $headers + content = $content + headers = $headers } } @@ -458,11 +453,10 @@ If supplied, the route passed in will be returned for further chaining. .EXAMPLE Add-PodeRoute -PassThru | Set-PodeOARequest -RequestBody (New-PodeOARequestBody -Reference 'UserIdBody') #> -function Set-PodeOARequest -{ +function Set-PodeOARequest { [CmdletBinding()] param( - [Parameter(Mandatory=$true, ValueFromPipeline=$true)] + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [ValidateNotNullOrEmpty()] [hashtable[]] $Route, @@ -522,23 +516,22 @@ New-PodeOARequestBody -ContentSchemas @{ 'application/json' = 'UserIdSchema' } .EXAMPLE New-PodeOARequestBody -Reference 'UserIdBody' #> -function New-PodeOARequestBody -{ - [CmdletBinding(DefaultParameterSetName='Schema')] +function New-PodeOARequestBody { + [CmdletBinding(DefaultParameterSetName = 'Schema')] param( - [Parameter(Mandatory=$true, ParameterSetName='Reference')] + [Parameter(Mandatory = $true, ParameterSetName = 'Reference')] [string] $Reference, - [Parameter(Mandatory=$true, ParameterSetName='Schema')] + [Parameter(Mandatory = $true, ParameterSetName = 'Schema')] [hashtable] $ContentSchemas, - [Parameter(ParameterSetName='Schema')] + [Parameter(ParameterSetName = 'Schema')] [string] $Description = $null, - [Parameter(ParameterSetName='Schema')] + [Parameter(ParameterSetName = 'Schema')] [switch] $Required ) @@ -546,9 +539,9 @@ function New-PodeOARequestBody switch ($PSCmdlet.ParameterSetName.ToLowerInvariant()) { 'schema' { return @{ - required = $Required.IsPresent + required = $Required.IsPresent description = $Description - content = ($ContentSchemas | ConvertTo-PodeOAContentTypeSchema) + content = ($ContentSchemas | ConvertTo-PodeOAContentTypeSchema) } } @@ -580,15 +573,14 @@ The Schema definition (the schema is created using the Property functions). .EXAMPLE Add-PodeOAComponentSchema -Name 'UserIdSchema' -Schema (New-PodeOAIntProperty -Name 'userId' -Object) #> -function Add-PodeOAComponentSchema -{ +function Add-PodeOAComponentSchema { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name, - [Parameter(Mandatory=$true, ValueFromPipeline=$true)] + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [hashtable] $Schema ) @@ -621,15 +613,14 @@ Add-PodeOAComponentRequestBody -Name 'UserIdBody' -ContentSchemas @{ 'applicatio .EXAMPLE Add-PodeOAComponentRequestBody -Name 'UserIdBody' -ContentSchemas @{ 'application/json' = 'UserIdSchema' } #> -function Add-PodeOAComponentRequestBody -{ +function Add-PodeOAComponentRequestBody { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name, - [Parameter(Mandatory=$true, ValueFromPipeline=$true)] + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [hashtable] $ContentSchemas, @@ -643,9 +634,9 @@ function Add-PodeOAComponentRequestBody ) $PodeContext.Server.OpenAPI.components.requestBodies[$Name] = @{ - required = $Required.IsPresent + required = $Required.IsPresent description = $Description - content = ($ContentSchemas | ConvertTo-PodeOAContentTypeSchema) + content = ($ContentSchemas | ConvertTo-PodeOAContentTypeSchema) } } @@ -665,15 +656,14 @@ The Parameter to use for the component (from ConvertTo-PodeOAParameter) .EXAMPLE New-PodeOAIntProperty -Name 'userId' | ConvertTo-PodeOAParameter -In Query | Add-PodeOAComponentParameter -Name 'UserIdParam' #> -function Add-PodeOAComponentParameter -{ +function Add-PodeOAComponentParameter { [CmdletBinding()] param( [Parameter()] [string] $Name, - [Parameter(Mandatory=$true, ValueFromPipeline=$true)] + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [hashtable] $Parameter ) @@ -731,8 +721,7 @@ If supplied, the integer will be automatically wrapped in an object. .EXAMPLE New-PodeOANumberProperty -Name 'age' -Required #> -function New-PodeOAIntProperty -{ +function New-PodeOAIntProperty { [CmdletBinding()] param( [Parameter()] @@ -782,17 +771,17 @@ function New-PodeOAIntProperty ) $param = @{ - name = $Name - type = 'integer' - array = $Array.IsPresent - object = $Object.IsPresent - required = $Required.IsPresent - deprecated = $Deprecated.IsPresent + name = $Name + type = 'integer' + array = $Array.IsPresent + object = $Object.IsPresent + required = $Required.IsPresent + deprecated = $Deprecated.IsPresent description = $Description - format = $Format.ToLowerInvariant() - default = $Default + format = $Format.ToLowerInvariant() + default = $Default - meta = @{ + meta = @{ enum = $Enum } } @@ -858,8 +847,7 @@ If supplied, the number will be automatically wrapped in an object. .EXAMPLE New-PodeOANumberProperty -Name 'gravity' -Default 9.8 #> -function New-PodeOANumberProperty -{ +function New-PodeOANumberProperty { [CmdletBinding()] param( [Parameter()] @@ -909,17 +897,17 @@ function New-PodeOANumberProperty ) $param = @{ - name = $Name - type = 'number' - array = $Array.IsPresent - object = $Object.IsPresent - required = $Required.IsPresent - deprecated = $Deprecated.IsPresent + name = $Name + type = 'number' + array = $Array.IsPresent + object = $Object.IsPresent + required = $Required.IsPresent + deprecated = $Deprecated.IsPresent description = $Description - format = $Format.ToLowerInvariant() - default = $Default + format = $Format.ToLowerInvariant() + default = $Default - meta = @{ + meta = @{ enum = $Enum } } @@ -991,20 +979,19 @@ New-PodeOAStringProperty -Name 'userType' -Default 'admin' .EXAMPLE New-PodeOAStringProperty -Name 'password' -Format Password #> -function New-PodeOAStringProperty -{ - [CmdletBinding(DefaultParameterSetName='Inbuilt')] +function New-PodeOAStringProperty { + [CmdletBinding(DefaultParameterSetName = 'Inbuilt')] param( [Parameter()] [string] $Name, - [Parameter(ParameterSetName='Inbuilt')] + [Parameter(ParameterSetName = 'Inbuilt')] [ValidateSet('', 'Binary', 'Byte', 'Date', 'Date-Time', 'Password')] [string] $Format, - [Parameter(ParameterSetName='Custom')] + [Parameter(ParameterSetName = 'Custom')] [string] $CustomFormat, @@ -1051,18 +1038,18 @@ function New-PodeOAStringProperty } $param = @{ - name = $Name - type = 'string' - array = $Array.IsPresent - object = $Object.IsPresent - required = $Required.IsPresent - deprecated = $Deprecated.IsPresent + name = $Name + type = 'string' + array = $Array.IsPresent + object = $Object.IsPresent + required = $Required.IsPresent + deprecated = $Deprecated.IsPresent description = $Description - format = $_format.ToLowerInvariant() - default = $Default + format = $_format.ToLowerInvariant() + default = $Default - meta = @{ - enum = $Enum + meta = @{ + enum = $Enum pattern = $Pattern } } @@ -1112,8 +1099,7 @@ If supplied, the boolean will be automatically wrapped in an object. .EXAMPLE New-PodeOABoolProperty -Name 'enabled' -Required #> -function New-PodeOABoolProperty -{ +function New-PodeOABoolProperty { [CmdletBinding()] param( [Parameter()] @@ -1146,16 +1132,16 @@ function New-PodeOABoolProperty ) $param = @{ - name = $Name - type = 'boolean' - array = $Array.IsPresent - object = $Object.IsPresent - required = $Required.IsPresent - deprecated = $Deprecated.IsPresent + name = $Name + type = 'boolean' + array = $Array.IsPresent + object = $Object.IsPresent + required = $Required.IsPresent + deprecated = $Deprecated.IsPresent description = $Description - default = $Default + default = $Default - meta = @{ + meta = @{ enum = $Enum } } @@ -1191,15 +1177,14 @@ If supplied, the object will be treated as an array of objects. .EXAMPLE New-PodeOAObjectProperty -Name 'user' -Properties @('') #> -function New-PodeOAObjectProperty -{ +function New-PodeOAObjectProperty { [CmdletBinding()] param( [Parameter()] [string] $Name, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [hashtable[]] $Properties, @@ -1218,14 +1203,14 @@ function New-PodeOAObjectProperty ) $param = @{ - name = $Name - type = 'object' - array = $Array.IsPresent - required = $Required.IsPresent - deprecated = $Deprecated.IsPresent + name = $Name + type = 'object' + array = $Array.IsPresent + required = $Required.IsPresent + deprecated = $Deprecated.IsPresent description = $Description - properties = $Properties - default = $Default + properties = $Properties + default = $Default } return $param @@ -1253,15 +1238,14 @@ If supplied, the schema reference will be treated as an array. .EXAMPLE New-PodeOASchemaProperty -Name 'Config' -ComponentSchema "MyConfigSchema" #> -function New-PodeOASchemaProperty -{ +function New-PodeOASchemaProperty { [CmdletBinding()] param( [Parameter()] [string] $Name, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Reference, @@ -1273,15 +1257,15 @@ function New-PodeOASchemaProperty $Array ) - if(!(Test-PodeOAComponentSchema -Name $Reference)) { + if (!(Test-PodeOAComponentSchema -Name $Reference)) { throw "The OpenApi component schema doesn't exist: $($Reference)" } $param = @{ - name = $Name - type = 'schema' - schema = $Reference - array = $Array.IsPresent + name = $Name + type = 'schema' + schema = $Reference + array = $Array.IsPresent description = $Description } @@ -1310,21 +1294,20 @@ New-PodeOAIntProperty -Name 'userId' | ConvertTo-PodeOAParameter -In Query .EXAMPLE ConvertTo-PodeOAParameter -Reference 'UserIdParam' #> -function ConvertTo-PodeOAParameter -{ - [CmdletBinding(DefaultParameterSetName='Reference')] +function ConvertTo-PodeOAParameter { + [CmdletBinding(DefaultParameterSetName = 'Reference')] param( - [Parameter(Mandatory=$true, ParameterSetName='Schema')] + [Parameter(Mandatory = $true, ParameterSetName = 'Schema')] [ValidateSet('Cookie', 'Header', 'Path', 'Query')] [string] $In, - [Parameter(Mandatory=$true, ValueFromPipeline=$true, ParameterSetName='Schema')] + [Parameter(Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = 'Schema')] [ValidateNotNull()] [hashtable] $Property, - [Parameter(Mandatory=$true, ParameterSetName='Reference')] + [Parameter(Mandatory = $true, ParameterSetName = 'Reference')] [string] $Reference ) @@ -1342,16 +1325,16 @@ function ConvertTo-PodeOAParameter # non-object/array only if (@('array', 'object') -icontains $Property.type) { - throw "OpenApi request parameter cannot be an array of object" + throw 'OpenApi request parameter cannot be an array of object' } # build the base parameter $prop = @{ - in = $In.ToLowerInvariant() - name = $Property.name + in = $In.ToLowerInvariant() + name = $Property.name description = $Property.description - schema = @{ - type = $Property.type + schema = @{ + type = $Property.type format = $Property.format } } @@ -1409,11 +1392,10 @@ If supplied, the route passed in will be returned for further chaining. .EXAMPLE Add-PodeRoute -PassThru | Set-PodeOARouteInfo -Summary 'A quick summary' -Tags 'Admin' #> -function Set-PodeOARouteInfo -{ +function Set-PodeOARouteInfo { [CmdletBinding()] param( - [Parameter(Mandatory=$true, ValueFromPipeline=$true)] + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [ValidateNotNullOrEmpty()] [hashtable[]] $Route, @@ -1485,11 +1467,10 @@ Enable-PodeOpenApiViewer -Type Swagger -DarkMode .EXAMPLE Enable-PodeOpenApiViewer -Type ReDoc -Title 'Some Title' -OpenApi 'http://some-url/openapi' #> -function Enable-PodeOpenApiViewer -{ +function Enable-PodeOpenApiViewer { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [ValidateSet('Swagger', 'ReDoc')] [string] $Type, @@ -1535,9 +1516,9 @@ function Enable-PodeOpenApiViewer # setup meta info $meta = @{ - Type = $Type.ToLowerInvariant() - Title = $Title - OpenApi = $OpenApiUrl + Type = $Type.ToLowerInvariant() + Title = $Title + OpenApi = $OpenApiUrl DarkMode = $DarkMode } @@ -1546,8 +1527,8 @@ function Enable-PodeOpenApiViewer param($meta) $podeRoot = Get-PodeModuleMiscPath Write-PodeFileResponse -Path ([System.IO.Path]::Combine($podeRoot, "default-$($meta.Type).html.pode")) -Data @{ - Title = $meta.Title - OpenApi = $meta.OpenApi + Title = $meta.Title + OpenApi = $meta.OpenApi DarkMode = $meta.DarkMode } } diff --git a/src/Public/Responses.ps1 b/src/Public/Responses.ps1 index 8f55dad3a..3af31830c 100644 --- a/src/Public/Responses.ps1 +++ b/src/Public/Responses.ps1 @@ -33,11 +33,10 @@ Set-PodeResponseAttachment -Path './data.txt' -ContentType 'application/json' .EXAMPLE Set-PodeResponseAttachment -Path '/assets/data.txt' -EndpointName 'Example' #> -function Set-PodeResponseAttachment -{ +function Set-PodeResponseAttachment { [CmdletBinding()] - param ( - [Parameter(Mandatory=$true, ValueFromPipeline=$true)] + param( + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [string] $Path, @@ -158,15 +157,14 @@ Write-PodeTextResponse -Bytes (Get-Content -Path ./some/image.png -Raw -AsByteSt .EXAMPLE Write-PodeTextResponse -Value 'Untitled Text Response' -StatusCode 418 #> -function Write-PodeTextResponse -{ - [CmdletBinding(DefaultParameterSetName='String')] - param ( - [Parameter(ParameterSetName='String', ValueFromPipeline=$true, Position=0)] +function Write-PodeTextResponse { + [CmdletBinding(DefaultParameterSetName = 'String')] + param( + [Parameter(ParameterSetName = 'String', ValueFromPipeline = $true, Position = 0)] [string] $Value, - [Parameter(ParameterSetName='Bytes')] + [Parameter(ParameterSetName = 'Bytes')] [byte[]] $Bytes, @@ -212,7 +210,7 @@ function Write-PodeTextResponse # set a cache value if ($Cache) { Set-PodeHeader -Name 'Cache-Control' -Value "max-age=$($MaxAge), must-revalidate" - Set-PodeHeader -Name 'Expires' -Value ([datetime]::UtcNow.AddSeconds($MaxAge).ToString("r", [CultureInfo]::InvariantCulture)) + Set-PodeHeader -Name 'Expires' -Value ([datetime]::UtcNow.AddSeconds($MaxAge).ToString('r', [CultureInfo]::InvariantCulture)) } # specify the content-type if supplied (adding utf-8 if missing) @@ -247,44 +245,44 @@ function Write-PodeTextResponse $size = $Bytes.Length $Bytes = @(foreach ($range in $WebEvent.Ranges) { - # ensure range not invalid - if (([int]$range.Start -lt 0) -or ([int]$range.Start -ge $size) -or ([int]$range.End -lt 0)) { - Set-PodeResponseStatus -Code 416 -NoErrorPage - return - } - - # skip start bytes only - if ([string]::IsNullOrWhiteSpace($range.End)) { - $Bytes[$range.Start..($size - 1)] - $lengths += "$($range.Start)-$($size - 1)/$($size)" - } + # ensure range not invalid + if (([int]$range.Start -lt 0) -or ([int]$range.Start -ge $size) -or ([int]$range.End -lt 0)) { + Set-PodeResponseStatus -Code 416 -NoErrorPage + return + } - # end bytes only - elseif ([string]::IsNullOrWhiteSpace($range.Start)) { - if ([int]$range.End -gt $size) { - $range.End = $size + # skip start bytes only + if ([string]::IsNullOrWhiteSpace($range.End)) { + $Bytes[$range.Start..($size - 1)] + $lengths += "$($range.Start)-$($size - 1)/$($size)" } - if ([int]$range.End -gt 0) { - $Bytes[$($size - $range.End)..($size - 1)] - $lengths += "$($size - $range.End)-$($size - 1)/$($size)" + # end bytes only + elseif ([string]::IsNullOrWhiteSpace($range.Start)) { + if ([int]$range.End -gt $size) { + $range.End = $size + } + + if ([int]$range.End -gt 0) { + $Bytes[$($size - $range.End)..($size - 1)] + $lengths += "$($size - $range.End)-$($size - 1)/$($size)" + } + else { + $lengths += "0-0/$($size)" + } } + + # normal range else { - $lengths += "0-0/$($size)" - } - } + if ([int]$range.End -ge $size) { + Set-PodeResponseStatus -Code 416 -NoErrorPage + return + } - # normal range - else { - if ([int]$range.End -ge $size) { - Set-PodeResponseStatus -Code 416 -NoErrorPage - return + $Bytes[$range.Start..$range.End] + $lengths += "$($range.Start)-$($range.End)/$($size)" } - - $Bytes[$range.Start..$range.End] - $lengths += "$($range.Start)-$($range.End)/$($size)" - } - }) + }) Set-PodeHeader -Name 'Content-Range' -Value "bytes $($lengths -join ', ')" if ($StatusCode -eq 200) { @@ -381,11 +379,10 @@ Write-PodeFileResponse -Path 'C:/Views/Index.pode' -Data @{ Counter = 2 } .EXAMPLE Write-PodeFileResponse -Path 'C:/Files/Stuff.txt' -StatusCode 201 #> -function Write-PodeFileResponse -{ +function Write-PodeFileResponse { [CmdletBinding()] - param ( - [Parameter(Mandatory=$true, ValueFromPipeline=$true)] + param( + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [ValidateNotNull()] [string] $Path, @@ -422,9 +419,9 @@ function Write-PodeFileResponse # generate dynamic content if (![string]::IsNullOrWhiteSpace($mainExt) -and ( - ($mainExt -ieq 'pode') -or - ($mainExt -ieq $PodeContext.Server.ViewEngine.Extension -and $PodeContext.Server.ViewEngine.IsDynamic) - )) { + ($mainExt -ieq 'pode') -or + ($mainExt -ieq $PodeContext.Server.ViewEngine.Extension -and $PodeContext.Server.ViewEngine.IsDynamic) + )) { $content = Get-PodeFileContentUsingViewEngine -Path $Path -Data $Data # get the sub-file extension, if empty, use original @@ -474,14 +471,13 @@ Write-PodeCsvResponse -Value @{ Name = 'Rick' } .EXAMPLE Write-PodeCsvResponse -Path 'E:/Files/Names.csv' #> -function Write-PodeCsvResponse -{ - [CmdletBinding(DefaultParameterSetName='Value')] - param ( - [Parameter(Mandatory=$true, ParameterSetName='Value', ValueFromPipeline=$true, Position=0)] +function Write-PodeCsvResponse { + [CmdletBinding(DefaultParameterSetName = 'Value')] + param( + [Parameter(Mandatory = $true, ParameterSetName = 'Value', ValueFromPipeline = $true, Position = 0)] $Value, - [Parameter(Mandatory=$true, ParameterSetName='File')] + [Parameter(Mandatory = $true, ParameterSetName = 'File')] [string] $Path, @@ -500,8 +496,8 @@ function Write-PodeCsvResponse 'value' { if ($Value -isnot [string]) { $Value = @(foreach ($v in $Value) { - New-Object psobject -Property $v - }) + New-Object psobject -Property $v + }) if (Test-PodeIsPSCore) { $Value = ($Value | ConvertTo-Csv -Delimiter ',' -IncludeTypeInformation:$false) @@ -547,14 +543,13 @@ Write-PodeHtmlResponse -Value @{ Message = 'Hello, all!' } .EXAMPLE Write-PodeHtmlResponse -Path 'E:/Site/About.html' #> -function Write-PodeHtmlResponse -{ - [CmdletBinding(DefaultParameterSetName='Value')] - param ( - [Parameter(Mandatory=$true, ParameterSetName='Value', ValueFromPipeline=$true, Position=0)] +function Write-PodeHtmlResponse { + [CmdletBinding(DefaultParameterSetName = 'Value')] + param( + [Parameter(Mandatory = $true, ParameterSetName = 'Value', ValueFromPipeline = $true, Position = 0)] $Value, - [Parameter(Mandatory=$true, ParameterSetName='File')] + [Parameter(Mandatory = $true, ParameterSetName = 'File')] [string] $Path, @@ -610,14 +605,13 @@ Write-PodeMarkdownResponse -Value '# Hello, world!' -AsHtml .EXAMPLE Write-PodeMarkdownResponse -Path 'E:/Site/About.md' #> -function Write-PodeMarkdownResponse -{ - [CmdletBinding(DefaultParameterSetName='Value')] - param ( - [Parameter(Mandatory=$true, ParameterSetName='Value', ValueFromPipeline=$true, Position=0)] +function Write-PodeMarkdownResponse { + [CmdletBinding(DefaultParameterSetName = 'Value')] + param( + [Parameter(Mandatory = $true, ParameterSetName = 'Value', ValueFromPipeline = $true, Position = 0)] $Value, - [Parameter(Mandatory=$true, ParameterSetName='File')] + [Parameter(Mandatory = $true, ParameterSetName = 'File')] [string] $Path, @@ -681,14 +675,13 @@ Write-PodeJsonResponse -Value @{ Name = 'Rick' } -StatusCode 201 .EXAMPLE Write-PodeJsonResponse -Path 'E:/Files/Names.json' #> -function Write-PodeJsonResponse -{ - [CmdletBinding(DefaultParameterSetName='Value')] - param ( - [Parameter(Mandatory=$true, ParameterSetName='Value', ValueFromPipeline=$true, Position=0)] +function Write-PodeJsonResponse { + [CmdletBinding(DefaultParameterSetName = 'Value')] + param( + [Parameter(Mandatory = $true, ParameterSetName = 'Value', ValueFromPipeline = $true, Position = 0)] $Value, - [Parameter(Mandatory=$true, ParameterSetName='File')] + [Parameter(Mandatory = $true, ParameterSetName = 'File')] [string] $Path, @@ -752,14 +745,13 @@ Write-PodeXmlResponse -Value @{ Name = 'Rick' } -StatusCode 201 .EXAMPLE Write-PodeXmlResponse -Path 'E:/Files/Names.xml' #> -function Write-PodeXmlResponse -{ - [CmdletBinding(DefaultParameterSetName='Value')] - param ( - [Parameter(Mandatory=$true, ParameterSetName='Value', ValueFromPipeline=$true, Position=0)] +function Write-PodeXmlResponse { + [CmdletBinding(DefaultParameterSetName = 'Value')] + param( + [Parameter(Mandatory = $true, ParameterSetName = 'Value', ValueFromPipeline = $true, Position = 0)] $Value, - [Parameter(Mandatory=$true, ParameterSetName='File')] + [Parameter(Mandatory = $true, ParameterSetName = 'File')] [string] $Path, @@ -778,8 +770,8 @@ function Write-PodeXmlResponse 'value' { if ($Value -isnot [string]) { $Value = @(foreach ($v in $Value) { - New-Object psobject -Property $v - }) + New-Object psobject -Property $v + }) $Value = ($Value | ConvertTo-Xml -Depth 10 -As String -NoTypeInformation) } @@ -824,11 +816,10 @@ Write-PodeViewResponse -Path 'accounts/profile_page' -Data @{ Username = 'Morty' .EXAMPLE Write-PodeViewResponse -Path 'login' -FlashMessages #> -function Write-PodeViewResponse -{ +function Write-PodeViewResponse { [CmdletBinding()] - param ( - [Parameter(Mandatory=$true, ValueFromPipeline=$true)] + param( + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [string] $Path, @@ -935,11 +926,10 @@ Set-PodeResponseStatus -Code 500 -Exception $_.Exception .EXAMPLE Set-PodeResponseStatus -Code 500 -Exception $_.Exception -ContentType 'application/json' #> -function Set-PodeResponseStatus -{ +function Set-PodeResponseStatus { [CmdletBinding()] - param ( - [Parameter(Mandatory=$true)] + param( + [Parameter(Mandatory = $true)] [int] $Code, @@ -1018,28 +1008,27 @@ Move-PodeResponseUrl -Protocol HTTPS .EXAMPLE Move-PodeResponseUrl -Port 9000 -Moved #> -function Move-PodeResponseUrl -{ - [CmdletBinding(DefaultParameterSetName='Url')] +function Move-PodeResponseUrl { + [CmdletBinding(DefaultParameterSetName = 'Url')] param( - [Parameter(Mandatory=$true, ParameterSetName='Url')] + [Parameter(Mandatory = $true, ParameterSetName = 'Url')] [string] $Url, - [Parameter(ParameterSetName='Endpoint')] + [Parameter(ParameterSetName = 'Endpoint')] [string] $EndpointName, - [Parameter(ParameterSetName='Components')] + [Parameter(ParameterSetName = 'Components')] [int] $Port = 0, - [Parameter(ParameterSetName='Components')] + [Parameter(ParameterSetName = 'Components')] [ValidateSet('', 'Http', 'Https')] [string] $Protocol, - [Parameter(ParameterSetName='Components')] + [Parameter(ParameterSetName = 'Components')] [string] $Address, @@ -1112,11 +1101,10 @@ The message to write .EXAMPLE Write-PodeTcpClient -Message '250 OK' #> -function Write-PodeTcpClient -{ +function Write-PodeTcpClient { [CmdletBinding()] param( - [Parameter(ValueFromPipeline=$true)] + [Parameter(ValueFromPipeline = $true)] [string] $Message ) @@ -1146,20 +1134,19 @@ $data = Read-PodeTcpClient .EXAMPLE $data = Read-PodeTcpClient -CRLFMessageEnd #> -function Read-PodeTcpClient -{ - [CmdletBinding(DefaultParameterSetName='default')] +function Read-PodeTcpClient { + [CmdletBinding(DefaultParameterSetName = 'default')] [OutputType([string])] param( [Parameter()] [int] $Timeout = 0, - [Parameter(ParameterSetName='CheckBytes')] + [Parameter(ParameterSetName = 'CheckBytes')] [byte[]] $CheckBytes = $null, - [Parameter(ParameterSetName='CRLF')] + [Parameter(ParameterSetName = 'CRLF')] [switch] $CRLFMessageEnd ) @@ -1182,8 +1169,7 @@ Close an open TCP client connection .EXAMPLE Close-PodeTcpClient #> -function Close-PodeTcpClient -{ +function Close-PodeTcpClient { [CmdletBinding()] param() @@ -1216,11 +1202,10 @@ Save-PodeRequestFile -Key 'avatar' -Path 'F:/Images' .EXAMPLE Save-PodeRequestFile -Key 'avatar' -Path 'F:/Images' -FileName 'icon.png' #> -function Save-PodeRequestFile -{ +function Save-PodeRequestFile { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Key, @@ -1245,10 +1230,10 @@ function Save-PodeRequestFile $files = @($WebEvent.Data[$Key]) if (($null -ne $FileName) -and ($FileName.Length -gt 0)) { $files = @(foreach ($file in $files) { - if ($FileName -icontains $file) { - $file - } - }) + if ($FileName -icontains $file) { + $file + } + }) } # ensure the file data exists @@ -1290,11 +1275,10 @@ Test-PodeRequestFile -Key 'avatar' .EXAMPLE Test-PodeRequestFile -Key 'avatar' -FileName 'icon.png' #> -function Test-PodeRequestFile -{ +function Test-PodeRequestFile { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Key, @@ -1347,10 +1331,9 @@ Set-PodeViewEngine -Type Markdown .EXAMPLE Set-PodeViewEngine -Type PSHTML -Extension PS1 -ScriptBlock { param($path, $data) /* logic */ } #> -function Set-PodeViewEngine -{ +function Set-PodeViewEngine { [CmdletBinding()] - param ( + param( [Parameter()] [string] $Type, @@ -1406,12 +1389,11 @@ If supplied, a custom views folder will be used. .EXAMPLE Use-PodePartialView -Path 'shared/footer' #> -function Use-PodePartialView -{ +function Use-PodePartialView { [CmdletBinding()] [OutputType([string])] - param ( - [Parameter(Mandatory=$true, ValueFromPipeline=$true)] + param( + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [string] $Path, @@ -1482,11 +1464,10 @@ Send-PodeSignal -Value @{ Message = 'Hello, world!' } .EXAMPLE Send-PodeSignal -Value @{ Data = @(123, 100, 101) } -Path '/response-charts' #> -function Send-PodeSignal -{ +function Send-PodeSignal { [CmdletBinding()] param( - [Parameter(ValueFromPipeline=$true)] + [Parameter(ValueFromPipeline = $true)] $Value, [Parameter()] @@ -1512,7 +1493,7 @@ function Send-PodeSignal # error if not configured if (!$PodeContext.Server.Signals.Enabled) { - throw "WebSockets have not been configured to send signal messages" + throw 'WebSockets have not been configured to send signal messages' } # do nothing if no value @@ -1570,15 +1551,14 @@ The literal, or relative, path to the directory that contains views. .EXAMPLE Add-PodeViewFolder -Name 'assets' -Source './assets' #> -function Add-PodeViewFolder -{ +function Add-PodeViewFolder { [CmdletBinding()] - param ( - [Parameter(Mandatory=$true)] + param( + [Parameter(Mandatory = $true)] [string] $Name, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Source ) diff --git a/src/Public/Routes.ps1 b/src/Public/Routes.ps1 index 76ed89814..ae6d1d1e5 100644 --- a/src/Public/Routes.ps1 +++ b/src/Public/Routes.ps1 @@ -38,6 +38,9 @@ An array of arguments to supply to the Route's ScriptBlock. .PARAMETER Authentication The name of an Authentication method which should be used as middleware on this Route. +.PARAMETER Access +The name of an Access method which should be used as middleware on this Route. + .PARAMETER AllowAnon If supplied, the Route will allow anonymous access for non-authenticated users. @@ -53,6 +56,18 @@ If supplied, the route created will be returned so it can be passed through a pi .PARAMETER IfExists Specifies what action to take when a Route already exists. (Default: Default) +.PARAMETER Role +One or more optional Roles that will be authorised to access this Route, when using Authentication with an Access method. + +.PARAMETER Group +One or more optional Groups that will be authorised to access this Route, when using Authentication with an Access method. + +.PARAMETER Scope +One or more optional Scopes that will be authorised to access this Route, when using Authentication with an Access method. + +.PARAMETER User +One or more optional Users that will be authorised to access this Route, when using Authentication with an Access method. + .EXAMPLE Add-PodeRoute -Method Get -Path '/' -ScriptBlock { /* logic */ } @@ -70,17 +85,19 @@ Add-PodeRoute -Method Get -Path '/api/cpu' -ErrorContentType 'application/json' .EXAMPLE Add-PodeRoute -Method Get -Path '/' -ScriptBlock { /* logic */ } -ArgumentList 'arg1', 'arg2' + +.EXAMPLE +Add-PodeRoute -Method Get -Path '/' -Role 'Developer', 'QA' -ScriptBlock { /* logic */ } #> -function Add-PodeRoute -{ - [CmdletBinding(DefaultParameterSetName='Script')] +function Add-PodeRoute { + [CmdletBinding(DefaultParameterSetName = 'Script')] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [ValidateSet('Connect', 'Delete', 'Get', 'Head', 'Merge', 'Options', 'Patch', 'Post', 'Put', 'Trace', '*')] [string[]] $Method, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Path, @@ -88,7 +105,7 @@ function Add-PodeRoute [object[]] $Middleware, - [Parameter(ParameterSetName='Script')] + [Parameter(ParameterSetName = 'Script')] [scriptblock] $ScriptBlock, @@ -109,7 +126,7 @@ function Add-PodeRoute [string] $ErrorContentType, - [Parameter(Mandatory=$true, ParameterSetName='File')] + [Parameter(Mandatory = $true, ParameterSetName = 'File')] [string] $FilePath, @@ -122,10 +139,31 @@ function Add-PodeRoute [string] $Authentication, + [Parameter()] + [string] + $Access, + [Parameter()] [ValidateSet('Default', 'Error', 'Overwrite', 'Skip')] + [string] $IfExists = 'Default', + [Parameter()] + [string[]] + $Role, + + [Parameter()] + [string[]] + $Group, + + [Parameter()] + [string[]] + $Scope, + + [Parameter()] + [string[]] + $User, + [switch] $AllowAnon, @@ -169,9 +207,37 @@ function Add-PodeRoute $Authentication = $RouteGroup.Authentication } + if ([string]::IsNullOrWhiteSpace($Access)) { + $Access = $RouteGroup.Access + } + if ($RouteGroup.AllowAnon) { $AllowAnon = $RouteGroup.AllowAnon } + + if ($RouteGroup.IfExists -ine 'default') { + $IfExists = $RouteGroup.IfExists + } + + if ($null -ne $RouteGroup.AccessMeta.Role) { + $Role = $RouteGroup.AccessMeta.Role + $Role + } + + if ($null -ne $RouteGroup.AccessMeta.Group) { + $Group = $RouteGroup.AccessMeta.Group + $Group + } + + if ($null -ne $RouteGroup.AccessMeta.Scope) { + $Scope = $RouteGroup.AccessMeta.Scope + $Scope + } + + if ($null -ne $RouteGroup.AccessMeta.User) { + $User = $RouteGroup.AccessMeta.User + $User + } + + if ($null -ne $RouteGroup.AccessMeta.Custom) { + $CustomAccess = $RouteGroup.AccessMeta.Custom + } } # var for new routes created @@ -183,7 +249,7 @@ function Add-PodeRoute # split route on '?' for query $Path = Split-PodeRouteQuery -Path $Path if ([string]::IsNullOrWhiteSpace($Path)) { - throw "No Path supplied for Route" + throw 'No Path supplied for Route' } # ensure the route has appropriate slashes @@ -219,22 +285,44 @@ function Add-PodeRoute # convert any middleware into valid hashtables $Middleware = @(ConvertTo-PodeMiddleware -Middleware $Middleware -PSSession $PSCmdlet.SessionState) + # if an access name was supplied, setup access as middleware first to it's after auth middleware + if (![string]::IsNullOrWhiteSpace($Access)) { + if ([string]::IsNullOrWhiteSpace($Authentication)) { + throw 'Access requires Authentication to be supplied on Routes' + } + + if (!(Test-PodeAccessExists -Name $Access)) { + throw "Access method does not exist: $($Access)" + } + + $options = @{ + Name = $Access + } + + $Middleware = (@(Get-PodeAccessMiddlewareScript | New-PodeMiddleware -ArgumentList $options) + $Middleware) + } + # if an auth name was supplied, setup the auth as the first middleware if (![string]::IsNullOrWhiteSpace($Authentication)) { - if (!(Test-PodeAuth -Name $Authentication)) { + if (!(Test-PodeAuthExists -Name $Authentication)) { throw "Authentication method does not exist: $($Authentication)" } $options = @{ - Name = $Authentication - Login = $Login + Name = $Authentication + Login = $Login Logout = $Logout - Anon = $AllowAnon + Anon = $AllowAnon } $Middleware = (@(Get-PodeAuthMiddlewareScript | New-PodeMiddleware -ArgumentList $options) + $Middleware) } + # custom access + if ($null -eq $CustomAccess) { + $CustomAccess = @{} + } + # workout a default content type for the route $ContentType = Find-PodeRouteContentType -Path $Path -ContentType $ContentType @@ -245,20 +333,20 @@ function Add-PodeRoute foreach ($_method in $Method) { # ensure the route doesn't already exist for each endpoint $endpoints = @(foreach ($_endpoint in $endpoints) { - $found = Test-PodeRouteInternal -Method $_method -Path $Path -Protocol $_endpoint.Protocol -Address $_endpoint.Address -ThrowError:($IfExists -ieq 'Error') + $found = Test-PodeRouteInternal -Method $_method -Path $Path -Protocol $_endpoint.Protocol -Address $_endpoint.Address -ThrowError:($IfExists -ieq 'Error') - if ($found) { - if ($IfExists -ieq 'Overwrite') { - Remove-PodeRoute -Method $_method -Path $origPath -EndpointName $_endpoint.Name - } + if ($found) { + if ($IfExists -ieq 'Overwrite') { + Remove-PodeRoute -Method $_method -Path $origPath -EndpointName $_endpoint.Name + } - if ($IfExists -ieq 'Skip') { - continue + if ($IfExists -ieq 'Skip') { + continue + } } - } - $_endpoint - }) + $_endpoint + }) if (($null -eq $endpoints) -or ($endpoints.Length -eq 0)) { continue @@ -267,41 +355,49 @@ function Add-PodeRoute # add the route(s) Write-Verbose "Adding Route: [$($_method)] $($Path)" $methodRoutes = @(foreach ($_endpoint in $endpoints) { - @{ - Logic = $ScriptBlock - UsingVariables = $usingVars - Middleware = $Middleware - Authentication = $Authentication - Endpoint = @{ - Protocol = $_endpoint.Protocol - Address = $_endpoint.Address.Trim() - Name = $_endpoint.Name - } - ContentType = $ContentType - TransferEncoding = $TransferEncoding - ErrorType = $ErrorContentType - Arguments = $ArgumentList - Method = $_method - Path = $Path - OpenApi = @{ - Path = $OpenApiPath - Responses = @{ - '200' = @{ description = 'OK' } - 'default' = @{ description = 'Internal server error' } + @{ + Logic = $ScriptBlock + UsingVariables = $usingVars + Middleware = $Middleware + Authentication = $Authentication + Access = $Access + AccessMeta = @{ + Role = $Role + Group = $Group + Scope = $Scope + User = $User + Custom = $CustomAccess } - Parameters = $null - RequestBody = $null - Authentication = @() - } - IsStatic = $false - Metrics = @{ - Requests = @{ - Total = 0 - StatusCodes = @{} + Endpoint = @{ + Protocol = $_endpoint.Protocol + Address = $_endpoint.Address.Trim() + Name = $_endpoint.Name + } + ContentType = $ContentType + TransferEncoding = $TransferEncoding + ErrorType = $ErrorContentType + Arguments = $ArgumentList + Method = $_method + Path = $Path + OpenApi = @{ + Path = $OpenApiPath + Responses = @{ + '200' = @{ description = 'OK' } + 'default' = @{ description = 'Internal server error' } + } + Parameters = $null + RequestBody = $null + Authentication = @() + } + IsStatic = $false + Metrics = @{ + Requests = @{ + Total = 0 + StatusCodes = @{} + } } } - } - }) + }) if (![string]::IsNullOrWhiteSpace($Authentication)) { Set-PodeOAAuth -Route $methodRoutes -Name $Authentication @@ -353,6 +449,9 @@ The content type of any error pages that may get returned. .PARAMETER Authentication The name of an Authentication method which should be used as middleware on this Route. +.PARAMETER Access +The name of an Access method which should be used as middleware on this Route. + .PARAMETER AllowAnon If supplied, the static route will allow anonymous access for non-authenticated users. @@ -365,6 +464,18 @@ If supplied, the static route created will be returned so it can be passed throu .PARAMETER IfExists Specifies what action to take when a Static Route already exists. (Default: Default) +.PARAMETER Role +One or more optional Roles that will be authorised to access this Route, when using Authentication with an Access method. + +.PARAMETER Group +One or more optional Groups that will be authorised to access this Route, when using Authentication with an Access method. + +.PARAMETER Scope +One or more optional Scopes that will be authorised to access this Route, when using Authentication with an Access method. + +.PARAMETER User +One or more optional Users that will be authorised to access this Route, when using Authentication with an Access method. + .EXAMPLE Add-PodeStaticRoute -Path '/assets' -Source './assets' @@ -374,15 +485,14 @@ Add-PodeStaticRoute -Path '/assets' -Source './assets' -Defaults @('index.html') .EXAMPLE Add-PodeStaticRoute -Path '/installers' -Source './exes' -DownloadOnly #> -function Add-PodeStaticRoute -{ +function Add-PodeStaticRoute { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Path, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Source, @@ -416,10 +526,31 @@ function Add-PodeStaticRoute [string] $Authentication, + [Parameter()] + [string] + $Access, + [Parameter()] [ValidateSet('Default', 'Error', 'Overwrite', 'Skip')] + [string] $IfExists = 'Default', + [Parameter()] + [string[]] + $Role, + + [Parameter()] + [string[]] + $Group, + + [Parameter()] + [string[]] + $Scope, + + [Parameter()] + [string[]] + $User, + [switch] $AllowAnon, @@ -464,6 +595,10 @@ function Add-PodeStaticRoute $Authentication = $RouteGroup.Authentication } + if ([string]::IsNullOrWhiteSpace($Access)) { + $Access = $RouteGroup.Access + } + if (Test-PodeIsEmpty $Defaults) { $Defaults = $RouteGroup.Defaults } @@ -475,6 +610,30 @@ function Add-PodeStaticRoute if ($RouteGroup.DownloadOnly) { $DownloadOnly = $RouteGroup.DownloadOnly } + + if ($RouteGroup.IfExists -ine 'default') { + $IfExists = $RouteGroup.IfExists + } + + if ($null -ne $RouteGroup.AccessMeta.Role) { + $Role = $RouteGroup.AccessMeta.Role + $Role + } + + if ($null -ne $RouteGroup.AccessMeta.Group) { + $Group = $RouteGroup.AccessMeta.Group + $Group + } + + if ($null -ne $RouteGroup.AccessMeta.Scope) { + $Scope = $RouteGroup.AccessMeta.Scope + $Scope + } + + if ($null -ne $RouteGroup.AccessMeta.User) { + $User = $RouteGroup.AccessMeta.User + $User + } + + if ($null -ne $RouteGroup.AccessMeta.Custom) { + $CustomAccess = $RouteGroup.AccessMeta.Custom + } } # store the route method @@ -508,20 +667,20 @@ function Add-PodeStaticRoute # ensure the route doesn't already exist for each endpoint $endpoints = @(foreach ($_endpoint in $endpoints) { - $found = Test-PodeRouteInternal -Method $Method -Path $Path -Protocol $_endpoint.Protocol -Address $_endpoint.Address -ThrowError:($IfExists -ieq 'Error') + $found = Test-PodeRouteInternal -Method $Method -Path $Path -Protocol $_endpoint.Protocol -Address $_endpoint.Address -ThrowError:($IfExists -ieq 'Error') - if ($found) { - if ($IfExists -ieq 'Overwrite') { - Remove-PodeStaticRoute -Path $origPath -EndpointName $_endpoint.Name - } + if ($found) { + if ($IfExists -ieq 'Overwrite') { + Remove-PodeStaticRoute -Path $origPath -EndpointName $_endpoint.Name + } - if ($IfExists -ieq 'Skip') { - continue + if ($IfExists -ieq 'Skip') { + continue + } } - } - $_endpoint - }) + $_endpoint + }) if (($null -eq $endpoints) -or ($endpoints.Length -eq 0)) { return @@ -544,9 +703,26 @@ function Add-PodeStaticRoute # convert any middleware into valid hashtables $Middleware = @(ConvertTo-PodeMiddleware -Middleware $Middleware -PSSession $PSCmdlet.SessionState) + # if an access name was supplied, setup access as middleware first to it's after auth middleware + if (![string]::IsNullOrWhiteSpace($Access)) { + if ([string]::IsNullOrWhiteSpace($Authentication)) { + throw 'Access requires Authentication to be supplied on Static Routes' + } + + if (!(Test-PodeAccessExists -Name $Access)) { + throw "Access method does not exist: $($Access)" + } + + $options = @{ + Name = $Access + } + + $Middleware = (@(Get-PodeAccessMiddlewareScript | New-PodeMiddleware -ArgumentList $options) + $Middleware) + } + # if an auth name was supplied, setup the auth as the first middleware if (![string]::IsNullOrWhiteSpace($Authentication)) { - if (!(Test-PodeAuth -Name $Authentication)) { + if (!(Test-PodeAuthExists -Name $Authentication)) { throw "Authentication method does not exist: $($Authentication)" } @@ -567,40 +743,49 @@ function Add-PodeStaticRoute # add the route(s) Write-Verbose "Adding Route: [$($Method)] $($Path)" $newRoutes = @(foreach ($_endpoint in $endpoints) { - @{ - Source = $Source - Path = $Path - Method = $Method - Defaults = $Defaults - Middleware = $Middleware - Endpoint = @{ - Protocol = $_endpoint.Protocol - Address = $_endpoint.Address.Trim() - Name = $_endpoint.Name - } - ContentType = $ContentType - TransferEncoding = $TransferEncoding - ErrorType = $ErrorContentType - Download = $DownloadOnly - OpenApi = @{ - Path = $OpenApiPath - Responses = @{ - '200' = @{ description = 'OK' } - 'default' = @{ description = 'Internal server error' } + @{ + Source = $Source + Path = $Path + Method = $Method + Defaults = $Defaults + Middleware = $Middleware + Authentication = $Authentication + Access = $Access + AccessMeta = @{ + Role = $Role + Group = $Group + Scope = $Scope + User = $User + Custom = $CustomAccess } - Parameters = @() - RequestBody = @{} - Authentication = @() - } - IsStatic = $true - Metrics = @{ - Requests = @{ - Total = 0 - StatusCodes = @{} + Endpoint = @{ + Protocol = $_endpoint.Protocol + Address = $_endpoint.Address.Trim() + Name = $_endpoint.Name + } + ContentType = $ContentType + TransferEncoding = $TransferEncoding + ErrorType = $ErrorContentType + Download = $DownloadOnly + OpenApi = @{ + Path = $OpenApiPath + Responses = @{ + '200' = @{ description = 'OK' } + 'default' = @{ description = 'Internal server error' } + } + Parameters = @() + RequestBody = @{} + Authentication = @() + } + IsStatic = $true + Metrics = @{ + Requests = @{ + Total = 0 + StatusCodes = @{} + } } } - } - }) + }) if (![string]::IsNullOrWhiteSpace($Authentication)) { Set-PodeOAAuth -Route $newRoutes -Name $Authentication @@ -645,15 +830,14 @@ Add-PodeSignalRoute -Path '/message' -ScriptBlock { /* logic */ } .EXAMPLE Add-PodeSignalRoute -Path '/message' -ScriptBlock { /* logic */ } -ArgumentList 'arg1', 'arg2' #> -function Add-PodeSignalRoute -{ - [CmdletBinding(DefaultParameterSetName='Script')] +function Add-PodeSignalRoute { + [CmdletBinding(DefaultParameterSetName = 'Script')] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Path, - [Parameter(ParameterSetName='Script')] + [Parameter(ParameterSetName = 'Script')] [scriptblock] $ScriptBlock, @@ -661,7 +845,7 @@ function Add-PodeSignalRoute [string[]] $EndpointName, - [Parameter(Mandatory=$true, ParameterSetName='File')] + [Parameter(Mandatory = $true, ParameterSetName = 'File')] [string] $FilePath, @@ -671,6 +855,7 @@ function Add-PodeSignalRoute [Parameter()] [ValidateSet('Default', 'Error', 'Overwrite', 'Skip')] + [string] $IfExists = 'Default' ) @@ -683,6 +868,10 @@ function Add-PodeSignalRoute if ([string]::IsNullOrWhiteSpace($EndpointName)) { $EndpointName = $RouteGroup.EndpointName } + + if ($RouteGroup.IfExists -ine 'default') { + $IfExists = $RouteGroup.IfExists + } } $Method = 'Signal' @@ -707,20 +896,20 @@ function Add-PodeSignalRoute # ensure the route doesn't already exist for each endpoint $endpoints = @(foreach ($_endpoint in $endpoints) { - $found = Test-PodeRouteInternal -Method $Method -Path $Path -Protocol $_endpoint.Protocol -Address $_endpoint.Address -ThrowError:($IfExists -ieq 'Error') + $found = Test-PodeRouteInternal -Method $Method -Path $Path -Protocol $_endpoint.Protocol -Address $_endpoint.Address -ThrowError:($IfExists -ieq 'Error') - if ($found) { - if ($IfExists -ieq 'Overwrite') { - Remove-PodeSignalRoute -Path $origPath -EndpointName $_endpoint.Name - } + if ($found) { + if ($IfExists -ieq 'Overwrite') { + Remove-PodeSignalRoute -Path $origPath -EndpointName $_endpoint.Name + } - if ($IfExists -ieq 'Skip') { - continue + if ($IfExists -ieq 'Skip') { + continue + } } - } - $_endpoint - }) + $_endpoint + }) if (($null -eq $endpoints) -or ($endpoints.Length -eq 0)) { return @@ -742,25 +931,25 @@ function Add-PodeSignalRoute # add the route(s) Write-Verbose "Adding Route: [$($Method)] $($Path)" $newRoutes = @(foreach ($_endpoint in $endpoints) { - @{ - Logic = $ScriptBlock - UsingVariables = $usingVars - Endpoint = @{ - Protocol = $_endpoint.Protocol - Address = $_endpoint.Address.Trim() - Name = $_endpoint.Name - } - Arguments = $ArgumentList - Method = $Method - Path = $Path - IsStatic = $false - Metrics = @{ - Requests = @{ - Total = 0 + @{ + Logic = $ScriptBlock + UsingVariables = $usingVars + Endpoint = @{ + Protocol = $_endpoint.Protocol + Address = $_endpoint.Address.Trim() + Name = $_endpoint.Name + } + Arguments = $ArgumentList + Method = $Method + Path = $Path + IsStatic = $false + Metrics = @{ + Requests = @{ + Total = 0 + } } } - } - }) + }) $PodeContext.Server.Routes[$Method][$Path] += @($newRoutes) } @@ -796,24 +985,38 @@ The content type of any error pages that may get returned. .PARAMETER Authentication The name of an Authentication method which should be used as middleware on the Routes. +.PARAMETER Access +The name of an Access method which should be used as middleware on this Route. + .PARAMETER IfExists Specifies what action to take when a Route already exists. (Default: Default) +.PARAMETER Role +One or more optional Roles that will be authorised to access this Route, when using Authentication with an Access method. + +.PARAMETER Group +One or more optional Groups that will be authorised to access this Route, when using Authentication with an Access method. + +.PARAMETER Scope +One or more optional Scopes that will be authorised to access this Route, when using Authentication with an Access method. + +.PARAMETER User +One or more optional Users that will be authorised to access this Route, when using Authentication with an Access method. + .PARAMETER AllowAnon If supplied, the Routes will allow anonymous access for non-authenticated users. .EXAMPLE Add-PodeRouteGroup -Path '/api' -Routes { Add-PodeRoute -Path '/route1' -Etc } #> -function Add-PodeRouteGroup -{ +function Add-PodeRouteGroup { [CmdletBinding()] param( [Parameter()] [string] $Path, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [scriptblock] $Routes, @@ -843,16 +1046,37 @@ function Add-PodeRouteGroup [string] $Authentication, + [Parameter()] + [string] + $Access, + [Parameter()] [ValidateSet('Default', 'Error', 'Overwrite', 'Skip')] + [string] $IfExists = 'Default', + [Parameter()] + [string[]] + $Role, + + [Parameter()] + [string[]] + $Group, + + [Parameter()] + [string[]] + $Scope, + + [Parameter()] + [string[]] + $User, + [switch] $AllowAnon ) if (Test-PodeIsEmpty $Routes) { - throw "No scriptblock for -Routes passed" + throw 'No scriptblock for -Routes passed' } if ($Path -eq '/') { @@ -892,25 +1116,57 @@ function Add-PodeRouteGroup $Authentication = $RouteGroup.Authentication } + if ([string]::IsNullOrWhiteSpace($Access)) { + $Access = $RouteGroup.Access + } + if ($RouteGroup.AllowAnon) { $AllowAnon = $RouteGroup.AllowAnon } - if ($IfExists -ieq 'default') { - $IfExists = Get-PodeRouteIfExistsPreference + if ($RouteGroup.IfExists -ine 'default') { + $IfExists = $RouteGroup.IfExists + } + + if ($null -ne $RouteGroup.AccessMeta.Role) { + $Role = $RouteGroup.AccessMeta.Role + $Role + } + + if ($null -ne $RouteGroup.AccessMeta.Group) { + $Group = $RouteGroup.AccessMeta.Group + $Group + } + + if ($null -ne $RouteGroup.AccessMeta.Scope) { + $Scope = $RouteGroup.AccessMeta.Scope + $Scope + } + + if ($null -ne $RouteGroup.AccessMeta.User) { + $User = $RouteGroup.AccessMeta.User + $User + } + + if ($null -ne $RouteGroup.AccessMeta.Custom) { + $CustomAccess = $RouteGroup.AccessMeta.Custom } } $RouteGroup = @{ - Path = $Path - Middleware = $Middleware - EndpointName = $EndpointName - ContentType = $ContentType + Path = $Path + Middleware = $Middleware + EndpointName = $EndpointName + ContentType = $ContentType TransferEncoding = $TransferEncoding ErrorContentType = $ErrorContentType - Authentication = $Authentication - AllowAnon = $AllowAnon - IfExists = $IfExists + Authentication = $Authentication + Access = $Access + AllowAnon = $AllowAnon + IfExists = $IfExists + AccessMeta = @{ + Role = $Role + Group = $Group + Scope = $Scope + User = $User + Custom = $CustomAccess + } } # add routes @@ -955,6 +1211,9 @@ The content type of any error pages that may get returned. .PARAMETER Authentication The name of an Authentication method which should be used as middleware on the Static Routes. +.PARAMETER Access +The name of an Access method which should be used as middleware on this Route. + .PARAMETER IfExists Specifies what action to take when a Static Route already exists. (Default: Default) @@ -964,11 +1223,22 @@ If supplied, the Static Routes will allow anonymous access for non-authenticated .PARAMETER DownloadOnly When supplied, all static content on the Routes will be attached as downloads - rather than rendered. +.PARAMETER Role +One or more optional Roles that will be authorised to access this Route, when using Authentication with an Access method. + +.PARAMETER Group +One or more optional Groups that will be authorised to access this Route, when using Authentication with an Access method. + +.PARAMETER Scope +One or more optional Scopes that will be authorised to access this Route, when using Authentication with an Access method. + +.PARAMETER User +One or more optional Users that will be authorised to access this Route, when using Authentication with an Access method. + .EXAMPLE Add-PodeStaticRouteGroup -Path '/static' -Routes { Add-PodeStaticRoute -Path '/images' -Etc } #> -function Add-PodeStaticRouteGroup -{ +function Add-PodeStaticRouteGroup { [CmdletBinding()] param( [Parameter()] @@ -979,7 +1249,7 @@ function Add-PodeStaticRouteGroup [string] $Source, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [scriptblock] $Routes, @@ -1013,10 +1283,31 @@ function Add-PodeStaticRouteGroup [string] $Authentication, + [Parameter()] + [string] + $Access, + [Parameter()] [ValidateSet('Default', 'Error', 'Overwrite', 'Skip')] + [string] $IfExists = 'Default', + [Parameter()] + [string[]] + $Role, + + [Parameter()] + [string[]] + $Group, + + [Parameter()] + [string[]] + $Scope, + + [Parameter()] + [string[]] + $User, + [switch] $AllowAnon, @@ -1025,7 +1316,7 @@ function Add-PodeStaticRouteGroup ) if (Test-PodeIsEmpty $Routes) { - throw "No scriptblock for -Routes passed" + throw 'No scriptblock for -Routes passed' } if ($Path -eq '/') { @@ -1069,6 +1360,10 @@ function Add-PodeStaticRouteGroup $Authentication = $RouteGroup.Authentication } + if ([string]::IsNullOrWhiteSpace($Access)) { + $Access = $RouteGroup.Access + } + if (Test-PodeIsEmpty $Defaults) { $Defaults = $RouteGroup.Defaults } @@ -1081,24 +1376,52 @@ function Add-PodeStaticRouteGroup $DownloadOnly = $RouteGroup.DownloadOnly } - if ($IfExists -ieq 'default') { - $IfExists = Get-PodeRouteIfExistsPreference + if ($RouteGroup.IfExists -ine 'default') { + $IfExists = $RouteGroup.IfExists + } + + if ($null -ne $RouteGroup.AccessMeta.Role) { + $Role = $RouteGroup.AccessMeta.Role + $Role + } + + if ($null -ne $RouteGroup.AccessMeta.Group) { + $Group = $RouteGroup.AccessMeta.Group + $Group + } + + if ($null -ne $RouteGroup.AccessMeta.Scope) { + $Scope = $RouteGroup.AccessMeta.Scope + $Scope + } + + if ($null -ne $RouteGroup.AccessMeta.User) { + $User = $RouteGroup.AccessMeta.User + $User + } + + if ($null -ne $RouteGroup.AccessMeta.Custom) { + $CustomAccess = $RouteGroup.AccessMeta.Custom } } $RouteGroup = @{ - Path = $Path - Source = $Source - Middleware = $Middleware - EndpointName = $EndpointName - ContentType = $ContentType + Path = $Path + Source = $Source + Middleware = $Middleware + EndpointName = $EndpointName + ContentType = $ContentType TransferEncoding = $TransferEncoding - Defaults = $Defaults + Defaults = $Defaults ErrorContentType = $ErrorContentType - Authentication = $Authentication - AllowAnon = $AllowAnon - DownloadOnly = $DownloadOnly - IfExists = $IfExists + Authentication = $Authentication + Access = $Access + AllowAnon = $AllowAnon + DownloadOnly = $DownloadOnly + IfExists = $IfExists + AccessMeta = @{ + Role = $Role + Group = $Group + Scope = $Scope + User = $User + Custom = $CustomAccess + } } # add routes @@ -1129,15 +1452,14 @@ Specifies what action to take when a Signal Route already exists. (Default: Defa .EXAMPLE Add-PodeSignalRouteGroup -Path '/signals' -Routes { Add-PodeSignalRoute -Path '/signal1' -Etc } #> -function Add-PodeSignalRouteGroup -{ +function Add-PodeSignalRouteGroup { [CmdletBinding()] param( [Parameter()] [string] $Path, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [scriptblock] $Routes, @@ -1147,11 +1469,12 @@ function Add-PodeSignalRouteGroup [Parameter()] [ValidateSet('Default', 'Error', 'Overwrite', 'Skip')] + [string] $IfExists = 'Default' ) if (Test-PodeIsEmpty $Routes) { - throw "No scriptblock for -Routes passed" + throw 'No scriptblock for -Routes passed' } if ($Path -eq '/') { @@ -1171,15 +1494,15 @@ function Add-PodeSignalRouteGroup $EndpointName = $RouteGroup.EndpointName } - if ($IfExists -ieq 'default') { - $IfExists = Get-PodeRouteIfExistsPreference + if ($RouteGroup.IfExists -ine 'default') { + $IfExists = $RouteGroup.IfExists } } $RouteGroup = @{ - Path = $Path + Path = $Path EndpointName = $EndpointName - IfExists = $IfExists + IfExists = $IfExists } # add routes @@ -1209,16 +1532,15 @@ Remove-PodeRoute -Method Get -Route '/about' .EXAMPLE Remove-PodeRoute -Method Post -Route '/users/:userId' -EndpointName User #> -function Remove-PodeRoute -{ +function Remove-PodeRoute { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [ValidateSet('Connect', 'Delete', 'Get', 'Head', 'Merge', 'Options', 'Patch', 'Post', 'Put', 'Trace', '*')] [string] $Method, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Path, @@ -1244,8 +1566,8 @@ function Remove-PodeRoute # remove the route's logic $PodeContext.Server.Routes[$Method][$Path] = @($PodeContext.Server.Routes[$Method][$Path] | Where-Object { - $_.Endpoint.Name -ine $EndpointName - }) + $_.Endpoint.Name -ine $EndpointName + }) # if the route has no more logic, just remove it if ((Get-PodeCount $PodeContext.Server.Routes[$Method][$Path]) -eq 0) { @@ -1269,11 +1591,10 @@ The EndpointName of an Endpoint(s) bound to the static Route to be removed. .EXAMPLE Remove-PodeStaticRoute -Path '/assets' #> -function Remove-PodeStaticRoute -{ +function Remove-PodeStaticRoute { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Path, @@ -1294,8 +1615,8 @@ function Remove-PodeStaticRoute # remove the route's logic $PodeContext.Server.Routes[$Method][$Path] = @($PodeContext.Server.Routes[$Method][$Path] | Where-Object { - $_.Endpoint.Name -ine $EndpointName - }) + $_.Endpoint.Name -ine $EndpointName + }) # if the route has no more logic, just remove it if ((Get-PodeCount $PodeContext.Server.Routes[$Method][$Path]) -eq 0) { @@ -1319,11 +1640,10 @@ The EndpointName of an Endpoint(s) bound to the Signal Route to be removed. .EXAMPLE Remove-PodeSignalRoute -Route '/message' #> -function Remove-PodeSignalRoute -{ +function Remove-PodeSignalRoute { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Path, @@ -1344,8 +1664,8 @@ function Remove-PodeSignalRoute # remove the route's logic $PodeContext.Server.Routes[$Method][$Path] = @($PodeContext.Server.Routes[$Method][$Path] | Where-Object { - $_.Endpoint.Name -ine $EndpointName - }) + $_.Endpoint.Name -ine $EndpointName + }) # if the route has no more logic, just remove it if ((Get-PodeCount $PodeContext.Server.Routes[$Method][$Path]) -eq 0) { @@ -1369,8 +1689,7 @@ Clear-PodeRoutes .EXAMPLE Clear-PodeRoutes -Method Get #> -function Clear-PodeRoutes -{ +function Clear-PodeRoutes { [CmdletBinding()] param( [Parameter()] @@ -1399,8 +1718,7 @@ Removes all added static Routes. .EXAMPLE Clear-PodeStaticRoutes #> -function Clear-PodeStaticRoutes -{ +function Clear-PodeStaticRoutes { [CmdletBinding()] param() @@ -1417,8 +1735,7 @@ Removes all added Signal Routes. .EXAMPLE Clear-PodeSignalRoutes #> -function Clear-PodeSignalRoutes -{ +function Clear-PodeSignalRoutes { [CmdletBinding()] param() @@ -1450,6 +1767,9 @@ Like normal Routes, an array of Middleware that will be applied to all generated .PARAMETER Authentication The name of an Authentication method which should be used as middleware on this Route. +.PARAMETER Access +The name of an Access method which should be used as middleware on this Route. + .PARAMETER AllowAnon If supplied, the Route will allow anonymous access for non-authenticated users. @@ -1459,6 +1779,18 @@ If supplied, the Command's Verb will not be included in the Route's path. .PARAMETER NoOpenApi If supplied, no OpenAPI definitions will be generated for the routes created. +.PARAMETER Role +One or more optional Roles that will be authorised to access this Route, when using Authentication with an Access method. + +.PARAMETER Group +One or more optional Groups that will be authorised to access this Route, when using Authentication with an Access method. + +.PARAMETER Scope +One or more optional Scopes that will be authorised to access this Route, when using Authentication with an Access method. + +.PARAMETER User +One or more optional Users that will be authorised to access this Route, when using Authentication with an Access method. + .EXAMPLE ConvertTo-PodeRoute -Commands @('Get-ChildItem', 'Get-Host', 'Invoke-Expression') -Middleware { ... } @@ -1471,11 +1803,10 @@ ConvertTo-PodeRoute -Module Pester -Path '/api' .EXAMPLE ConvertTo-PodeRoute -Commands @('Invoke-Pester') -Module Pester #> -function ConvertTo-PodeRoute -{ +function ConvertTo-PodeRoute { [CmdletBinding()] param( - [Parameter(ValueFromPipeline=$true)] + [Parameter(ValueFromPipeline = $true)] [string[]] $Commands, @@ -1501,6 +1832,26 @@ function ConvertTo-PodeRoute [string] $Authentication, + [Parameter()] + [string] + $Access, + + [Parameter()] + [string[]] + $Role, + + [Parameter()] + [string[]] + $Group, + + [Parameter()] + [string[]] + $Scope, + + [Parameter()] + [string[]] + $User, + [switch] $AllowAnon, @@ -1515,7 +1866,7 @@ function ConvertTo-PodeRoute if (![string]::IsNullOrWhiteSpace($Module)) { Import-PodeModule -Name $Module - Write-Verbose "Getting exported commands from module" + Write-Verbose 'Getting exported commands from module' $ModuleCommands = (Get-Module -Name $Module | Sort-Object -Descending | Select-Object -First 1).ExportedCommands.Keys # if commands were supplied validate them - otherwise use all exported ones @@ -1572,7 +1923,22 @@ function ConvertTo-PodeRoute $_path = ("$($Path)/$($Module)/$($name)" -replace '[/]+', '/') # create the route - $route = (Add-PodeRoute -Method $_method -Path $_path -Middleware $Middleware -Authentication $Authentication -AllowAnon:$AllowAnon -ArgumentList $cmd -ScriptBlock { + $params = @{ + Method = $_method + Path = $_path + Middleware = $Middleware + Authentication = $Authentication + Access = $Access + Role = $Role + Group = $Group + Scope = $Scope + User = $User + AllowAnon = $AllowAnon + ArgumentList = $cmd + PassThru = $true + } + + $route = Add-PodeRoute @params -ScriptBlock { param($cmd) # either get params from the QueryString or Payload @@ -1590,7 +1956,7 @@ function ConvertTo-PodeRoute if (!(Test-PodeIsEmpty $result)) { Write-PodeJsonResponse -Value $result -Depth 1 } - } -PassThru) + } # set the openapi metadata of the function, unless told to skip if ($NoOpenApi) { @@ -1607,8 +1973,8 @@ function ConvertTo-PodeRoute } $props = @(foreach ($key in $params.Keys) { - $params[$key] | ConvertTo-PodeOAPropertyFromCmdletParameter - }) + $params[$key] | ConvertTo-PodeOAPropertyFromCmdletParameter + }) if ($_method -ieq 'get') { $route | Set-PodeOARequest -Parameters @(foreach ($prop in $props) { $prop | ConvertTo-PodeOAParameter -In Query }) @@ -1654,12 +2020,27 @@ Like normal Routes, an array of Middleware that will be applied to all generated .PARAMETER Authentication The name of an Authentication method which should be used as middleware on this Route. +.PARAMETER Access +The name of an Access method which should be used as middleware on this Route. + .PARAMETER AllowAnon If supplied, the Page will allow anonymous access for non-authenticated users. .PARAMETER FlashMessages If supplied, Views will have any flash messages supplied to them for rendering. +.PARAMETER Role +One or more optional Roles that will be authorised to access this Route, when using Authentication with an Access method. + +.PARAMETER Group +One or more optional Groups that will be authorised to access this Route, when using Authentication with an Access method. + +.PARAMETER Scope +One or more optional Scopes that will be authorised to access this Route, when using Authentication with an Access method. + +.PARAMETER User +One or more optional Users that will be authorised to access this Route, when using Authentication with an Access method. + .EXAMPLE Add-PodePage -Name Services -ScriptBlock { Get-Service } @@ -1669,24 +2050,23 @@ Add-PodePage -Name Index -View 'index' .EXAMPLE Add-PodePage -Name About -FilePath '.\views\about.pode' -Data @{ Date = [DateTime]::UtcNow } #> -function Add-PodePage -{ - [CmdletBinding(DefaultParameterSetName='ScriptBlock')] +function Add-PodePage { + [CmdletBinding(DefaultParameterSetName = 'ScriptBlock')] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $Name, - [Parameter(Mandatory=$true, ParameterSetName='ScriptBlock')] + [Parameter(Mandatory = $true, ParameterSetName = 'ScriptBlock')] [scriptblock] $ScriptBlock, - [Parameter(Mandatory=$true, ParameterSetName='File')] + [Parameter(Mandatory = $true, ParameterSetName = 'File')] [string] $FilePath, - [Parameter(Mandatory=$true, ParameterSetName='View')] + [Parameter(Mandatory = $true, ParameterSetName = 'View')] [string] $View, @@ -1707,10 +2087,30 @@ function Add-PodePage [string] $Authentication, + [Parameter()] + [string] + $Access, + + [Parameter()] + [string[]] + $Role, + + [Parameter()] + [string[]] + $Group, + + [Parameter()] + [string[]] + $Scope, + + [Parameter()] + [string[]] + $User, + [switch] $AllowAnon, - [Parameter(ParameterSetName='View')] + [Parameter(ParameterSetName = 'View')] [switch] $FlashMessages ) @@ -1728,10 +2128,9 @@ function Add-PodePage $Path = $Path.TrimEnd('/') # define the appropriate logic - switch ($PSCmdlet.ParameterSetName.ToLowerInvariant()) - { + switch ($PSCmdlet.ParameterSetName.ToLowerInvariant()) { 'scriptblock' { - if (Test-PodeIsEmpty $ScriptBlock){ + if (Test-PodeIsEmpty $ScriptBlock) { throw 'A non-empty ScriptBlock is required to created a Page Route' } @@ -1776,14 +2175,22 @@ function Add-PodePage $_path = ("$($Path)/$($Name)" -replace '[/]+', '/') # create the route - Add-PodeRoute ` - -Method Get ` - -Path $_path ` - -Middleware $Middleware ` - -Authentication $Authentication ` - -AllowAnon:$AllowAnon ` - -ArgumentList $arg ` - -ScriptBlock $logic + $params = @{ + Method = 'Get' + Path = $_path + Middleware = $Middleware + Authentication = $Authentication + Access = $Access + Role = $Role + Group = $Group + Scope = $Scope + User = $User + AllowAnon = $AllowAnon + ArgumentList = $arg + ScriptBlock = $logic + } + + Add-PodeRoute @params } <# @@ -1808,8 +2215,7 @@ Get-PodeRoute -Method Get -Path '/about' .EXAMPLE Get-PodeRoute -Method Post -Path '/users/:userId' -EndpointName User #> -function Get-PodeRoute -{ +function Get-PodeRoute { [CmdletBinding()] param( [Parameter()] @@ -1835,12 +2241,12 @@ function Get-PodeRoute # if we have a method, filter if (![string]::IsNullOrWhiteSpace($Method)) { $routes = @(foreach ($route in $routes) { - if ($route.Method -ine $Method) { - continue - } + if ($route.Method -ine $Method) { + continue + } - $route - }) + $route + }) } # if we have a path, filter @@ -1850,25 +2256,25 @@ function Get-PodeRoute $Path = Resolve-PodePlaceholders -Path $Path $routes = @(foreach ($route in $routes) { - if ($route.Path -ine $Path) { - continue - } + if ($route.Path -ine $Path) { + continue + } - $route - }) + $route + }) } # further filter by endpoint names if (($null -ne $EndpointName) -and ($EndpointName.Length -gt 0)) { $routes = @(foreach ($name in $EndpointName) { - foreach ($route in $routes) { - if ($route.Endpoint.Name -ine $name) { - continue - } + foreach ($route in $routes) { + if ($route.Endpoint.Name -ine $name) { + continue + } - $route - } - }) + $route + } + }) } # return @@ -1894,8 +2300,7 @@ Get-PodeStaticRoute -Path '/assets' .EXAMPLE Get-PodeStaticRoute -Path '/assets' -EndpointName User #> -function Get-PodeStaticRoute -{ +function Get-PodeStaticRoute { [CmdletBinding()] param( [Parameter()] @@ -1917,25 +2322,25 @@ function Get-PodeStaticRoute if (![string]::IsNullOrWhiteSpace($Path)) { $Path = Update-PodeRouteSlashes -Path $Path -Static $routes = @(foreach ($route in $routes) { - if ($route.Path -ine $Path) { - continue - } + if ($route.Path -ine $Path) { + continue + } - $route - }) + $route + }) } # further filter by endpoint names if (($null -ne $EndpointName) -and ($EndpointName.Length -gt 0)) { $routes = @(foreach ($name in $EndpointName) { - foreach ($route in $routes) { - if ($route.Endpoint.Name -ine $name) { - continue - } + foreach ($route in $routes) { + if ($route.Endpoint.Name -ine $name) { + continue + } - $route - } - }) + $route + } + }) } # return @@ -1958,8 +2363,7 @@ The name of an endpoint to filter signal routes. .EXAMPLE Get-PodeSignalRoute -Path '/message' #> -function Get-PodeSignalRoute -{ +function Get-PodeSignalRoute { [CmdletBinding()] param( [Parameter()] @@ -1981,25 +2385,25 @@ function Get-PodeSignalRoute if (![string]::IsNullOrWhiteSpace($Path)) { $Path = Update-PodeRouteSlashes -Path $Path $routes = @(foreach ($route in $routes) { - if ($route.Path -ine $Path) { - continue - } + if ($route.Path -ine $Path) { + continue + } - $route - }) + $route + }) } # further filter by endpoint names if (($null -ne $EndpointName) -and ($EndpointName.Length -gt 0)) { $routes = @(foreach ($name in $EndpointName) { - foreach ($route in $routes) { - if ($route.Endpoint.Name -ine $name) { - continue - } + foreach ($route in $routes) { + if ($route.Endpoint.Name -ine $name) { + continue + } - $route - } - }) + $route + } + }) } # return @@ -2025,8 +2429,7 @@ Use-PodeRoutes .EXAMPLE Use-PodeRoutes -Path './my-routes' -IfExists Skip #> -function Use-PodeRoutes -{ +function Use-PodeRoutes { [CmdletBinding()] param( [Parameter()] @@ -2035,6 +2438,7 @@ function Use-PodeRoutes [Parameter()] [ValidateSet('Default', 'Error', 'Overwrite', 'Skip')] + [string] $IfExists = 'Default' ) @@ -2059,12 +2463,12 @@ Specifies what action to take when a Route already exists. (Default: Default) .EXAMPLE Set-PodeRouteIfExistsPreference -Value Overwrite #> -function Set-PodeRouteIfExistsPreference -{ +function Set-PodeRouteIfExistsPreference { [CmdletBinding()] param( [Parameter()] [ValidateSet('Default', 'Error', 'Overwrite', 'Skip')] + [string] $Value = 'Default' ) @@ -2099,16 +2503,15 @@ Test-PodeRoute -Method Post -Path '/example' -CheckWildcard .EXAMPLE Test-PodeRoute -Method Get -Path '/example/:exampleId' -CheckWildcard #> -function Test-PodeRoute -{ +function Test-PodeRoute { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [ValidateSet('Connect', 'Delete', 'Get', 'Head', 'Merge', 'Options', 'Patch', 'Post', 'Put', 'Trace', '*')] [string] $Method, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Path, @@ -2123,7 +2526,7 @@ function Test-PodeRoute # split route on '?' for query $Path = Split-PodeRouteQuery -Path $Path if ([string]::IsNullOrWhiteSpace($Path)) { - throw "No Path supplied for testing Route" + throw 'No Path supplied for testing Route' } # ensure the route has appropriate slashes @@ -2158,11 +2561,10 @@ The EndpointName of an Endpoint the Static Route is bound against. .EXAMPLE Test-PodeStaticRoute -Path '/assets' #> -function Test-PodeStaticRoute -{ +function Test-PodeStaticRoute { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Path, @@ -2177,7 +2579,7 @@ function Test-PodeStaticRoute # split route on '?' for query $Path = Split-PodeRouteQuery -Path $Path if ([string]::IsNullOrWhiteSpace($Path)) { - throw "No Path supplied for testing Static Route" + throw 'No Path supplied for testing Static Route' } # ensure the route has appropriate slashes @@ -2207,11 +2609,10 @@ The EndpointName of an Endpoint the Signal Route is bound against. .EXAMPLE Test-PodeSignalRoute -Path '/message' #> -function Test-PodeSignalRoute -{ +function Test-PodeSignalRoute { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Path, diff --git a/src/Public/Schedules.ps1 b/src/Public/Schedules.ps1 index 3c7cfa1cc..e88b577c4 100644 --- a/src/Public/Schedules.ps1 +++ b/src/Public/Schedules.ps1 @@ -44,19 +44,18 @@ Add-PodeSchedule -Name 'StartAfter2days' -Cron '@hourly' -StartTime [DateTime]:: .EXAMPLE Add-PodeSchedule -Name 'Args' -Cron '@minutely' -ScriptBlock { /* logic */ } -ArgumentList @{ Arg1 = 'value' } #> -function Add-PodeSchedule -{ - [CmdletBinding(DefaultParameterSetName='Script')] +function Add-PodeSchedule { + [CmdletBinding(DefaultParameterSetName = 'Script')] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string[]] $Cron, - [Parameter(Mandatory=$true, ParameterSetName='Script')] + [Parameter(Mandatory = $true, ParameterSetName = 'Script')] [scriptblock] $ScriptBlock, @@ -72,7 +71,7 @@ function Add-PodeSchedule [DateTime] $EndTime, - [Parameter(Mandatory=$true, ParameterSetName='File')] + [Parameter(Mandatory = $true, ParameterSetName = 'File')] [string] $FilePath, @@ -120,20 +119,20 @@ function Add-PodeSchedule $PodeContext.Schedules.Enabled = $true $PodeContext.Schedules.Items[$Name] = @{ - Name = $Name - StartTime = $StartTime - EndTime = $EndTime - Crons = $parsedCrons - CronsRaw = @($Cron) - Limit = $Limit - Count = 0 + Name = $Name + StartTime = $StartTime + EndTime = $EndTime + Crons = $parsedCrons + CronsRaw = @($Cron) + Limit = $Limit + Count = 0 NextTriggerTime = $nextTrigger LastTriggerTime = $null - Script = $ScriptBlock - UsingVariables = $usingVars - Arguments = (Protect-PodeValue -Value $ArgumentList -Default @{}) - OnStart = $OnStart - Completed = ($null -eq $nextTrigger) + Script = $ScriptBlock + UsingVariables = $usingVars + Arguments = (Protect-PodeValue -Value $ArgumentList -Default @{}) + OnStart = $OnStart + Completed = ($null -eq $nextTrigger) } } @@ -150,11 +149,10 @@ The Maximum number of schedules to run. .EXAMPLE Set-PodeScheduleConcurrency -Maximum 25 #> -function Set-PodeScheduleConcurrency -{ +function Set-PodeScheduleConcurrency { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [int] $Maximum ) @@ -197,11 +195,10 @@ A hashtable of arguments to supply to the Schedule's ScriptBlock. .EXAMPLE Invoke-PodeSchedule -Name 'schedule-name' #> -function Invoke-PodeSchedule -{ +function Invoke-PodeSchedule { [CmdletBinding()] param( - [Parameter(Mandatory=$true, ValueFromPipeline=$true)] + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [string] $Name, @@ -232,11 +229,10 @@ The Name of the Schedule to be removed. .EXAMPLE Remove-PodeSchedule -Name 'RenewToken' #> -function Remove-PodeSchedule -{ +function Remove-PodeSchedule { [CmdletBinding()] - param ( - [Parameter(Mandatory=$true, ValueFromPipeline=$true)] + param( + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [string] $Name ) @@ -254,8 +250,7 @@ Removes all Schedules. .EXAMPLE Clear-PodeSchedules #> -function Clear-PodeSchedules -{ +function Clear-PodeSchedules { [CmdletBinding()] param() @@ -287,11 +282,10 @@ Edit-PodeSchedule -Name 'Hello' -Cron '@minutely' .EXAMPLE Edit-PodeSchedule -Name 'Hello' -Cron @('@hourly', '0 0 * * TUE') #> -function Edit-PodeSchedule -{ +function Edit-PodeSchedule { [CmdletBinding()] param( - [Parameter(Mandatory=$true, ValueFromPipeline=$true)] + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [string] $Name, @@ -360,8 +354,7 @@ Get-PodeSchedule -Name Name1, Name2 .EXAMPLE Get-PodeSchedule -Name Name1, Name2 -StartTime [datetime]::new(2020, 3, 1) -EndTime [datetime]::new(2020, 3, 31) #> -function Get-PodeSchedule -{ +function Get-PodeSchedule { [CmdletBinding()] param( [Parameter()] @@ -380,58 +373,58 @@ function Get-PodeSchedule # further filter by schedule names if (($null -ne $Name) -and ($Name.Length -gt 0)) { $schedules = @(foreach ($_name in $Name) { - foreach ($schedule in $schedules) { - if ($schedule.Name -ine $_name) { - continue - } + foreach ($schedule in $schedules) { + if ($schedule.Name -ine $_name) { + continue + } - $schedule - } - }) + $schedule + } + }) } # filter by some start time if ($null -ne $StartTime) { $schedules = @(foreach ($schedule in $schedules) { - if (($null -ne $schedule.StartTime) -and ($StartTime -lt $schedule.StartTime)) { - continue - } + if (($null -ne $schedule.StartTime) -and ($StartTime -lt $schedule.StartTime)) { + continue + } - $_end = $EndTime - if ($null -eq $_end) { - $_end = $schedule.EndTime - } + $_end = $EndTime + if ($null -eq $_end) { + $_end = $schedule.EndTime + } - if (($null -ne $schedule.EndTime) -and + if (($null -ne $schedule.EndTime) -and (($StartTime -gt $schedule.EndTime) -or ((Get-PodeScheduleNextTrigger -Name $schedule.Name -DateTime $StartTime) -gt $_end))) { - continue - } + continue + } - $schedule - }) + $schedule + }) } # filter by some end time if ($null -ne $EndTime) { $schedules = @(foreach ($schedule in $schedules) { - if (($null -ne $schedule.EndTime) -and ($EndTime -gt $schedule.EndTime)) { - continue - } + if (($null -ne $schedule.EndTime) -and ($EndTime -gt $schedule.EndTime)) { + continue + } - $_start = $StartTime - if ($null -eq $_start) { - $_start = $schedule.StartTime - } + $_start = $StartTime + if ($null -eq $_start) { + $_start = $schedule.StartTime + } - if (($null -ne $schedule.StartTime) -and + if (($null -ne $schedule.StartTime) -and (($EndTime -lt $schedule.StartTime) -or ((Get-PodeScheduleNextTrigger -Name $schedule.Name -DateTime $_start) -gt $EndTime))) { - continue - } + continue + } - $schedule - }) + $schedule + }) } # return @@ -451,11 +444,10 @@ The Name of the Schedule. .EXAMPLE if (Test-PodeSchedule -Name ScheduleName) { } #> -function Test-PodeSchedule -{ +function Test-PodeSchedule { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name ) @@ -482,11 +474,10 @@ Get-PodeScheduleNextTrigger -Name Schedule1 .EXAMPLE Get-PodeScheduleNextTrigger -Name Schedule1 -DateTime [datetime]::new(2020, 3, 10) #> -function Get-PodeScheduleNextTrigger -{ +function Get-PodeScheduleNextTrigger { [CmdletBinding()] param( - [Parameter(Mandatory=$true, ValueFromPipeline=$true)] + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [string] $Name, @@ -534,8 +525,7 @@ Use-PodeSchedules .EXAMPLE Use-PodeSchedules -Path './my-schedules' #> -function Use-PodeSchedules -{ +function Use-PodeSchedules { [CmdletBinding()] param( [Parameter()] diff --git a/src/Public/Secrets.ps1 b/src/Public/Secrets.ps1 index bcfe71cd7..ca7eb47c5 100644 --- a/src/Public/Secrets.ps1 +++ b/src/Public/Secrets.ps1 @@ -56,11 +56,10 @@ Register-PodeSecretVault -Name 'VaultName' -ModuleName 'Az.KeyVault' -VaultParam .EXAMPLE Register-PodeSecretVault -Name 'VaultName' -VaultParameters @{ Address = 'http://127.0.0.1:8200' } -ScriptBlock { ... } #> -function Register-PodeSecretVault -{ +function Register-PodeSecretVault { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name, @@ -91,35 +90,35 @@ function Register-PodeSecretVault [scriptblock] $InitScriptBlock, - [Parameter(ParameterSetName='SecretManagement')] + [Parameter(ParameterSetName = 'SecretManagement')] [string] $VaultName, - [Parameter(Mandatory=$true, ParameterSetName='SecretManagement')] + [Parameter(Mandatory = $true, ParameterSetName = 'SecretManagement')] [Alias('Module')] [string] $ModuleName, - [Parameter(Mandatory=$true, ParameterSetName='Custom')] + [Parameter(Mandatory = $true, ParameterSetName = 'Custom')] [scriptblock] $ScriptBlock, # Read a secret - [Parameter(ParameterSetName='Custom')] + [Parameter(ParameterSetName = 'Custom')] [Alias('Unlock')] [scriptblock] $UnlockScriptBlock, - [Parameter(ParameterSetName='Custom')] + [Parameter(ParameterSetName = 'Custom')] [Alias('Remove')] [scriptblock] $RemoveScriptBlock, - [Parameter(ParameterSetName='Custom')] + [Parameter(ParameterSetName = 'Custom')] [Alias('Set')] [scriptblock] $SetScriptBlock, - [Parameter(ParameterSetName='Custom')] + [Parameter(ParameterSetName = 'Custom')] [Alias('Unregister')] [scriptblock] $UnregisterScriptBlock @@ -141,18 +140,18 @@ function Register-PodeSecretVault } $vault = @{ - Name = $Name - Type = $PSCmdlet.ParameterSetName.ToLowerInvariant() - Parameters = $VaultParameters + Name = $Name + Type = $PSCmdlet.ParameterSetName.ToLowerInvariant() + Parameters = $VaultParameters AutoImported = $false - Unlock = @{ - Secret = $UnlockSecureSecret - Expiry = $null + Unlock = @{ + Secret = $UnlockSecureSecret + Expiry = $null Interval = $UnlockInterval - Enabled = (!(Test-PodeIsEmpty $UnlockSecureSecret)) + Enabled = (!(Test-PodeIsEmpty $UnlockSecureSecret)) } - Cache = @{ - Ttl = $CacheTtl + Cache = @{ + Ttl = $CacheTtl Enabled = ($CacheTtl -gt 0) } } @@ -205,11 +204,10 @@ The Name of the Secret Vault in Pode to unregister. .EXAMPLE Unregister-PodeSecretVault -Name 'VaultName' #> -function Unregister-PodeSecretVault -{ +function Unregister-PodeSecretVault { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name ) @@ -250,10 +248,9 @@ The Name of the Secret Vault in Pode to be unlocked. .EXAMPLE Unlock-PodeSecretVault -Name 'VaultName' #> -function Unlock-PodeSecretVault -{ +function Unlock-PodeSecretVault { param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name ) @@ -311,11 +308,10 @@ $vault = Get-PodeSecretVault -Name 'VaultName' .EXAMPLE $vaults = Get-PodeSecretVault -Name 'VaultName1', 'VaultName2' #> -function Get-PodeSecretVault -{ +function Get-PodeSecretVault { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string[]] $Name ) @@ -325,14 +321,14 @@ function Get-PodeSecretVault # further filter by vault names if (($null -ne $Name) -and ($Name.Length -gt 0)) { $vaults = @(foreach ($_name in $Name) { - foreach ($vault in $vaults) { - if ($vault.Name -ine $_name) { - continue - } + foreach ($vault in $vaults) { + if ($vault.Name -ine $_name) { + continue + } - $vault - } - }) + $vault + } + }) } # return @@ -352,11 +348,10 @@ The Name of the Secret Vault to test. .EXAMPLE if (Test-PodeSecretVault -Name 'VaultName') { ... } #> -function Test-PodeSecretVault -{ +function Test-PodeSecretVault { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name ) @@ -399,15 +394,14 @@ Mount-PodeSecret -Name 'SecretName' -Vault 'VaultName' -Key 'path/to/secret' -Ex .EXAMPLE Mount-PodeSecret -Name 'SecretName' -Vault 'VaultName' -Key 'key_of_secret' -CacheTtl 5 #> -function Mount-PodeSecret -{ +function Mount-PodeSecret { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Vault, @@ -419,7 +413,7 @@ function Mount-PodeSecret [string] $ExpandProperty, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Key, @@ -460,16 +454,16 @@ function Mount-PodeSecret } $PodeContext.Server.Secrets.Keys[$Name] = @{ - Key = $Key + Key = $Key Properties = @{ - Fields = $props - Expand = (![string]::IsNullOrWhiteSpace($ExpandProperty)) + Fields = $props + Expand = (![string]::IsNullOrWhiteSpace($ExpandProperty)) Enabled = (!(Test-PodeIsEmpty $props)) } - Vault = $Vault - Arguments = $ArgumentList - Cache = @{ - Ttl = $CacheTtl + Vault = $Vault + Arguments = $ArgumentList + Cache = @{ + Ttl = $CacheTtl Enabled = ($CacheTtl -gt 0) } } @@ -494,11 +488,10 @@ Dismount-PodeSecret -Name 'SecretName' .EXAMPLE Dismount-PodeSecret -Name 'SecretName' -Remove #> -function Dismount-PodeSecret -{ +function Dismount-PodeSecret { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name, @@ -541,11 +534,10 @@ $value = Get-PodeSecret -Name 'SecretName' .EXAMPLE $value = $secret:SecretName #> -function Get-PodeSecret -{ +function Get-PodeSecret { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name ) @@ -577,7 +569,7 @@ function Get-PodeSecret # filter the value by any properties if ($secret.Properties.Enabled) { if ($secret.Properties.Expand) { - $value = Select-Object -InputObject $value -ExpandProperty $secret.Properties.Fields + $value = Select-Object -InputObject $value -ExpandProperty $secret.Properties.Fields } else { $value = Select-Object -InputObject $value -Property $secret.Properties.Fields @@ -607,11 +599,10 @@ The friendly Name of a Secret. .EXAMPLE if (Test-PodeSecret -Name 'SecretName') { ... } #> -function Test-PodeSecret -{ +function Test-PodeSecret { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name ) @@ -645,16 +636,15 @@ Update-PodeSecret -Name 'SecretName' -InputObject 'value' .EXAMPLE $secret:SecretName = 'value' #> -function Update-PodeSecret -{ +function Update-PodeSecret { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name, #> byte[], string, securestring, pscredential, hashtable - [Parameter(Mandatory=$true, ValueFromPipeline=$true)] + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [object] $InputObject, @@ -718,15 +708,14 @@ An optional array of Arguments to be supplied to a custom Secret Vault's scriptb .EXAMPLE Remove-PodeSecret -Key 'path/to/secret' -Vault 'VaultName' #> -function Remove-PodeSecret -{ +function Remove-PodeSecret { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Key, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Vault, @@ -780,15 +769,14 @@ $value = Read-PodeSecret -Key 'path/to/secret' -Vault 'VaultName' .EXAMPLE $value = Read-PodeSecret -Key 'key_of_secret' -Vault 'VaultName' -Property prop1, prop2 #> -function Read-PodeSecret -{ +function Read-PodeSecret { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Key, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Vault, @@ -823,7 +811,7 @@ function Read-PodeSecret # filter the value by any properties if (![string]::IsNullOrWhiteSpace($ExpandProperty)) { - $value = Select-Object -InputObject $value -ExpandProperty $ExpandProperty + $value = Select-Object -InputObject $value -ExpandProperty $ExpandProperty } elseif (![string]::IsNullOrEmpty($Property)) { $value = Select-Object -InputObject $value -Property $Property @@ -862,20 +850,19 @@ Set-PodeSecret -Key 'path/to/secret' -Vault 'VaultName' -InputObject 'value' .EXAMPLE Set-PodeSecret -Key 'key_of_secret' -Vault 'VaultName' -InputObject @{ key = value } #> -function Set-PodeSecret -{ +function Set-PodeSecret { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Key, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Vault, #> byte[], string, securestring, pscredential, hashtable - [Parameter(Mandatory=$true, ValueFromPipeline=$true)] + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [object] $InputObject, diff --git a/src/Public/Security.ps1 b/src/Public/Security.ps1 index 04c233375..5d5190014 100644 --- a/src/Public/Security.ps1 +++ b/src/Public/Security.ps1 @@ -20,11 +20,10 @@ Set-PodeSecurity -Type Simple .EXAMPLE Set-PodeSecurity -Type Strict -UseHsts #> -function Set-PodeSecurity -{ +function Set-PodeSecurity { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [ValidateSet('Simple', 'Strict')] [string] $Type, @@ -75,6 +74,9 @@ function Set-PodeSecurity Set-PodeSecurityReferrerPolicy -Type No-Referrer } } + + # hide server info + Hide-PodeSecurityServer } <# @@ -87,12 +89,12 @@ Removes definitions for all security headers. .EXAMPLE Remove-PodeSecurity #> -function Remove-PodeSecurity -{ +function Remove-PodeSecurity { [CmdletBinding()] param() $PodeContext.Server.Security.Headers.Clear() + Show-PodeSecurityServer } <# @@ -111,11 +113,10 @@ The Value of the security header. .EXAMPLE Add-PodeSecurityHeader -Name 'X-Header-Name' -Value 'SomeValue' #> -function Add-PodeSecurityHeader -{ +function Add-PodeSecurityHeader { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name, @@ -142,11 +143,10 @@ The Name of the security header. .EXAMPLE Remove-PodeSecurityHeader -Name 'X-Header-Name' #> -function Remove-PodeSecurityHeader -{ +function Remove-PodeSecurityHeader { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name ) @@ -154,6 +154,40 @@ function Remove-PodeSecurityHeader $PodeContext.Server.Security.Headers.Remove($Name) } +<# +.SYNOPSIS +Hide the Server HTTP Header from Responses + +.DESCRIPTION +Hide the Server HTTP Header from Responses + +.EXAMPLE +Hide-PodeSecurityServer +#> +function Hide-PodeSecurityServer { + [CmdletBinding()] + param() + + $PodeContext.Server.Security.ServerDetails = $false +} + +<# +.SYNOPSIS +Show the Server HTTP Header on Responses + +.DESCRIPTION +Show the Server HTTP Header on Responses + +.EXAMPLE +Show-PodeSecurityServer +#> +function Show-PodeSecurityServer { + [CmdletBinding()] + param() + + $PodeContext.Server.Security.ServerDetails = $true +} + <# .SYNOPSIS Set a value for the X-Frame-Options header. @@ -167,11 +201,10 @@ The Type to use. .EXAMPLE Set-PodeSecurityFrameOptions -Type SameOrigin #> -function Set-PodeSecurityFrameOptions -{ +function Set-PodeSecurityFrameOptions { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [ValidateSet('Deny', 'SameOrigin')] [string] $Type @@ -190,8 +223,7 @@ Removes definition for the X-Frame-Options header. .EXAMPLE Remove-PodeSecurityFrameOptions #> -function Remove-PodeSecurityFrameOptions -{ +function Remove-PodeSecurityFrameOptions { [CmdletBinding()] param() @@ -259,8 +291,7 @@ If supplied, the X-XSS-Protection header will be set to blocking mode. (Default: .EXAMPLE Set-PodeSecurityContentSecurityPolicy -Default 'self' #> -function Set-PodeSecurityContentSecurityPolicy -{ +function Set-PodeSecurityContentSecurityPolicy { [CmdletBinding()] param( [Parameter()] @@ -368,10 +399,10 @@ function Set-PodeSecurityContentSecurityPolicy # this is done to explicitly disable XSS auditors in modern browsers # as having it enabled has now been found to cause more vulnerabilities if ($XssBlock) { - Add-PodeSecurityHeader -Name 'X-XSS-Protection' -Value "1; mode=block" + Add-PodeSecurityHeader -Name 'X-XSS-Protection' -Value '1; mode=block' } else { - Add-PodeSecurityHeader -Name 'X-XSS-Protection' -Value "0" + Add-PodeSecurityHeader -Name 'X-XSS-Protection' -Value '0' } } @@ -433,8 +464,7 @@ If supplied, the header will have the upgrade-insecure-requests value added. .EXAMPLE Add-PodeSecurityContentSecurityPolicy -Default '*.twitter.com' -Image 'data' #> -function Add-PodeSecurityContentSecurityPolicy -{ +function Add-PodeSecurityContentSecurityPolicy { [CmdletBinding()] param( [Parameter()] @@ -547,8 +577,7 @@ Removes definition for the Content-Security-Policy and X-XSS-Protection headers. .EXAMPLE Remove-PodeSecurityContentSecurityPolicy #> -function Remove-PodeSecurityContentSecurityPolicy -{ +function Remove-PodeSecurityContentSecurityPolicy { [CmdletBinding()] param() @@ -656,8 +685,7 @@ The values to use for the XrSpatialTracking portion of the header. .EXAMPLE Set-PodeSecurityPermissionsPolicy -LayoutAnimations 'none' -UnoptimisedImages 'none' -OversizedImages 'none' -SyncXhr 'none' -UnsizedMedia 'none' #> -function Set-PodeSecurityPermissionsPolicy -{ +function Set-PodeSecurityPermissionsPolicy { [CmdletBinding()] param( [Parameter()] @@ -922,8 +950,7 @@ The values to add for the XrSpatialTracking portion of the header. .EXAMPLE Add-PodeSecurityPermissionsPolicy -AmbientLightSensor 'none' #> -function Add-PodeSecurityPermissionsPolicy -{ +function Add-PodeSecurityPermissionsPolicy { [CmdletBinding()] param( [Parameter()] @@ -1098,8 +1125,7 @@ Removes definitions for the Permissions-Policy header. .EXAMPLE Remove-PodeSecurityPermissionsPolicy #> -function Remove-PodeSecurityPermissionsPolicy -{ +function Remove-PodeSecurityPermissionsPolicy { [CmdletBinding()] param() @@ -1119,11 +1145,10 @@ The Type to use. .EXAMPLE Set-PodeSecurityReferrerPolicy -Type No-Referrer #> -function Set-PodeSecurityReferrerPolicy -{ +function Set-PodeSecurityReferrerPolicy { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [ValidateSet('No-Referrer', 'No-Referrer-When-Downgrade', 'Same-Origin', 'Origin', 'Strict-Origin', 'Origin-When-Cross-Origin', 'Strict-Origin-When-Cross-Origin', 'Unsafe-Url')] [string] @@ -1143,8 +1168,7 @@ Removes definitions for the Referrer-Policy header. .EXAMPLE Remove-PodeSecurityReferrerPolicy #> -function Remove-PodeSecurityReferrerPolicy -{ +function Remove-PodeSecurityReferrerPolicy { [CmdletBinding()] param() @@ -1161,8 +1185,7 @@ Set a value for the X-Content-Type-Options header to "nosniff". .EXAMPLE Set-PodeSecurityContentTypeOptions #> -function Set-PodeSecurityContentTypeOptions -{ +function Set-PodeSecurityContentTypeOptions { [CmdletBinding()] param() @@ -1179,8 +1202,7 @@ Removes definitions for the X-Content-Type-Options header. .EXAMPLE Remove-PodeSecurityContentTypeOptions #> -function Remove-PodeSecurityContentTypeOptions -{ +function Remove-PodeSecurityContentTypeOptions { [CmdletBinding()] param() @@ -1203,8 +1225,7 @@ If supplied, the header will have includeSubDomains. .EXAMPLE Set-PodeSecurityStrictTransportSecurity -Duration 86400 -IncludeSubDomains #> -function Set-PodeSecurityStrictTransportSecurity -{ +function Set-PodeSecurityStrictTransportSecurity { [CmdletBinding()] param( [Parameter()] @@ -1220,9 +1241,9 @@ function Set-PodeSecurityStrictTransportSecurity } $value = "max-age=$($Duration)" - + if ($IncludeSubDomains) { - $value += "; includeSubDomains" + $value += '; includeSubDomains' } Add-PodeSecurityHeader -Name 'Strict-Transport-Security' -Value $value @@ -1238,8 +1259,7 @@ Removes definitions for the Strict-Transport-Security header. .EXAMPLE Remove-PodeSecurityStrictTransportSecurity #> -function Remove-PodeSecurityStrictTransportSecurity -{ +function Remove-PodeSecurityStrictTransportSecurity { [CmdletBinding()] param() @@ -1265,8 +1285,7 @@ Specifies a value for Cross-Origin-Resource-Policy. .EXAMPLE Set-PodeSecurityCrossOrigin -Embed Require-Corp -Open Same-Origin -Resource Same-Origin #> -function Set-PodeSecurityCrossOrigin -{ +function Set-PodeSecurityCrossOrigin { [CmdletBinding()] param( [Parameter()] @@ -1300,8 +1319,7 @@ Removes definitions for the Cross-Origin headers: Cross-Origin-Embedder-Policy, .EXAMPLE Remove-PodeSecurityCrossOrigin #> -function Remove-PodeSecurityCrossOrigin -{ +function Remove-PodeSecurityCrossOrigin { [CmdletBinding()] param() @@ -1338,8 +1356,7 @@ If supplied, a global Options Route will be created. .EXAMPLE Set-PodeSecurityAccessControl -Origin '*' -Methods '*' -Headers '*' -Duration 7200 #> -function Set-PodeSecurityAccessControl -{ +function Set-PodeSecurityAccessControl { [CmdletBinding()] param( [Parameter()] @@ -1419,8 +1436,7 @@ Removes definitions for the Access-Control headers: Access-Control-Allow-Origin, .EXAMPLE Remove-PodeSecurityAccessControl #> -function Remove-PodeSecurityAccessControl -{ +function Remove-PodeSecurityAccessControl { [CmdletBinding()] param() diff --git a/src/Public/Sessions.ps1 b/src/Public/Sessions.ps1 index a96496a02..b8335452b 100644 --- a/src/Public/Sessions.ps1 +++ b/src/Public/Sessions.ps1 @@ -45,10 +45,9 @@ Enable-PodeSessionMiddleware -Duration 120 -Extend -Generator { return [System.I .EXAMPLE Enable-PodeSessionMiddleware -Secret 'schwifty' -Duration 120 -UseHeaders -Strict #> -function Enable-PodeSessionMiddleware -{ - [CmdletBinding(DefaultParameterSetName='Cookies')] - param ( +function Enable-PodeSessionMiddleware { + [CmdletBinding(DefaultParameterSetName = 'Cookies')] + param( [Parameter()] [string] $Secret, @@ -60,12 +59,12 @@ function Enable-PodeSessionMiddleware [Parameter()] [ValidateScript({ - if ($_ -lt 0) { - throw "Duration must be 0 or greater, but got: $($_)s" - } + if ($_ -lt 0) { + throw "Duration must be 0 or greater, but got: $($_)s" + } - return $true - })] + return $true + })] [int] $Duration = 0, @@ -80,18 +79,18 @@ function Enable-PodeSessionMiddleware [switch] $Extend, - [Parameter(ParameterSetName='Cookies')] + [Parameter(ParameterSetName = 'Cookies')] [switch] $HttpOnly, - [Parameter(ParameterSetName='Cookies')] + [Parameter(ParameterSetName = 'Cookies')] [switch] $Secure, [switch] $Strict, - [Parameter(ParameterSetName='Headers')] + [Parameter(ParameterSetName = 'Headers')] [switch] $UseHeaders ) @@ -114,7 +113,7 @@ function Enable-PodeSessionMiddleware # verify the secret, set to guid if not supplied, or error if none and we have a storage if ([string]::IsNullOrEmpty($Secret)) { if (!(Test-PodeIsEmpty $Storage)) { - throw "A Secret is required when using custom session storage" + throw 'A Secret is required when using custom session storage' } $Secret = New-PodeGuid -Secure @@ -128,16 +127,16 @@ function Enable-PodeSessionMiddleware # set options against server context $PodeContext.Server.Sessions = @{ - Name = $Name - Secret = $Secret + Name = $Name + Secret = $Secret GenerateId = (Protect-PodeValue -Value $Generator -Default { return (New-PodeGuid) }) - Store = $Storage - Info = @{ - Duration = $Duration - Extend = $Extend.IsPresent - Secure = $Secure.IsPresent - Strict = $Strict.IsPresent - HttpOnly = $HttpOnly.IsPresent + Store = $Storage + Info = @{ + Duration = $Duration + Extend = $Extend.IsPresent + Secure = $Secure.IsPresent + Strict = $Strict.IsPresent + HttpOnly = $HttpOnly.IsPresent UseHeaders = $UseHeaders.IsPresent } } @@ -157,8 +156,7 @@ Remove the current Session, logging it out. This will remove the session from St .EXAMPLE Remove-PodeSession #> -function Remove-PodeSession -{ +function Remove-PodeSession { [CmdletBinding()] param() @@ -189,8 +187,7 @@ If supplied, the data will be saved even if nothing has changed. .EXAMPLE Save-PodeSession -Force #> -function Save-PodeSession -{ +function Save-PodeSession { [CmdletBinding()] param( [switch] @@ -233,8 +230,7 @@ If supplied, the sessionId will be returned regardless of authentication. .EXAMPLE $sessionId = Get-PodeSessionId #> -function Get-PodeSessionId -{ +function Get-PodeSessionId { [CmdletBinding()] param( [switch] @@ -282,8 +278,7 @@ Resets the current Session's expiry date, to be from the current time plus the d .EXAMPLE Reset-PodeSessionExpiry #> -function Reset-PodeSessionExpiry -{ +function Reset-PodeSessionExpiry { [CmdletBinding()] param() @@ -314,8 +309,7 @@ Returns the defined Session duration that all Session are created using. .EXAMPLE $duration = Get-PodeSessionDuration #> -function Get-PodeSessionDuration -{ +function Get-PodeSessionDuration { [CmdletBinding()] param() @@ -332,8 +326,7 @@ Returns the datetime on which the current Session's will expire. .EXAMPLE $expiry = Get-PodeSessionExpiry #> -function Get-PodeSessionExpiry -{ +function Get-PodeSessionExpiry { [CmdletBinding()] param() diff --git a/src/Public/State.ps1 b/src/Public/State.ps1 index 79fd16896..34d8519bb 100644 --- a/src/Public/State.ps1 +++ b/src/Public/State.ps1 @@ -20,16 +20,15 @@ Set-PodeState -Name 'Data' -Value @{ 'Name' = 'Rick Sanchez' } .EXAMPLE Set-PodeState -Name 'Users' -Value @('user1', 'user2') -Scope General, Users #> -function Set-PodeState -{ +function Set-PodeState { [CmdletBinding()] [OutputType([object])] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name, - [Parameter(ValueFromPipeline=$true)] + [Parameter(ValueFromPipeline = $true)] [object] $Value, @@ -39,7 +38,7 @@ function Set-PodeState ) if ($null -eq $PodeContext.Server.State) { - throw "Pode has not been initialised" + throw 'Pode has not been initialised' } if ($null -eq $Scope) { @@ -70,11 +69,10 @@ If supplied, the state's value and scope will be returned as a hashtable. .EXAMPLE Get-PodeState -Name 'Data' #> -function Get-PodeState -{ +function Get-PodeState { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name, @@ -83,7 +81,7 @@ function Get-PodeState ) if ($null -eq $PodeContext.Server.State) { - throw "Pode has not been initialised" + throw 'Pode has not been initialised' } if ($WithScope) { @@ -113,8 +111,7 @@ $names = Get-PodeStateNames -Scope '' .EXAMPLE $names = Get-PodeStateNames -Pattern '^\w+[0-9]{0,2}$' #> -function Get-PodeStateNames -{ +function Get-PodeStateNames { [CmdletBinding()] param( [Parameter()] @@ -127,7 +124,7 @@ function Get-PodeStateNames ) if ($null -eq $PodeContext.Server.State) { - throw "Pode has not been initialised" + throw 'Pode has not been initialised' } if ($null -eq $Scope) { @@ -138,19 +135,19 @@ function Get-PodeStateNames $keys = $tempState.Keys if ($Scope.Length -gt 0) { - $keys = @(foreach($key in $keys) { - if ($tempState[$key].Scope -iin $Scope) { - $key - } - }) + $keys = @(foreach ($key in $keys) { + if ($tempState[$key].Scope -iin $Scope) { + $key + } + }) } if (![string]::IsNullOrWhiteSpace($Pattern)) { - $keys = @(foreach($key in $keys) { - if ($key -imatch $Pattern) { - $key - } - }) + $keys = @(foreach ($key in $keys) { + if ($key -imatch $Pattern) { + $key + } + }) } return $keys @@ -169,18 +166,17 @@ The name of the state object. .EXAMPLE Remove-PodeState -Name 'Data' #> -function Remove-PodeState -{ +function Remove-PodeState { [CmdletBinding()] [OutputType([object])] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name ) if ($null -eq $PodeContext.Server.State) { - throw "Pode has not been initialised" + throw 'Pode has not been initialised' } $value = $PodeContext.Server.State[$Name].Value @@ -207,6 +203,9 @@ An optional array of state object names to exclude from being saved. (This has a .PARAMETER Include An optional array of state object names to only include when being saved. +.PARAMETER Depth +Saved JSON maximum depth. Will be passed to ConvertTo-JSON's -Depth parameter. Default is 10. + .PARAMETER Compress If supplied, the saved JSON will be compressed. @@ -219,11 +218,10 @@ Save-PodeState -Path './state.json' -Exclude Name1, Name2 .EXAMPLE Save-PodeState -Path './state.json' -Scope Users #> -function Save-PodeState -{ +function Save-PodeState { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Path, @@ -239,13 +237,17 @@ function Save-PodeState [string[]] $Include, + [Parameter()] + [int16] + $Depth = 10, + [switch] $Compress ) # error if attempting to use outside of the pode server if ($null -eq $PodeContext.Server.State) { - throw "Pode has not been initialised" + throw 'Pode has not been initialised' } # get the full path to save the state @@ -301,7 +303,7 @@ function Save-PodeState } # save the state - $null = ConvertTo-Json -InputObject $state -Depth 10 -Compress:$Compress | Out-File -FilePath $Path -Force + $null = ConvertTo-Json -InputObject $state -Depth $Depth -Compress:$Compress | Out-File -FilePath $Path -Force } <# @@ -317,24 +319,29 @@ The path to a JSON file that contains the state information. .PARAMETER Merge If supplied, the state loaded from the JSON file will be merged with the current state, instead of overwriting it. +.PARAMETER Depth +Saved JSON maximum depth. Will be passed to ConvertFrom-JSON's -Depth parameter (Powershell >=6). Default is 10. + .EXAMPLE Restore-PodeState -Path './state.json' #> -function Restore-PodeState -{ +function Restore-PodeState { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Path, [switch] - $Merge + $Merge, + + [int16] + $Depth = 10 ) # error if attempting to use outside of the pode server if ($null -eq $PodeContext.Server.State) { - throw "Pode has not been initialised" + throw 'Pode has not been initialised' } # get the full path to the state @@ -347,7 +354,7 @@ function Restore-PodeState $state = @{} if (Test-PodeIsPSCore) { - $state = (Get-Content $Path -Force | ConvertFrom-Json -AsHashtable -Depth 10) + $state = (Get-Content $Path -Force | ConvertFrom-Json -AsHashtable -Depth $Depth) } else { $props = (Get-Content $Path -Force | ConvertFrom-Json).psobject.properties @@ -398,19 +405,18 @@ The name of the state object. .EXAMPLE Test-PodeState -Name 'Data' #> -function Test-PodeState -{ +function Test-PodeState { [CmdletBinding()] [OutputType([bool])] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name ) if ($null -eq $PodeContext.Server.State) { - throw "Pode has not been initialised" + throw 'Pode has not been initialised' } return $PodeContext.Server.State.ContainsKey($Name) -} \ No newline at end of file +} diff --git a/src/Public/Tasks.ps1 b/src/Public/Tasks.ps1 index ad0a6d331..d4f47573c 100644 --- a/src/Public/Tasks.ps1 +++ b/src/Public/Tasks.ps1 @@ -23,19 +23,18 @@ Add-PodeTask -Name 'Example1' -ScriptBlock { Invoke-SomeLogic } .EXAMPLE Add-PodeTask -Name 'Example1' -ScriptBlock { return Get-SomeObject } #> -function Add-PodeTask -{ - [CmdletBinding(DefaultParameterSetName='Script')] +function Add-PodeTask { + [CmdletBinding(DefaultParameterSetName = 'Script')] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name, - [Parameter(Mandatory=$true, ParameterSetName='Script')] + [Parameter(Mandatory = $true, ParameterSetName = 'Script')] [scriptblock] $ScriptBlock, - [Parameter(Mandatory=$true, ParameterSetName='File')] + [Parameter(Mandatory = $true, ParameterSetName = 'File')] [string] $FilePath, @@ -59,10 +58,10 @@ function Add-PodeTask # add the task $PodeContext.Tasks.Enabled = $true $PodeContext.Tasks.Items[$Name] = @{ - Name = $Name - Script = $ScriptBlock + Name = $Name + Script = $ScriptBlock UsingVariables = $usingVars - Arguments = (Protect-PodeValue -Value $ArgumentList -Default @{}) + Arguments = (Protect-PodeValue -Value $ArgumentList -Default @{}) } } @@ -79,11 +78,10 @@ The Maximum number of Tasks to run. .EXAMPLE Set-PodeTaskConcurrency -Maximum 10 #> -function Set-PodeTaskConcurrency -{ +function Set-PodeTaskConcurrency { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [int] $Maximum ) @@ -138,11 +136,10 @@ $task = Invoke-PodeTask -Name 'Example1' .EXAMPLE Invoke-PodeTask -Name 'Example1' | Wait-PodeTask -Timeout 3 #> -function Invoke-PodeTask -{ +function Invoke-PodeTask { [CmdletBinding()] param( - [Parameter(Mandatory=$true, ValueFromPipeline=$true)] + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [string] $Name, @@ -188,11 +185,10 @@ The Name of Task to be removed. .EXAMPLE Remove-PodeTask -Name 'Example1' #> -function Remove-PodeTask -{ +function Remove-PodeTask { [CmdletBinding()] param( - [Parameter(Mandatory=$true, ValueFromPipeline=$true)] + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [string] $Name ) @@ -210,8 +206,7 @@ Removes all Tasks. .EXAMPLE Clear-PodeTasks #> -function Clear-PodeTasks -{ +function Clear-PodeTasks { [CmdletBinding()] param() @@ -237,11 +232,10 @@ Any new Arguments for the Task. .EXAMPLE Edit-PodeTask -Name 'Example1' -ScriptBlock { Invoke-SomeNewLogic } #> -function Edit-PodeTask -{ +function Edit-PodeTask { [CmdletBinding()] param( - [Parameter(Mandatory=$true, ValueFromPipeline=$true)] + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [string] $Name, @@ -290,8 +284,7 @@ Get-PodeTask .EXAMPLE Get-PodeTask -Name Example1, Example2 #> -function Get-PodeTask -{ +function Get-PodeTask { [CmdletBinding()] param( [Parameter()] @@ -304,14 +297,14 @@ function Get-PodeTask # further filter by task names if (($null -ne $Name) -and ($Name.Length -gt 0)) { $tasks = @(foreach ($_name in $Name) { - foreach ($task in $tasks) { - if ($task.Name -ine $_name) { - continue - } + foreach ($task in $tasks) { + if ($task.Name -ine $_name) { + continue + } - $task - } - }) + $task + } + }) } # return @@ -334,8 +327,7 @@ Use-PodeTasks .EXAMPLE Use-PodeTasks -Path './my-tasks' #> -function Use-PodeTasks -{ +function Use-PodeTasks { [CmdletBinding()] param( [Parameter()] @@ -359,11 +351,10 @@ The Task to be closed. .EXAMPLE Invoke-PodeTask -Name 'Example1' | Close-PodeTask #> -function Close-PodeTask -{ +function Close-PodeTask { [CmdletBinding()] param( - [Parameter(Mandatory=$true, ValueFromPipeline=$true)] + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [hashtable] $Task ) @@ -384,11 +375,10 @@ The Task to be check. .EXAMPLE Invoke-PodeTask -Name 'Example1' | Test-PodeTaskCompleted #> -function Test-PodeTaskCompleted -{ +function Test-PodeTaskCompleted { [CmdletBinding()] param( - [Parameter(Mandatory=$true, ValueFromPipeline=$true)] + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [hashtable] $Task ) @@ -415,12 +405,11 @@ $context = Wait-PodeTask -Task $listener.GetContextAsync() .EXAMPLE $result = Invoke-PodeTask -Name 'Example1' | Wait-PodeTask #> -function Wait-PodeTask -{ +function Wait-PodeTask { [CmdletBinding()] [OutputType([object])] param( - [Parameter(Mandatory=$true, ValueFromPipeline=$true)] + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] $Task, [Parameter()] @@ -436,5 +425,5 @@ function Wait-PodeTask return (Wait-PodeTaskInternal -Task $Task -Timeout $Timeout) } - throw "Task type is invalid, expected either [System.Threading.Tasks.Task] or [hashtable]" + throw 'Task type is invalid, expected either [System.Threading.Tasks.Task] or [hashtable]' } \ No newline at end of file diff --git a/src/Public/Threading.ps1 b/src/Public/Threading.ps1 index 041649e9f..a8249d15e 100644 --- a/src/Public/Threading.ps1 +++ b/src/Public/Threading.ps1 @@ -35,20 +35,19 @@ Lock-PodeObject -Name 'LockName' -Timeout 5000 -ScriptBlock { /* logic */ } .EXAMPLE $result = (Lock-PodeObject -Return -Object $SomeArray -ScriptBlock { /* logic */ }) #> -function Lock-PodeObject -{ - [CmdletBinding(DefaultParameterSetName='Object')] +function Lock-PodeObject { + [CmdletBinding(DefaultParameterSetName = 'Object')] [OutputType([object])] param( - [Parameter(ValueFromPipeline=$true, ParameterSetName='Object')] + [Parameter(ValueFromPipeline = $true, ParameterSetName = 'Object')] [object] $Object, - [Parameter(Mandatory=$true, ParameterSetName='Name')] + [Parameter(Mandatory = $true, ParameterSetName = 'Name')] [string] $Name, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [scriptblock] $ScriptBlock, @@ -102,11 +101,10 @@ The Name of the Lockable object. .EXAMPLE New-PodeLockable -Name 'Lock1' #> -function New-PodeLockable -{ +function New-PodeLockable { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name ) @@ -131,11 +129,10 @@ The Name of the Lockable object to remove. .EXAMPLE Remove-PodeLockable -Name 'Lock1' #> -function Remove-PodeLockable -{ +function Remove-PodeLockable { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name ) @@ -158,11 +155,10 @@ The Name of the Lockable object. .EXAMPLE Get-PodeLockable -Name 'Lock1' | Lock-PodeObject -ScriptBlock {} #> -function Get-PodeLockable -{ +function Get-PodeLockable { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name ) @@ -183,11 +179,10 @@ The Name of the Lockable object. .EXAMPLE Test-PodeLockable -Name 'Lock1' #> -function Test-PodeLockable -{ +function Test-PodeLockable { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name ) @@ -220,15 +215,14 @@ Enter-PodeLockable -Object $SomeArray .EXAMPLE Enter-PodeLockable -Name 'LockName' -Timeout 5000 #> -function Enter-PodeLockable -{ - [CmdletBinding(DefaultParameterSetName='Object')] +function Enter-PodeLockable { + [CmdletBinding(DefaultParameterSetName = 'Object')] param( - [Parameter(ValueFromPipeline=$true, ParameterSetName='Object')] + [Parameter(ValueFromPipeline = $true, ParameterSetName = 'Object')] [object] $Object, - [Parameter(Mandatory=$true, ParameterSetName='Name')] + [Parameter(Mandatory = $true, ParameterSetName = 'Name')] [string] $Name, @@ -269,7 +263,7 @@ function Enter-PodeLockable $locked = $false [System.Threading.Monitor]::TryEnter($Object.SyncRoot, $Timeout, [ref]$locked) if (!$locked) { - throw "Failed to acquire lock on object" + throw 'Failed to acquire lock on object' } } @@ -292,15 +286,14 @@ Exit-PodeLockable -Object $SomeArray .EXAMPLE Exit-PodeLockable -Name 'LockName' #> -function Exit-PodeLockable -{ - [CmdletBinding(DefaultParameterSetName='Object')] +function Exit-PodeLockable { + [CmdletBinding(DefaultParameterSetName = 'Object')] param( - [Parameter(ValueFromPipeline=$true, ParameterSetName='Object')] + [Parameter(ValueFromPipeline = $true, ParameterSetName = 'Object')] [object] $Object, - [Parameter(Mandatory=$true, ParameterSetName='Name')] + [Parameter(Mandatory = $true, ParameterSetName = 'Name')] [string] $Name ) @@ -341,8 +334,7 @@ Remove all Lockables. .EXAMPLE Clear-PodeLockables #> -function Clear-PodeLockables -{ +function Clear-PodeLockables { [CmdletBinding()] param() @@ -380,11 +372,10 @@ New-PodeMutex -Name 'LocalMutex' -Scope Local .EXAMPLE New-PodeMutex -Name 'GlobalMutex' -Scope Global #> -function New-PodeMutex -{ +function New-PodeMutex { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name, @@ -430,11 +421,10 @@ The Name of the Mutex. .EXAMPLE Test-PodeMutex -Name 'LocalMutex' #> -function Test-PodeMutex -{ +function Test-PodeMutex { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name ) @@ -455,11 +445,10 @@ The Name of the Mutex. .EXAMPLE $mutex = Get-PodeMutex -Name 'SelfMutex' #> -function Get-PodeMutex -{ +function Get-PodeMutex { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name ) @@ -480,11 +469,10 @@ The Name of the Mutex. .EXAMPLE Remove-PodeMutex -Name 'GlobalMutex' #> -function Remove-PodeMutex -{ +function Remove-PodeMutex { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name ) @@ -520,15 +508,14 @@ Use-PodeMutex -Name 'SelfMutex' -Timeout 5000 -ScriptBlock {} .EXAMPLE $result = Use-PodeMutex -Name 'LocalMutex' -Return -ScriptBlock {} #> -function Use-PodeMutex -{ +function Use-PodeMutex { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [scriptblock] $ScriptBlock, @@ -573,11 +560,10 @@ If supplied, a number of milliseconds to timeout after if a hold cannot be acqui .EXAMPLE Enter-PodeMutex -Name 'SelfMutex' -Timeout 5000 #> -function Enter-PodeMutex -{ +function Enter-PodeMutex { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name, @@ -609,11 +595,10 @@ The Name of the Mutex. .EXAMPLE Exit-PodeMutex -Name 'SelfMutex' #> -function Exit-PodeMutex -{ +function Exit-PodeMutex { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name ) @@ -636,8 +621,7 @@ Removes all Mutexes. .EXAMPLE Clear-PodeMutexes #> -function Clear-PodeMutexes -{ +function Clear-PodeMutexes { [CmdletBinding()] param() @@ -678,11 +662,10 @@ New-PodeSemaphore -Name 'LocalSemaphore' -Scope Local .EXAMPLE New-PodeSemaphore -Name 'GlobalSemaphore' -Count 3 -Scope Global #> -function New-PodeSemaphore -{ +function New-PodeSemaphore { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name, @@ -736,11 +719,10 @@ The Name of the Semaphore. .EXAMPLE Test-PodeSemaphore -Name 'LocalSemaphore' #> -function Test-PodeSemaphore -{ +function Test-PodeSemaphore { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name ) @@ -761,11 +743,10 @@ The Name of the Semaphore. .EXAMPLE $semaphore = Get-PodeSemaphore -Name 'SelfSemaphore' #> -function Get-PodeSemaphore -{ +function Get-PodeSemaphore { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name ) @@ -786,11 +767,10 @@ The Name of the Semaphore. .EXAMPLE Remove-PodeSemaphore -Name 'GlobalSemaphore' #> -function Remove-PodeSemaphore -{ +function Remove-PodeSemaphore { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name ) @@ -826,15 +806,14 @@ Use-PodeSemaphore -Name 'SelfSemaphore' -Timeout 5000 -ScriptBlock {} .EXAMPLE $result = Use-PodeSemaphore -Name 'LocalSemaphore' -Return -ScriptBlock {} #> -function Use-PodeSemaphore -{ +function Use-PodeSemaphore { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [scriptblock] $ScriptBlock, @@ -879,11 +858,10 @@ If supplied, a number of milliseconds to timeout after if a hold cannot be acqui .EXAMPLE Enter-PodeSemaphore -Name 'SelfSemaphore' -Timeout 5000 #> -function Enter-PodeSemaphore -{ +function Enter-PodeSemaphore { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name, @@ -918,11 +896,10 @@ The number of releases to release in one go. (Default: 1) .EXAMPLE Exit-PodeSemaphore -Name 'SelfSemaphore' #> -function Exit-PodeSemaphore -{ +function Exit-PodeSemaphore { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name, @@ -953,8 +930,7 @@ Removes all Semaphores. .EXAMPLE Clear-PodeSemaphores #> -function Clear-PodeSemaphores -{ +function Clear-PodeSemaphores { [CmdletBinding()] param() diff --git a/src/Public/Timers.ps1 b/src/Public/Timers.ps1 index cda89acb1..84ef29dd7 100644 --- a/src/Public/Timers.ps1 +++ b/src/Public/Timers.ps1 @@ -41,19 +41,18 @@ Add-PodeTimer -Name 'RunAfter60secs' -Interval 10 -Skip 6 -ScriptBlock { /* logi .EXAMPLE Add-PodeTimer -Name 'Args' -Interval 2 -ScriptBlock { /* logic */ } -ArgumentList 'arg1', 'arg2' #> -function Add-PodeTimer -{ - [CmdletBinding(DefaultParameterSetName='Script')] - param ( - [Parameter(Mandatory=$true)] +function Add-PodeTimer { + [CmdletBinding(DefaultParameterSetName = 'Script')] + param( + [Parameter(Mandatory = $true)] [string] $Name, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [int] $Interval, - [Parameter(Mandatory=$true, ParameterSetName='Script')] + [Parameter(Mandatory = $true, ParameterSetName = 'Script')] [scriptblock] $ScriptBlock, @@ -65,7 +64,7 @@ function Add-PodeTimer [int] $Skip = 0, - [Parameter(Mandatory=$true, ParameterSetName='File')] + [Parameter(Mandatory = $true, ParameterSetName = 'File')] [string] $FilePath, @@ -117,18 +116,18 @@ function Add-PodeTimer # add the timer $PodeContext.Timers.Enabled = $true $PodeContext.Timers.Items[$Name] = @{ - Name = $Name - Interval = $Interval - Limit = $Limit - Count = 0 - Skip = $Skip + Name = $Name + Interval = $Interval + Limit = $Limit + Count = 0 + Skip = $Skip NextTriggerTime = $NextTriggerTime LastTriggerTime = $null - Script = $ScriptBlock - UsingVariables = $usingVars - Arguments = $ArgumentList - OnStart = $OnStart - Completed = $false + Script = $ScriptBlock + UsingVariables = $usingVars + Arguments = $ArgumentList + OnStart = $OnStart + Completed = $false } } @@ -149,11 +148,10 @@ An array of arguments to supply to the Timer's ScriptBlock. .EXAMPLE Invoke-PodeTimer -Name 'timer-name' #> -function Invoke-PodeTimer -{ +function Invoke-PodeTimer { [CmdletBinding()] param( - [Parameter(Mandatory=$true, ValueFromPipeline=$true)] + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [string] $Name, @@ -184,11 +182,10 @@ The Name of Timer to be removed. .EXAMPLE Remove-PodeTimer -Name 'SaveState' #> -function Remove-PodeTimer -{ +function Remove-PodeTimer { [CmdletBinding()] - param ( - [Parameter(Mandatory=$true, ValueFromPipeline=$true)] + param( + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [string] $Name ) @@ -206,8 +203,7 @@ Removes all Timers. .EXAMPLE Clear-PodeTimers #> -function Clear-PodeTimers -{ +function Clear-PodeTimers { [CmdletBinding()] param() @@ -236,11 +232,10 @@ Any new Arguments for the Timer. .EXAMPLE Edit-PodeTimer -Name 'Hello' -Interval 10 #> -function Edit-PodeTimer -{ +function Edit-PodeTimer { [CmdletBinding()] param( - [Parameter(Mandatory=$true, ValueFromPipeline=$true)] + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [string] $Name, @@ -298,8 +293,7 @@ Get-PodeTimer .EXAMPLE Get-PodeTimer -Name Name1, Name2 #> -function Get-PodeTimer -{ +function Get-PodeTimer { [CmdletBinding()] param( [Parameter()] @@ -312,14 +306,14 @@ function Get-PodeTimer # further filter by timer names if (($null -ne $Name) -and ($Name.Length -gt 0)) { $timers = @(foreach ($_name in $Name) { - foreach ($timer in $timers) { - if ($timer.Name -ine $_name) { - continue - } + foreach ($timer in $timers) { + if ($timer.Name -ine $_name) { + continue + } - $timer - } - }) + $timer + } + }) } # return @@ -339,11 +333,10 @@ The Name of the Timer. .EXAMPLE if (Test-PodeTimer -Name TimerName) { } #> -function Test-PodeTimer -{ +function Test-PodeTimer { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name ) @@ -367,8 +360,7 @@ Use-PodeTimers .EXAMPLE Use-PodeTimers -Path './my-timers' #> -function Use-PodeTimers -{ +function Use-PodeTimers { [CmdletBinding()] param( [Parameter()] diff --git a/src/Public/Utilities.ps1 b/src/Public/Utilities.ps1 index c40e52b63..8074c3379 100644 --- a/src/Public/Utilities.ps1 +++ b/src/Public/Utilities.ps1 @@ -17,8 +17,7 @@ If an error is thrown, check the reason - if it's network related ignore the err .EXAMPLE Close-PodeDisposable -Disposable $stream -Close #> -function Close-PodeDisposable -{ +function Close-PodeDisposable { [CmdletBinding()] param( [Parameter()] @@ -64,8 +63,7 @@ Returns the literal path of the server. .EXAMPLE $path = Get-PodeServerPath #> -function Get-PodeServerPath -{ +function Get-PodeServerPath { [CmdletBinding()] [OutputType([string])] param() @@ -89,15 +87,14 @@ The ScriptBlock to time. .EXAMPLE Start-PodeStopwatch -Name 'ReadFile' -ScriptBlock { $content = Get-Content './file.txt' } #> -function Start-PodeStopwatch -{ +function Start-PodeStopwatch { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name, - [Parameter(Mandatory=$true, ValueFromPipeline=$true)] + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [scriptblock] $ScriptBlock ) @@ -132,16 +129,15 @@ The ScriptBlock to invoke. It will be supplied the Stream. .EXAMPLE $content = (Use-PodeStream -Stream $stream -ScriptBlock { return $args[0].ReadToEnd() }) #> -function Use-PodeStream -{ +function Use-PodeStream { [CmdletBinding()] [OutputType([object])] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [System.IDisposable] $Stream, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [scriptblock] $ScriptBlock ) @@ -171,11 +167,10 @@ The path, literal or relative to the server, to some script. .EXAMPLE Use-PodeScript -Path './scripts/tools.ps1' #> -function Use-PodeScript -{ +function Use-PodeScript { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Path ) @@ -217,8 +212,7 @@ Returns the loaded configuration of the server. .EXAMPLE $s = Get-PodeConfig #> -function Get-PodeConfig -{ +function Get-PodeConfig { [CmdletBinding()] [OutputType([hashtable])] param() @@ -242,11 +236,10 @@ An array of arguments to supply to the Endware's ScriptBlock. .EXAMPLE Add-PodeEndware -ScriptBlock { /* logic */ } #> -function Add-PodeEndware -{ +function Add-PodeEndware { [CmdletBinding()] param( - [Parameter(Mandatory=$true, ValueFromPipeline=$true)] + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [scriptblock] $ScriptBlock, @@ -260,9 +253,9 @@ function Add-PodeEndware # add the scriptblock to array of endware that needs to be run $PodeContext.Server.Endware += @{ - Logic = $ScriptBlock + Logic = $ScriptBlock UsingVariables = $usingVars - Arguments = $ArgumentList + Arguments = $ArgumentList } } @@ -282,8 +275,7 @@ Use-PodeEndware .EXAMPLE Use-PodeEndware -Path './endware' #> -function Use-PodeEndware -{ +function Use-PodeEndware { [CmdletBinding()] param( [Parameter()] @@ -313,15 +305,14 @@ Import-PodeModule -Name IISManager .EXAMPLE Import-PodeModule -Path './modules/utilities.psm1' #> -function Import-PodeModule -{ - [CmdletBinding(DefaultParameterSetName='Name')] +function Import-PodeModule { + [CmdletBinding(DefaultParameterSetName = 'Name')] param( - [Parameter(Mandatory=$true, ParameterSetName='Name')] + [Parameter(Mandatory = $true, ParameterSetName = 'Name')] [string] $Name, - [Parameter(Mandatory=$true, ParameterSetName='Path')] + [Parameter(Mandatory = $true, ParameterSetName = 'Path')] [string] $Path ) @@ -383,11 +374,10 @@ The name of a Snapin to import. .EXAMPLE Import-PodeSnapin -Name 'WDeploySnapin3.0' #> -function Import-PodeSnapin -{ +function Import-PodeSnapin { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name ) @@ -417,8 +407,7 @@ A default value to return should the main value be null/empty. .EXAMPLE $Name = Protect-PodeValue -Value $Name -Default 'Rick' #> -function Protect-PodeValue -{ +function Protect-PodeValue { [CmdletBinding()] [OutputType([object])] param( @@ -451,12 +440,11 @@ The value to use if evaluated to False. .EXAMPLE $Port = Resolve-PodeValue -Check $AllowSsl -TrueValue 443 -FalseValue -80 #> -function Resolve-PodeValue -{ +function Resolve-PodeValue { [CmdletBinding()] [OutputType([object])] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [bool] $Check, @@ -505,12 +493,11 @@ Invoke-PodeScriptBlock -ScriptBlock { Write-Host 'Hello!' } .EXAMPLE Invoke-PodeScriptBlock -Arguments 'Morty' -ScriptBlock { /* logic */ } #> -function Invoke-PodeScriptBlock -{ +function Invoke-PodeScriptBlock { [CmdletBinding()] [OutputType([object])] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [scriptblock] $ScriptBlock, @@ -573,8 +560,7 @@ The value to test. .EXAMPLE if (Test-PodeIsEmpty @{}) { /* logic */ } #> -function Test-PodeIsEmpty -{ +function Test-PodeIsEmpty { [CmdletBinding()] [OutputType([bool])] param( @@ -619,8 +605,7 @@ Tests if the the current session is running in PowerShell Core. .EXAMPLE if (Test-PodeIsPSCore) { /* logic */ } #> -function Test-PodeIsPSCore -{ +function Test-PodeIsPSCore { [CmdletBinding()] [OutputType([bool])] param() @@ -638,8 +623,7 @@ Tests if the current OS is Unix. .EXAMPLE if (Test-PodeIsUnix) { /* logic */ } #> -function Test-PodeIsUnix -{ +function Test-PodeIsUnix { [CmdletBinding()] [OutputType([bool])] param() @@ -657,8 +641,7 @@ Tests if the current OS is Windows. .EXAMPLE if (Test-PodeIsWindows) { /* logic */ } #> -function Test-PodeIsWindows -{ +function Test-PodeIsWindows { [CmdletBinding()] [OutputType([bool])] param() @@ -677,8 +660,7 @@ Tests if the current OS is MacOS. .EXAMPLE if (Test-PodeIsMacOS) { /* logic */ } #> -function Test-PodeIsMacOS -{ +function Test-PodeIsMacOS { [CmdletBinding()] [OutputType([bool])] param() @@ -686,6 +668,23 @@ function Test-PodeIsMacOS return ([bool]$IsMacOS) } +<# +.SYNOPSIS +Tests if the scope you're in is currently within a Pode runspace. + +.DESCRIPTION +Tests if the scope you're in is currently within a Pode runspace. + +.EXAMPLE +If (Test-PodeInRunspace) { ... } +#> +function Test-PodeInRunspace { + [CmdletBinding()] + param() + + return ([bool]$PODE_SCOPE_RUNSPACE) +} + <# .SYNOPSIS Outputs an object to the main Host. @@ -703,11 +702,10 @@ The object to output. .EXAMPLE @{ Name = 'Rick' } | Out-PodeHost #> -function Out-PodeHost -{ +function Out-PodeHost { [CmdletBinding()] param( - [Parameter(Mandatory=$true, ValueFromPipeline=$true)] + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [object] $InputObject ) @@ -737,11 +735,10 @@ Whether or not to write a new line. .EXAMPLE 'Some output' | Write-PodeHost -ForegroundColor Cyan #> -function Write-PodeHost -{ +function Write-PodeHost { [CmdletBinding()] param( - [Parameter(Position=0, ValueFromPipeline=$true)] + [Parameter(Position = 0, ValueFromPipeline = $true)] [object] $Object, @@ -775,8 +772,7 @@ Returns whether or not the server is running via IIS. .EXAMPLE if (Test-PodeIsIIS) { } #> -function Test-PodeIsIIS -{ +function Test-PodeIsIIS { [CmdletBinding()] param() @@ -793,8 +789,7 @@ Returns the IIS application path, or null if not using IIS. .EXAMPLE $path = Get-PodeIISApplicationPath #> -function Get-PodeIISApplicationPath -{ +function Get-PodeIISApplicationPath { [CmdletBinding()] param() @@ -815,8 +810,7 @@ Returns whether or not the server is running via Heroku. .EXAMPLE if (Test-PodeIsHeroku) { } #> -function Test-PodeIsHeroku -{ +function Test-PodeIsHeroku { [CmdletBinding()] param() @@ -833,8 +827,7 @@ Returns whether or not the server is being hosted behind another application, su .EXAMPLE if (Test-PodeIsHosted) { } #> -function Test-PodeIsHosted -{ +function Test-PodeIsHosted { [CmdletBinding()] param() @@ -857,15 +850,14 @@ The Value of the variable to be set .EXAMPLE Out-PodeVariable -Name ExampleVar -Value @{ Name = 'Bob' } #> -function Out-PodeVariable -{ +function Out-PodeVariable { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name, - [Parameter(ValueFromPipeline=$true)] + [Parameter(ValueFromPipeline = $true)] [object] $Value ) @@ -931,8 +923,7 @@ New-PodeCron -Every Hour -Day Monday # every hour .EXAMPLE New-PodeCron -Every Quarter # every 1st jan, apr, jul, oct, at 00:00 #> -function New-PodeCron -{ +function New-PodeCron { [CmdletBinding()] param( [Parameter()] @@ -972,50 +963,50 @@ function New-PodeCron # cant have None and Interval if (($Every -ieq 'none') -and ($Interval -gt 0)) { - throw "Cannot supply an interval when -Every is set to None" + throw 'Cannot supply an interval when -Every is set to None' } # base cron $cron = @{ Minute = '*' - Hour = '*' - Date = '*' - Month = '*' - Day = '*' + Hour = '*' + Date = '*' + Month = '*' + Day = '*' } # convert month/day to numbers if ($Month.Length -gt 0) { $MonthInts = @(foreach ($item in $Month) { (@{ - January = 1 - February = 2 - March = 3 - April = 4 - May = 5 - June = 6 - July = 7 - August = 8 - September = 9 - October = 10 - November = 11 - December = 12 - })[$item] - }) + January = 1 + February = 2 + March = 3 + April = 4 + May = 5 + June = 6 + July = 7 + August = 8 + September = 9 + October = 10 + November = 11 + December = 12 + })[$item] + }) } if ($Day.Length -gt 0) { $DayInts = @(foreach ($item in $Day) { (@{ - Sunday = 0 - Monday = 1 - Tuesday = 2 - Wednesday = 3 - Thursday = 4 - Friday = 5 - Saturday = 6 - })[$item] - }) + Sunday = 0 + Monday = 1 + Tuesday = 2 + Wednesday = 3 + Thursday = 4 + Friday = 5 + Saturday = 6 + })[$item] + }) } # set "every" defaults @@ -1072,7 +1063,7 @@ function New-PodeCron $cron.Month = '1,4,7,10' if ($Interval -gt 0) { - throw "Cannot supply interval value for every quarter" + throw 'Cannot supply interval value for every quarter' } } @@ -1083,7 +1074,7 @@ function New-PodeCron $cron.Month = '1' if ($Interval -gt 0) { - throw "Cannot supply interval value for every year" + throw 'Cannot supply interval value for every year' } } } diff --git a/src/Public/Verbs.ps1 b/src/Public/Verbs.ps1 index 750740f8a..8ef239b81 100644 --- a/src/Public/Verbs.ps1 +++ b/src/Public/Verbs.ps1 @@ -38,20 +38,19 @@ Add-PodeVerb -Verb 'Quit' -Close .EXAMPLE Add-PodeVerb -Verb 'StartTls' -UpgradeToSsl #> -function Add-PodeVerb -{ - [CmdletBinding(DefaultParameterSetName='Script')] +function Add-PodeVerb { + [CmdletBinding(DefaultParameterSetName = 'Script')] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $Verb, - [Parameter(ParameterSetName='Script')] + [Parameter(ParameterSetName = 'Script')] [scriptblock] $ScriptBlock, - [Parameter(Mandatory=$true, ParameterSetName='File')] + [Parameter(Mandatory = $true, ParameterSetName = 'File')] [string] $FilePath, @@ -101,22 +100,22 @@ function Add-PodeVerb # add the verb(s) Write-Verbose "Adding Verb: $($Verb)" $PodeContext.Server.Verbs[$Verb] += @(foreach ($_endpoint in $endpoints) { - @{ - Logic = $ScriptBlock - UsingVariables = $usingVars - Endpoint = @{ - Protocol = $_endpoint.Protocol - Address = $_endpoint.Address.Trim() - Name = $_endpoint.Name - } - Arguments = $ArgumentList - Verb = $Verb - Connection = @{ - UpgradeToSsl = $UpgradeToSsl - Close = $Close + @{ + Logic = $ScriptBlock + UsingVariables = $usingVars + Endpoint = @{ + Protocol = $_endpoint.Protocol + Address = $_endpoint.Address.Trim() + Name = $_endpoint.Name + } + Arguments = $ArgumentList + Verb = $Verb + Connection = @{ + UpgradeToSsl = $UpgradeToSsl + Close = $Close + } } - } - }) + }) } <# @@ -138,11 +137,10 @@ Remove-PodeVerb -Verb 'Hello' .EXAMPLE Remove-PodeVerb -Verb 'Hello :username' -EndpointName User #> -function Remove-PodeVerb -{ +function Remove-PodeVerb { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Verb, @@ -161,8 +159,8 @@ function Remove-PodeVerb # remove the verb's logic $PodeContext.Server.Verbs[$Verb] = @($PodeContext.Server.Verbs[$Verb] | Where-Object { - $_.Endpoint.Name -ine $EndpointName - }) + $_.Endpoint.Name -ine $EndpointName + }) # if the verb has no more logic, just remove it if ((Get-PodeCount $PodeContext.Server.Verbs[$Verb]) -eq 0) { @@ -180,8 +178,7 @@ Removes all added Verbs. .EXAMPLE Clear-PodeVerbs #> -function Clear-PodeVerbs -{ +function Clear-PodeVerbs { [CmdletBinding()] param() @@ -207,8 +204,7 @@ Get-PodeVerb -Verb 'Hello' .EXAMPLE Get-PodeVerb -Verb 'Hello :username' -EndpointName User #> -function Get-PodeVerb -{ +function Get-PodeVerb { [CmdletBinding()] param( [Parameter()] @@ -237,14 +233,14 @@ function Get-PodeVerb # further filter by endpoint names if (($null -ne $EndpointName) -and ($EndpointName.Length -gt 0)) { $verbs = @(foreach ($name in $EndpointName) { - foreach ($v in $verbs) { - if ($v.Endpoint.Name -ine $name) { - continue - } + foreach ($v in $verbs) { + if ($v.Endpoint.Name -ine $name) { + continue + } - $v - } - }) + $v + } + }) } # return @@ -267,8 +263,7 @@ Use-PodeVerbs .EXAMPLE Use-PodeVerbs -Path './my-verbs' #> -function Use-PodeVerbs -{ +function Use-PodeVerbs { [CmdletBinding()] param( [Parameter()] diff --git a/src/Public/WebSockets.ps1 b/src/Public/WebSockets.ps1 index c7f7078e6..5d2cbd21d 100644 --- a/src/Public/WebSockets.ps1 +++ b/src/Public/WebSockets.ps1 @@ -13,11 +13,10 @@ The Maximum number of threads available to process WebSocket connection messages .EXAMPLE Set-PodeWebSocketConcurrency -Maximum 5 #> -function Set-PodeWebSocketConcurrency -{ +function Set-PodeWebSocketConcurrency { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [int] $Maximum ) @@ -84,23 +83,22 @@ Connect-PodeWebSocket -Name 'Example' -Url 'ws://example.com/some/socket' -FileP .EXAMPLE Connect-PodeWebSocket -Name 'Example' -Url 'ws://example.com/some/socket' -ScriptBlock { ... } -ContentType 'text/xml' #> -function Connect-PodeWebSocket -{ - [CmdletBinding(DefaultParameterSetName='Script')] +function Connect-PodeWebSocket { + [CmdletBinding(DefaultParameterSetName = 'Script')] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Url, - [Parameter(ParameterSetName='Script')] + [Parameter(ParameterSetName = 'Script')] [scriptblock] $ScriptBlock, - [Parameter(Mandatory=$true, ParameterSetName='File')] + [Parameter(Mandatory = $true, ParameterSetName = 'File')] [string] $FilePath, @@ -138,11 +136,11 @@ function Connect-PodeWebSocket } $PodeContext.Server.WebSockets.Connections[$Name] = @{ - Name = $Name - Url = $Url - Logic = $ScriptBlock + Name = $Name + Url = $Url + Logic = $ScriptBlock UsingVariables = $usingVars - Arguments = $ArgumentList + Arguments = $ArgumentList } } @@ -159,8 +157,7 @@ The Name of the WebSocket connection (optional if in the scope where $WsEvent is .EXAMPLE Disconnect-PodeWebSocket -Name 'Example' #> -function Disconnect-PodeWebSocket -{ +function Disconnect-PodeWebSocket { [CmdletBinding()] param( [Parameter()] @@ -173,7 +170,7 @@ function Disconnect-PodeWebSocket } if ([string]::IsNullOrWhiteSpace($Name)) { - throw "No Name for a WebSocket to disconnect from supplied" + throw 'No Name for a WebSocket to disconnect from supplied' } if (Test-PodeWebSocket -Name $Name) { @@ -194,8 +191,7 @@ The Name of the WebSocket connection (optional if in the scope where $WsEvent is .EXAMPLE Remove-PodeWebSocket -Name 'Example' #> -function Remove-PodeWebSocket -{ +function Remove-PodeWebSocket { [CmdletBinding()] param( [Parameter()] @@ -208,7 +204,7 @@ function Remove-PodeWebSocket } if ([string]::IsNullOrWhiteSpace($Name)) { - throw "No Name for a WebSocket to remove supplied" + throw 'No Name for a WebSocket to remove supplied' } $PodeContext.Server.WebSockets.Receiver.RemoveWebSocket($Name) @@ -237,8 +233,7 @@ An optional message Type. (default: Text) .EXAMPLE Send-PodeWebSocket -Name 'Example' -Message @{ message = 'Hello, there' } #> -function Send-PodeWebSocket -{ +function Send-PodeWebSocket { [CmdletBinding()] param( [Parameter()] @@ -265,7 +260,7 @@ function Send-PodeWebSocket # do we have a name? if ([string]::IsNullOrWhiteSpace($Name)) { - throw "No Name for a WebSocket to send message to supplied" + throw 'No Name for a WebSocket to send message to supplied' } # do the socket exist? @@ -302,8 +297,7 @@ Reset-PodeWebSocket -Name 'Example' .EXAMPLE Reset-PodeWebSocket -Name 'Example' -Url 'ws://example.com/some/socket' #> -function Reset-PodeWebSocket -{ +function Reset-PodeWebSocket { [CmdletBinding()] param( [Parameter()] @@ -321,7 +315,7 @@ function Reset-PodeWebSocket } if ([string]::IsNullOrWhiteSpace($Name)) { - throw "No Name for a WebSocket to reset supplied" + throw 'No Name for a WebSocket to reset supplied' } if (Test-PodeWebSocket -Name $Name) { @@ -342,11 +336,10 @@ The Name of the WebSocket connection. .EXAMPLE Test-PodeWebSocket -Name 'Example' #> -function Test-PodeWebSocket -{ +function Test-PodeWebSocket { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string] $Name ) diff --git a/tests/unit/Authentication.Tests.ps1 b/tests/unit/Authentication.Tests.ps1 index 31d891aac..764b3d5ae 100644 --- a/tests/unit/Authentication.Tests.ps1 +++ b/tests/unit/Authentication.Tests.ps1 @@ -8,26 +8,40 @@ Describe 'Set-PodeAuthStatus' { Mock Move-PodeResponseUrl {} Mock Set-PodeResponseStatus {} + $PodeContext = @{ + Server = @{ + Authentications = @{ + Methods = @{ + ExampleAuth = @{ + Failure = @{ Url = '/url' } + Success = @{ Url = '/url' } + Cache = @{} + } + } + } + } + } + It 'Redirects to a failure URL' { - Set-PodeAuthStatus -StatusCode 500 -Failure @{ 'Url' = 'url'} | Should Be $false + Set-PodeAuthStatus -StatusCode 500 -Name ExampleAuth | Should Be $false Assert-MockCalled Move-PodeResponseUrl -Times 1 -Scope It Assert-MockCalled Set-PodeResponseStatus -Times 0 -Scope It } It 'Sets status to failure' { - Set-PodeAuthStatus -StatusCode 500 | Should Be $false - Assert-MockCalled Move-PodeResponseUrl -Times 0 -Scope It - Assert-MockCalled Set-PodeResponseStatus -Times 1 -Scope It + Set-PodeAuthStatus -StatusCode 500 -Name ExampleAuth | Should Be $false + Assert-MockCalled Move-PodeResponseUrl -Times 1 -Scope It + Assert-MockCalled Set-PodeResponseStatus -Times 0 -Scope It } It 'Redirects to a success URL' { - Set-PodeAuthStatus -Success @{ 'Url' = 'url' } -LoginRoute | Should Be $false + Set-PodeAuthStatus -Name ExampleAuth -LoginRoute | Should Be $false Assert-MockCalled Move-PodeResponseUrl -Times 1 -Scope It Assert-MockCalled Set-PodeResponseStatus -Times 0 -Scope It } It 'Returns true for next middleware' { - Set-PodeAuthStatus | Should Be $true + Set-PodeAuthStatus -Name ExampleAuth -NoSuccessRedirect | Should Be $true Assert-MockCalled Move-PodeResponseUrl -Times 0 -Scope It Assert-MockCalled Set-PodeResponseStatus -Times 0 -Scope It } @@ -110,4 +124,20 @@ Describe 'Remove-PodeAuthSession' { Assert-MockCalled Revoke-PodeSession -Times 1 -Scope It } +} + +Describe 'Test-PodeJwt' { + It 'No exception - sucessful validation' { + (Test-PodeJwt @{}) | Should Be $null + } + + It 'Throws exception - the JWT has expired' { + # "exp" (Expiration Time) Claim + { Test-PodeJwt @{exp = 1 } } | Should -Throw -ExceptionType ([System.Exception]) -ExpectedMessage 'The JWT has expired' + } + + It 'Throws exception - the JWT is not yet valid for use' { + # "nbf" (Not Before) Claim + { Test-PodeJwt @{nbf = 99999999999 } } | Should -Throw -ExceptionType ([System.Exception]) -ExpectedMessage 'The JWT is not yet valid for use' + } } \ No newline at end of file diff --git a/tests/unit/Schedules.Tests.ps1 b/tests/unit/Schedules.Tests.ps1 index 6cb0364cd..373dc01bf 100644 --- a/tests/unit/Schedules.Tests.ps1 +++ b/tests/unit/Schedules.Tests.ps1 @@ -220,16 +220,6 @@ Describe 'Get-PodeSchedule' { $schedules.Length | Should Be 0 } - It 'Returns no schedules by where end just before end' { - $PodeContext = @{ Schedules = @{ Items = @{} } } - $start = ([DateTime]::Now.AddHours(3)) - $end = ([DateTime]::Now.AddHours(5)) - - Add-PodeSchedule -Name 'test1' -Cron '@hourly' -ScriptBlock { Write-Host 'hello' } -StartTime $start -EndTime $end - $schedules = Get-PodeSchedule -StartTime $start.AddHours(1).AddMinutes(1) -EndTime $end.AddHours(-1).AddMinutes(-1) - $schedules.Length | Should Be 0 - } - It 'Returns 2 schedules by name' { $PodeContext = @{ Schedules = @{ Items = @{} } } $start = ([DateTime]::Now.AddHours(3)) diff --git a/tests/unit/Server.Tests.ps1 b/tests/unit/Server.Tests.ps1 index ef5020646..68d207ecb 100644 --- a/tests/unit/Server.Tests.ps1 +++ b/tests/unit/Server.Tests.ps1 @@ -128,7 +128,12 @@ Describe 'Restart-PodeInternalServer' { } Cookies = @{} Sessions = @{ 'key' = 'value' } - Authentications = @{ 'key' = 'value' } + Authentications = @{ + Methods = @{ 'key' = 'value' } + } + Authorisations = @{ + Methods = @{ 'key' = 'value' } + } State = @{ 'key' = 'value' } Output = @{ Variables = @{ 'key' = 'value' } @@ -219,7 +224,7 @@ Describe 'Restart-PodeInternalServer' { $PodeContext.Server.Middleware.Count | Should Be 0 $PodeContext.Server.Endware.Count | Should Be 0 $PodeContext.Server.Sessions.Count | Should Be 0 - $PodeContext.Server.Authentications.Count | Should Be 0 + $PodeContext.Server.Authentications.Methods.Count | Should Be 0 $PodeContext.Server.State.Count | Should Be 0 $PodeContext.Server.Configuration | Should Be $null