Skip to content

Commit

Permalink
Merge pull request #789 from Badgerati/develop
Browse files Browse the repository at this point in the history
v2.4.0
  • Loading branch information
Badgerati authored Jul 21, 2021
2 parents 53b9a81 + e880ca0 commit e2dfb55
Show file tree
Hide file tree
Showing 58 changed files with 1,199 additions and 226 deletions.
38 changes: 35 additions & 3 deletions docs/Hosting/SmtpServer.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
# SMTP Server

Pode has an inbuilt SMTP server which automatically creates a TCP listener on port 25 (unless you specify a different port via the [`Add-PodeEndpoint`](../../Functions/Core/Add-PodeEndpoint) function).
Pode has an inbuilt SMTP server for receiving Email which automatically creates a TCP listener on port 25 - unless you specify a different port via the [`Add-PodeEndpoint`](../../Functions/Core/Add-PodeEndpoint) function.

Unlike with web servers that use the Route functions, SMTP servers use the Handler functions, which let you specify logic for handling responses from TCP streams.

!!! tip
You can setup multiple different Handlers to run different logic for one Email.

## Usage

To create a Handler for the inbuilt SMTP server you can use the following example:

```powershell
Expand All @@ -18,7 +23,31 @@ Start-PodeServer {
}
```

The SMTP Handler will be passed the a `$SmtpEvent` object, that contains te Request, Response, and Email:
Starting this server will listen for incoming email on `localhost:25`. The Handler will have access to the `$SmtpEvent` object (see below), which contains information about the Email.

An example of sending Email to the above server via `Send-MailMessage`:

```powershell
Send-MailMessage -SmtpServer localhost -To 'to@example.com' -From 'from@example.com' -Body 'Hello' -Subject 'Hi there' -Port 25
```

## Attachments

The SMTP server also accepts attachments, which are available in a Handler via `$SmtpEvent.Email.Attachments`. This property contains a list of available attachments on the Email, each attachment has a `Name` and `Bytes` properties - the latter being the raw byte content of the attachment.

An attachment also has a `.Save(<path>)` method. For example, if the Email has an a single attachment: an `example.png` file, and you wish to save it, then the following will save the file to `C:\temp\example.png`:

```powershell
Add-PodeHandler -Type Smtp -Name 'Main' -ScriptBlock {
$SmtpEvent.Email.Attachments[0].Save('C:\temp')
}
```

## Objects

### SmtpEvent

The SMTP Handler will be passed the `$SmtpEvent` object, that contains the Request, Response, and Email properties:

| Name | Type | Description |
| ---- | ---- | ----------- |
Expand All @@ -27,7 +56,9 @@ The SMTP Handler will be passed the a `$SmtpEvent` object, that contains te Requ
| Lockable | hashtable | A synchronized hashtable that can be used with `Lock-PodeObject` |
| Email | hashtable | An object containing data from the email, as seen below |

The `Email` property contains the following:
### Email

The `Email` property contains the following properties:

| Name | Type | Description |
| ---- | ---- | ----------- |
Expand All @@ -40,3 +71,4 @@ The `Email` property contains the following:
| ContentEncoding | string | The content encoding of the original email body |
| Headers | hashtable | A list of all the headers received for the email |
| Data | string | The full raw data of the email |
| Attachments | PodeSmtpAttachment[] | An list of SMTP attachments, containing the Name and Bytes of the attachment |
40 changes: 37 additions & 3 deletions docs/Tutorials/Endpoints/External.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ At most times you'll possibly be accessing your Pode server locally. However, yo

## All Addresses

The default and common approach is to set your Pode server to listen on all IP addresses:
The default and common approach is to set your Pode server to listen on all IP addresses; this approach does require administrator privileges:

```powershell
Add-PodeEndpoint -Address * -Port 8080 -Protocol Http
Expand All @@ -21,7 +21,7 @@ Invoke-RestMethod -Uri 'http://<ip-address|vm-name>:8080'

## IP Address

The other way to expose your server externally is to create an endpoint using the server's Private/Public IP address. For example, assuming the the server's IP is `10.10.1.5`:
The other way to expose your server externally is to create an endpoint using the server's Private/Public IP address; this approach does require administrator privileges. For example, assuming the the server's IP is `10.10.1.5`:

```powershell
Add-PodeEndpoint -Address 10.10.1.5 -Port 8080 -Protocol Http
Expand All @@ -35,7 +35,7 @@ Invoke-RestMethod -Uri 'http://10.10.1.5:8080'

## Hostnames

The final way to expose your server externally is to allow only specific hostnames bound to the server's Private/Public IP address - something like SNI in IIS.
Another way to expose your server externally is to allow only specific hostnames bound to the server's Private/Public IP address - something like SNI in IIS. This approach does require administrator privileges.

To do this, let's say you want to allow only `one.pode.com` and `two.pode.com` on a server with IP `10.10.1.5`. There are two way of doing this:

Expand Down Expand Up @@ -68,3 +68,37 @@ With these set, you can access your endpoint using only the `one.pode.com` and `
Invoke-RestMethod -Uri 'http://one.pode.com:8080'
Invoke-RestMethod -Uri 'http://two.pode.com:8080'
```

## Netsh

This next way allows you to access your server external, but be able to run the server without administrator privileges. The initial setup does require administrator privileges, but running the server does not.

To do this, let's say you want to access your server on `10.10.1.5`, you can use the following steps:

1. You server should be listening on localhost and then any port you wish:

```powershell
Add-PodeEndpoint -Address localhost -Port 8080 -Protocol Http
```

2. Next, you can run the following command as an administrator where the `<external-port>` can be any port that's not the port in your [`Add-PodeEndpoint`](../../../Functions/Core/Add-PodeEndpoint) (such as port+1):

```bash
netsh interface portproxy add v4tov4 listenport=<external-port> connectaddress=127.0.0.1 connectport=<pode-port>
```

For example, the above endpoint could be:

```bash
netsh interface portproxy add v4tov4 listenport=8081 connectaddress=127.0.0.1 connectport=8080
```

3. Run your Pode server as a non-admin user.

With this done, you can access your endpoint on `10.10.1.5:8081`:

```powershell
Invoke-RestMethod -Uri 'http://10.10.1.5:8081'
```

This works by having `netsh interface portproxy` redirect traffic to the local port which your Pode server is listening on.
50 changes: 50 additions & 0 deletions docs/Tutorials/Events.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Events

Pode lets you register scripts to be run when certain server events are triggered. The following types of events can have scripts registered:

* Start
* Terminate
* Restart
* Browser

## Overview

You can use [`Register-PodeEvent`](../../Functions/Events/Register-PodeEvent) to register a script that can be run when an event within Pode is triggered. Each event can have multiple scripts registered, and you can unregister a script at any point using [`Unregister-PodeEvent`](../../Functions/Events/Unregister-PodeEvent):

```powershell
# register:
Register-PodeEvent -Type Start -Name '<name>' -ScriptBlock {
# inform a portal, write a log, etc
}
# unregister:
Unregister-PodeEvent -Type Start -Name '<name>'
```

The scriptblock supplied to `Register-PodeEvent` also supports `$using:` variables. You can retrieve a registered script using [`Get-PodeEvent`](../../Functions/Events/Get-PodeEvent):

```powershell
$evt = Get-PodeEvent -Type Start -Name '<name>'
```

## Types

### 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).

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.

### 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.

### 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.
25 changes: 17 additions & 8 deletions docs/Tutorials/Middleware/Types/Sessions.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Sessions

Session Middleware is supported on web requests and responses in the form of signed-cookies/headers and server-side data storage. When configured, the middleware will check for a session-cookie/header on the request; if a cookie/header is not found on the request, or the session is not in the store, then a new session is created and attached to the response. If there is a session, then the appropriate data is loaded from the store.
Session Middleware is supported on web requests and responses in the form of signed-cookies/headers and server-side data storage. When configured, the middleware will check for a session-cookie/header on the request; if a cookie/header is not found on the request, or the session is not in storage, then a new session is created and attached to the response. If there is a session, then the appropriate data for that session is loaded from storage.

The duration of the session-cookie/header can be specified, as well as whether to extend the duration each time on each request. A secret-key to sign sessions can be supplied, as well as the ability to specify custom data stores - the default is in-mem, custom could be anything like Redis/MongoDB.
The duration of the session-cookie/header can be specified, as well as whether to extend the duration each time on each request. A secret-key to sign sessions can be supplied (default is a random GUID), as well as the ability to specify custom data stores - the default is in-memory, but custom storage could be anything like Redis/MongoDB/etc.

!!! note
Using sessions via headers is best used with REST APIs and the CLI. It's not advised to use them for normal websites, as browsers don't send back response headers in new requests - unlike cookies.
Expand All @@ -11,13 +11,15 @@ The duration of the session-cookie/header can be specified, as well as whether t

To initialise sessions in Pode you use the [`Enable-PodeSessionMiddleware`](../../../../Functions/Middleware/Enable-PodeSessionMiddleware) function. This function will configure and automatically create Middleware to enable sessions. By default sessions are set using 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.

### Cookies

The following is an example of how to setup session middleware using cookies:

```powershell
Start-PodeServer {
Enable-PodeSessionMiddleware -Secret 'schwifty' -Duration 120 -Extend
Enable-PodeSessionMiddleware -Duration 120 -Extend
}
```

Expand All @@ -29,7 +31,7 @@ Sessions are also supported using headers - useful for CLI requests. The followi

```powershell
Start-PodeServer {
Enable-PodeSessionMiddleware -Secret 'schwifty' -Duration 120 -Extend -UseHeaders
Enable-PodeSessionMiddleware -Duration 120 -Extend -UseHeaders
}
```

Expand All @@ -47,13 +49,13 @@ Within a route, or middleware, you can get the current authenticated sessionId u

You can flag sessions as being strict using the `-Strict` switch. Strict sessions will extend the signing process by also using the client's UserAgent and RemoteIPAddress, to help prevent session sharing on different browsers/consoles.

You can supply the secret value as normal, Pode will automatically extend it for you.
Pode will automatically extend the Secret for signing for you, whether you're using the default GUID, or supplying a specific `-Secret` value.

## Storage

The inbuilt storage for sessions is a simple In-Memory store - with auto-cleanup for expired sessions.
The inbuilt storage for sessions is a simple in-memory store - with auto-cleanup for expired sessions.

If supplied, the `-Storage` parameter is a `psobject` with the following required `NoteProperty` scriptblock members:
You can define a custom storage by supplying a `psobject` to the `-Storage` parameter, and also note that a `-Secret` will be required. The `psobject` supplied should have the following `NoteProperty` scriptblock members:

```powershell
[hashtable] Get([string] $sessionId)
Expand All @@ -64,22 +66,29 @@ If supplied, the `-Storage` parameter is a `psobject` with the following require
For example, the following is a mock up of a Storage for Redis (note that the functions are fake):

```powershell
# create the object
$store = New-Object -TypeName psobject
# add a Get property for retreiving a session's data by SessionId
$store | Add-Member -MemberType NoteProperty -Name Get -Value {
param($sessionId)
return (Get-RedisKey -Key $sessionId)
}
# add a Set property to save a session's data
$store | Add-Member -MemberType NoteProperty -Name Set -Value {
param($sessionId, $data, $expiry)
Set-RedisKey -Key $sessionId -Value $data -TimeToLive $expiry
}
# add a Delete property to delete a session's data by SessionId
$store | Add-Member -MemberType NoteProperty -Name Delete -Value {
param($sessionId)
Remove-RedisKey -Key $sessionId
}
# enable session middleware - a secret is required
Enable-PodeSessionMiddleware -Duration 120 -Storage $store -Secret 'schwifty'
```

## Session Data
Expand All @@ -92,7 +101,7 @@ An example of using sessions in a Route to increment a views counter could be do

```powershell
Start-PodeServer {
Enable-PodeSessionMiddleware -Secret 'schwifty' -Duration 120
Enable-PodeSessionMiddleware -Duration 120
Add-PodeRoute -Method Get -Path '/' -ScriptBlock {
$WebEvent.Session.Data.Views++
Expand Down
3 changes: 2 additions & 1 deletion docs/Tutorials/Restarting/Overview.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
# Overview

There are 3 ways to restart a running Pode server:
There are 4 ways to restart a running Pode server:

1. **Ctrl+R**: If you press `Ctrl+R` on a running server, it will trigger a restart to take place.
1a. On Unix you can use `Shift+R`.
2. [**File Monitoring**](../Types/FileMonitoring): This will watch for file changes, and if enabled will trigger the server to restart.
3. [**Auto-Restarting**](../Types/AutoRestarting): Defined within the `server.psd1` configuration file, you can set schedules for the server to automatically restart.
4. [`Restart-PodeServer`](../../../Functions/Core/Restart-PodeServer): This function lets you manually restart Pode from within the server.

When the server restarts, it will re-invoke the `-ScriptBlock` supplied to the [`Start-PodeServer`](../../../Functions/Core/Start-PodeServer) function. This means the best approach to reload new modules/scripts it to dot-source/[`Use-PodeScript`](../../../Functions/Utilities/Use-PodeScript) your scripts into your server, as any changes to the main `scriptblock` will **not** take place.
6 changes: 3 additions & 3 deletions docs/Tutorials/Routes/Examples/LoginPage.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Creating a Login Page

This is an example of having a website with a login and home page - with a logout button. The pages will all be done using `.pode` files, and authentication will be done using Form authentication with Sessions.
This is an example of having a website with a login and home page - with a logout button. The pages will all be done using `.pode` files, and authentication will be done using [Form authentication](../../../Authentication/Methods/Form) with [Sessions]((../../../Middleware/Types/Sessions)).

!!! info
The full example can be seen on GitHub in [`examples/web-auth-form.ps1`](https://github.com/Badgerati/Pode/blob/develop/examples/web-auth-form.ps1).
Expand Down Expand Up @@ -38,7 +38,7 @@ 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:

```powershell
Enable-PodeSessionMiddleware -Secret 'schwifty' -Duration 120 -Extend
Enable-PodeSessionMiddleware -Duration 120 -Extend
```

Once we have the Session Middleware initialised, we need to setup Form authentication - the username/password here are hard-coded, but normally you would validate against some database. We also specify a `-FailureUrl`, which is the URL to redirect a user to if they try to access a page un-authenticated. The `-SuccessUrl` is the URL to redirect to on successful authentication.
Expand Down Expand Up @@ -110,7 +110,7 @@ Start-PodeServer -Thread 2 {
Set-PodeViewEngine -Type Pode
# setup session middleware
Enable-PodeSessionMiddleware -Secret 'schwifty' -Duration 120 -Extend
Enable-PodeSessionMiddleware -Duration 120 -Extend
# setup form authentication
New-PodeAuthScheme -Form | Add-PodeAuth -Name 'Login' -FailureUrl '/login' -SuccessUrl '/' -ScriptBlock {
Expand Down
4 changes: 2 additions & 2 deletions docs/Tutorials/Routes/Examples/RestApiSessions.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# REST APIs and Sessions

Sessions in Pode are normally done using cookies, but you can also use them via headers as well. This way you can have two endpoints for authentication login/logout, and the rest of your routes depend on a valid SessionId.
[Sessions](../../../Middleware/Types/Sessions) in Pode are normally done using cookies, but you can also use them via headers as well. This way you can have two endpoints for authentication login/logout, and the rest of your routes depend on a valid SessionId.

!!! info
The full example can be seen on GitHub in `examples/web-auth-basic-header.ps1`.
Expand All @@ -26,7 +26,7 @@ Add-PodeEndpoint -Address * -Port 8080 -Protocol Http
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:

```powershell
Enable-PodeSessionMiddleware -Secret 'schwifty' -Duration 120 -Extend -UseHeaders
Enable-PodeSessionMiddleware -Duration 120 -Extend -UseHeaders
```

## Authentication
Expand Down
Loading

0 comments on commit e2dfb55

Please sign in to comment.