From 97a9eb7509a35ac6a7a1538cf41ecf0aa62ff882 Mon Sep 17 00:00:00 2001 From: Matthew Kelly Date: Mon, 29 Jul 2019 23:12:37 +0100 Subject: [PATCH] #314: Updates the docs for authentication --- docs/Tutorials/Authentication/Basic.md | 39 ++-- docs/Tutorials/Authentication/Custom.md | 65 +++--- docs/Tutorials/Authentication/Form.md | 41 ++-- docs/Tutorials/Authentication/Overview.md | 117 +++++------ .../Authentication/Validators/WindowsAD.md | 34 ++-- docs/Tutorials/Middleware/Overview.md | 4 +- docs/Tutorials/Routes/FlashMessages.md | 22 +-- docs/Tutorials/Routes/LoginPage.md | 131 ++++++------ examples/web-auth-form.ps1 | 4 +- src/Public/Authentication.ps1 | 187 ++++++++++++++++++ src/Public/OldMiddleware.ps1 | 75 ------- src/Public/Routes.ps1 | 1 - tests/unit/Authentication.Tests.ps1 | 125 +++++++----- tests/unit/Routes.Tests.ps1 | 4 - 14 files changed, 472 insertions(+), 377 deletions(-) delete mode 100644 src/Public/OldMiddleware.ps1 diff --git a/docs/Tutorials/Authentication/Basic.md b/docs/Tutorials/Authentication/Basic.md index bf4ef2d7d..141d20268 100644 --- a/docs/Tutorials/Authentication/Basic.md +++ b/docs/Tutorials/Authentication/Basic.md @@ -1,54 +1,50 @@ # Basic Authentication -Basic authentication is when you pass an encoded `username:password` value on the header of your requests: `@{ 'Authorization' = 'Basic ' }`. +Basic Authentication is when you pass an encoded `username:password` value on the header of your requests: `@{ 'Authorization' = 'Basic ' }`. ## Setup -To setup and start using Basic authentication in Pode you can call `auth use -t basic` in your server script, the validator script you need to supply will have the username/password passed as arguments to the scriptblock: +To setup and start using Form Authentication in Pode you use the `New-PodeAuthType -Basic` function, and then pipe this into the `Add-PodeAuth` function: ```powershell Start-PodeServer { - auth use login -t basic -v { + New-PodeAuthType -Basic | Add-PodeAuth -Name 'Login' -ScriptBlock { param($username, $password) # check if the user is valid - return @{ 'user' = $user } + return @{ User = $user } } } ``` -By default, Pode will check if the request's header contains an `Authorization` key, and whether the value of that key starts with `Basic`. The `auth use` action can be supplied options via `-o` to override the start name of the value, as well as the encoding that Pode uses. +By default, Pode will check if the Request's header contains an `Authorization` key, and whether the value of that key starts with `Basic`. The `New-PodeAuthType -Basic` function can be supplied parameters to customise this name, as well as the encoding. For example, to use `ASCII` encoding rather than the default `ISO-8859-1` you could do: ```powershell Start-PodeServer { - auth use login -t basic -v { - # check - } -o @{ 'Encoding' = 'ASCII' } + New-PodeAuthType -Basic -Encoding 'ASCII' | Add-PodeAuth -Name 'Login' -ScriptBlock {} } ``` -More options can be seen further below. - ## Validating -Once configured you can start using Basic authentication to validate incoming requests. You can either configure the validation to happen on every `route` as global `middleware`, or as custom `route` middleware. +Once configured you can start using Basic Authentication to validate incoming Requests. You can either configure the validation to happen on every Route as global Middleware, or as custom Route Middleware. -The following will use Basic authentication to validate every request on every `route`: +The following will use Basic Authentication to validate every request on every Route: ```powershell Start-PodeServer { - (auth check login) | Add-PodeMiddleware -Name 'GlobalAuthValidation' + Get-PodeAuthMiddleware -Name 'Login' | Add-PodeMiddleware -Name 'GlobalAuthValidation' } ``` -Whereas the following example will use Basic authentication to only validate requests on specific a `route`: +Whereas the following example will use Basic authentication to only validate requests on specific a Route: ```powershell Start-PodeServer { - Add-PodeRoute -Method Get -Path '/info' -Middleware (auth check login) -ScriptBlock { + Add-PodeRoute -Method Get -Path '/info' -Middleware (Get-PodeAuthMiddleware -Name 'Login') -ScriptBlock { # logic } } @@ -56,14 +52,14 @@ Start-PodeServer { ## Full Example -The following full example of Basic authentication will setup and configure authentication, validate that a users username/password is valid, and then validate on a specific `route`: +The following full example of Basic authentication will setup and configure authentication, validate that a users username/password is valid, and then validate on a specific Route: ```powershell Start-PodeServer { Add-PodeEndpoint -Address *:8080 -Protocol Http # setup basic authentication to validate a user - auth use login -t basic -v { + New-PodeAuthType -Basic | Add-PodeAuth -Name 'Login' -ScriptBlock { param($username, $password) # here you'd check a real user storage, this is just for example @@ -79,7 +75,7 @@ Start-PodeServer { } # check the request on this route against the authentication - Add-PodeRoute -Method Get -Path '/cpu' -Middleware (auth check login) -ScriptBlock { + Add-PodeRoute -Method Get -Path '/cpu' -Middleware (Get-PodeAuthMiddleware -Name 'Login') -ScriptBlock { Write-PodeJsonResponse -Value @{ 'cpu' = 82 } } @@ -89,10 +85,3 @@ Start-PodeServer { } } ``` - -## Use Options - -| Name | Description | Default | -| ---- | ----------- | ------- | -| Encoding | Defines which encoding to use when decoding the Authorization header | ISO-8859-1 | -| Name | Defines the name part of the header, in front of the encoded sting, such as the `Basic` part of `Basic ` | Basic | \ No newline at end of file diff --git a/docs/Tutorials/Authentication/Custom.md b/docs/Tutorials/Authentication/Custom.md index e71966f88..3a61b8d9a 100644 --- a/docs/Tutorials/Authentication/Custom.md +++ b/docs/Tutorials/Authentication/Custom.md @@ -1,21 +1,20 @@ # Custom Authentication -Custom authentication works much like the inbuilt types, but allows you to specify your own parsing logic, as well as any custom options that might be required. +Custom authentication works much like the inbuilt types (Basic and Form), but allows you to specify your own parsing logic, as well as any custom options that might be required. ## Setup and Parsing -To setup and start using Custom authentication in Pode you can set `auth use -c ` in you server script. The `` can be anything you want, even the name of an inbuilt method (it will still use your custom logic!). +To setup and start using Custom authentication in Pode you use the `New-PodeAuthType -Custom` function, and then pipe this into the `Add-PodeAuth` function. -Let's say we wanted something similar to [`Form`](../Form) authentication but it requires a third piece of information: `ClientName`. To setup Custom authentication for this method, you'll need to specify the parsing scriptblock under `-p`, as well as the validator script too. +Let's say we wanted something similar to [`Form`](../Form) Authentication, but it requires a third piece of information: `ClientName`. To setup Custom Authentication for this method, you'll need to specify the parsing logic within the `-ScriptBlock`. -The parsing script will be passed the current web event (containing the `Request`/`Response` objects, much like a `route`). In this script you can parse the request payload/headers for any credential information that needs validating. Once sourced, the data returned from the script should be either a `hashtable` or an `array`; this data will then `splatted` onto the validator scriptblock ([info](../../../Functions/Helpers/Invoke-PodeScriptBlock)): +The `-ScriptBlock` on `New-PodeAuthType` will be passed the current web event (containing the `Request`/`Response` objects, much like a Route). In this script you can parse the Request payload/headers for any credential information that needs validating. Once sourced, the data returned from the script should be either an `array`, which will then splatted onto the `-ScriptBlock` from your `Add-PodeAuth` function: ```powershell Start-PodeServer { - # here we're calling the custom method "client" - auth use -c client -p { - # the current web event, and auth method options supplied - param($event, $opts) + # define a new custom authentication type + $custom_type = New-PodeAuthType -Custom -ScriptBlock { + param($e, $opts) # get client/user/pass field names to get from payload $clientField = (Protect-PodeValue -Value $opts.ClientField -Default 'client') @@ -23,32 +22,34 @@ Start-PodeServer { $passField = (Protect-PodeValue -Value $opts.PasswordField -Default 'password') # get the client/user/pass from the post data - $client = $event.Data.$clientField - $username = $event.Data.$userField - $password = $event.Data.$passField + $client = $e.Data.$clientField + $username = $e.Data.$userField + $password = $e.Data.$passField # return the data, to be passed to the validator script return @($client, $username, $password) - } ` - -v { + } + + # now, add a new custom authentication method + $custom_type | Add-PodeAuth -Name 'Login' -ScriptBlock { param($client, $username, $password) # check if the client is valid - return @{ 'user' = $user } + return @{ User = $user } } } ``` ## Validating -Once configured you can start using the Custom authentication to validate incoming requests. You can either configure the validation to happen on every `route` as global `middleware`, or as custom `route` middleware. +Once configured you can start using the Custom Authentication to validate incoming Requests. You can either configure the validation to happen on every Route as global Middleware, or as custom Route Middleware. -The following will use Custom authentication to validate every request on every `route`: +The following will use Custom Authentication to validate every request on every Route: ```powershell Start-PodeServer { - (auth check client) | Add-PodeMiddleware -Name 'GlobalAuthValidation' + Get-PodeAuthMiddleware -Name 'Login' | Add-PodeMiddleware -Name 'GlobalAuthValidation' } ``` @@ -56,7 +57,7 @@ Whereas the following example will use Custom authentication to only validate re ```powershell Start-PodeServer { - Add-PodeRoute -Method Get -Path '/info' -Middleware (auth check login) -ScriptBlock { + Add-PodeRoute -Method Get -Path '/info' -Middleware (Get-PodeAuthMiddleware -Name 'Login') -ScriptBlock { # logic } } @@ -70,10 +71,9 @@ The following full example of Custom authentication will setup and configure aut Start-PodeServer { Add-PodeEndpoint -Address *:8080 -Protocol Http - # here we're calling the custom method "client" - auth use -c client -p { - # the current web event, and auth method options supplied - param($event, $opts) + # define a new custom authentication type + $custom_type = New-PodeAuthType -Custom -ScriptBlock { + param($e, $opts) # get client/user/pass field names to get from payload $clientField = (Protect-PodeValue -Value $opts.ClientField -Default 'client') @@ -81,23 +81,25 @@ Start-PodeServer { $passField = (Protect-PodeValue -Value $opts.PasswordField -Default 'password') # get the client/user/pass from the post data - $client = $event.Data.$clientField - $username = $event.Data.$userField - $password = $event.Data.$passField + $client = $e.Data.$clientField + $username = $e.Data.$userField + $password = $e.Data.$passField # return the data, to be passed to the validator script return @($client, $username, $password) - } ` - -v { + } + + # now, add a new custom authentication method + $custom_type | Add-PodeAuth -Name 'Login' -ScriptBlock { param($client, $username, $password) # check if the client is valid - return @{ 'user' = $user } + return @{ User = $user } } # check the request on this route against the authentication - Add-PodeRoute -Method Get -Path '/cpu' -Middleware (auth check client) -ScriptBlock { + Add-PodeRoute -Method Get -Path '/cpu' -Middleware (Get-PodeAuthMiddleware -Name 'Login') -ScriptBlock { Write-PodeJsonResponse -Value @{ 'cpu' = 82 } } @@ -129,8 +131,3 @@ Below is an example HTML page that would POST the client/username/password to th ``` - -## Use Options - -!!! info - There are no `use` options for custom types, unless you define your own. \ No newline at end of file diff --git a/docs/Tutorials/Authentication/Form.md b/docs/Tutorials/Authentication/Form.md index ae2357ee7..b0c463118 100644 --- a/docs/Tutorials/Authentication/Form.md +++ b/docs/Tutorials/Authentication/Form.md @@ -1,54 +1,50 @@ # Form Authentication -Form authentication is for when you're using a `
` in HTML, and you submit the form. The method expects a `username` and `password` to be passed from the form input fields. +Form Authentication is for when you're using a `` in HTML, and you submit the form. This Authentication method expects a `username` and `password` to be passed from the form's input fields. ## Setup -To setup and start using Form authentication in Pode you specify `auth use -t form` in your server script, the validator script you need to supply will have the username/password supplied as arguments to the scriptblock: +To setup and start using Form Authentication in Pode you use the `New-PodeAuthType -Form` function, and then pipe this into the `Add-PodeAuth` function: ```powershell Start-PodeServer { - auth use login -t form -v { + New-PodeAuthType -Form | Add-PodeAuth -Name 'Login' -ScriptBlock { param($username, $password) # check if the user is valid - return @{ 'user' = $user } + return @{ User = $user } } } ``` -By default, Pode will check if the request's payload (from POST) contains a `username` and `password` field. The `auth use` action can be supplied options via `-o` to override the names of these fields for anything custom. +By default, Pode will check if the Request's payload contains a `username` and `password` fields. The `New-PodeAuthType -Form` function can be supplied parameters to allow for custom names of these fields. -For example, to look for the field `email` rather than rather than the default `username` you could do: +For example, to look for the field `email` rather than the default `username` you could do: ```powershell Start-PodeServer { - auth use login -t form -v { - # check - } -o @{ 'UsernameField' = 'email' } + New-PodeAuthType -Form -UsernameField 'email' | Add-PodeAuth -Name 'Login' -ScriptBlock {} } ``` -More options can be seen further below. - ## Validating -Once configured you can start using Form authentication to validate incoming requests. You can either configure the validation to happen on every `route` as global `middleware`, or as custom `route` middleware. +Once configured you can start using Form Authentication to validate incoming Requests. You can either configure the validation to happen on every Route as global Middleware, or as custom Route Middleware. -The following will use Form authentication to validate every request on every `route`: +The following will use Form Authentication to validate every request on every Route: ```powershell Start-PodeServer { - (auth check login) | Add-PodeMiddleware -Name 'GlobalAuthValidation' + Get-PodeAuthMiddleware -Name 'Login' | Add-PodeMiddleware -Name 'GlobalAuthValidation' } ``` -Whereas the following example will use Form authentication to only validate requests on specific a `route`: +Whereas the following example will use Form Authentication to only validate requests on specific a Route: ```powershell Start-PodeServer { - Add-PodeRoute -Method Get -Path '/info' -Middleware (auth check login) -ScriptBlock { + Add-PodeRoute -Method Get -Path '/info' -Middleware (Get-PodeAuthMiddleware -Name 'Login') -ScriptBlock { # logic } } @@ -56,14 +52,14 @@ Start-PodeServer { ## Full Example -The following full example of Form authentication will setup and configure authentication, validate that a users username/password is valid, and then validate on a specific `route`: +The following full example of Form authentication will setup and configure authentication, validate that a users username/password is valid, and then validate on a specific Route: ```powershell Start-PodeServer { Add-PodeEndpoint -Address *:8080 -Protocol Http # setup form authentication to validate a user - auth use login -t form -v { + New-PodeAuthType -Form | Add-PodeAuth -Name 'Login' -ScriptBlock { param($username, $password) # here you'd check a real user storage, this is just for example @@ -79,7 +75,7 @@ Start-PodeServer { } # check the request on this route against the authentication - Add-PodeRoute -Method Get -Path '/cpu' -Middleware (auth check login) -ScriptBlock { + Add-PodeRoute -Method Get -Path '/cpu' -Middleware (Get-PodeAuthMiddleware -Name 'Login') -ScriptBlock { Write-PodeJsonResponse -Value @{ 'cpu' = 82 } } @@ -107,10 +103,3 @@ Below is an example HTML page that would POST the username/password to the serve ``` - -## Use Options - -| Name | Description | Default | -| ---- | ----------- | ------- | -| UsernameField | Defines the name of field which the username will be passed in from the `
` | username | -| PasswordField | Defines the name of field which the password will be passed in from the `` | password | \ No newline at end of file diff --git a/docs/Tutorials/Authentication/Overview.md b/docs/Tutorials/Authentication/Overview.md index c681b20a7..187efd076 100644 --- a/docs/Tutorials/Authentication/Overview.md +++ b/docs/Tutorials/Authentication/Overview.md @@ -3,42 +3,57 @@ Authentication can either be sessionless (requiring validation on every request), or session-persistent (only requiring validation once, and then checks against a session signed-cookie). !!! info - To use session-persistent authentication you will also need to use the [`session`](../../../Functions/Middleware/Sessions) middleware. + To use session-persistent authentication you will also need to use Session Middleware. -To setup and use authentication in Pode you need to use the [`auth`](../../../Functions/Middleware/Auth) function and middleware. The `auth` function has two actions: `use` and `check` which are detailed below: +To setup and use authentication in Pode you need to use the `New-PodeAuthType` and `Add-PodeAuth` functions, as well as the `Get-PodeAuthMiddleware` function for defining authentication Middleware. -## Actions +## Functions -### Use +### New-PodeAuthType -The `auth use` action allows you to specify and configure which authentication methods your server will use; you can have many methods configured, defining which one to validate against on the `auth check` action. +The `New-PodeAuthType` function allows you to create and configure Basic/Form authentication types, or you can create your own Custom authentication types. These types can then be used on the `Add-PodeAuth` function. -The make-up of the `use` action is: +An example of creating Basic/Form authentication is as follows: ```powershell -auth use -validator <{}|string> [-options @{}] [-parser {}] [-type ] [-custom] - -# or with aliases: -auth use -v <{}|string> [-o @{}] [-p {}] [-t ] [-c] +Start-PodeServer { + $basic_auth = New-PodeAuthType -Basic + $form_auth = New-PodeAuthType -Form +} ``` -A quick example of using the `use` action for Basic authentication is as follows: +Where as the following example defines a Custom type that retrieves the user credentials from Headers: ```powershell Start-PodeServer { - auth use basic -v { - param($username, $pass) - # logic to check user - return @{ 'user' = $user } + $custom_type = New-PodeAuthType -Custom -ScriptBlock { + param($e, $opts) + + # get client/user/pass field names to get from payload + $clientField = (Protect-PodeValue -Value $opts.ClientField -Default 'client') + $userField = (Protect-PodeValue -Value $opts.UsernameField -Default 'username') + $passField = (Protect-PodeValue -Value $opts.PasswordField -Default 'password') + + # get the client/user/pass from the post data + $client = $e.Data.$clientField + $username = $e.Data.$userField + $password = $e.Data.$passField + + # return the data, to be passed to the validator script + return @($client, $username, $password) } } ``` -or, if you want to use Basic authentication but with a custom name (such as 'login'): +### Add-PodeAuth + +The `Add-PodeAuth` function allows you to add authentication methods to your server. You can have many methods configured, defining which one to validate against using the `Get-PodeAuthMiddleware` function. + +An example of using `Add-PodeAuth` for Basic authentication is as follows: ```powershell Start-PodeServer { - auth use login -t basic -v { + New-PodeAuthType -Basic | Add-PodeAuth -Name 'Login' -ScriptBlock { param($username, $pass) # logic to check user return @{ 'user' = $user } @@ -46,67 +61,37 @@ Start-PodeServer { } ``` -The `` of the authentication method can be anything, so long as you specify the `` as well. The `` specified should be a valid inbuilt method (such as Basic or Form), unless you have stated that the method is custom (`-c`). If you do not specify a `` then the `` is used as the type instead - in which case the name needs to follow the same rules as ``. - -The validator (`-v`) script 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 validator passes, then a `user` needs to be returned from the script via `@{ 'user' = $user }` - if `$null` or a null user is returned then the validator script is assumed to have failed, thus meaning the user will have failed to authenticate. +The `-Name` of the authentication method must be unique. The `-Type` comes from `New-PodeAuthType`, and can also be pied in. -Some authentication methods also have options (`-o`) that can be supplied as a hashtable, such as field name or encoding overrides. Available options will vary between authentication methods, and can be seen on their tutorial pages, such as [`Basic`](../Basic). +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` 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. -If a custom (`-c`) authentication method is used, you *must* supply a parser (`-p`) script which can parse payloads/headers for credentials and then return this data as an array - which will then be supplied to the validator (`-v`) script. +### Get-PodeAuthMiddleware -### Check +The `Get-PodeAuthMiddleware` function allows you to define which authentication method to validate a Request against. It returns valid Middleware, meaning you can either use it on specific Routes, or globally for all routes as Middleware. If this action fails, then a 401 response is returned. -The `auth check` action allows you to define which authentication method to validate a request against. The action returns a valid middleware script, meaning you can either use this action on specific `route` definitions, or globally for all routes as `middleware`. If this action fails, then a 401 response is returned. - -The make-up of the `check` action is: - -```powershell -auth check [-options @{}] - -# or with aliases: -auth check [-o @{}] -``` - -A quick example of using the `check` action against Basic authentication is as follows. The first example sets up the `check` as global middleware, whereas the second example sets up the `check` as custom [`route`](../../../Functions/Core/Route) middleware: +An example of using `Get-PodeAuthMiddleware` against Basic authentication is as follows. The first example sets up global middleware, whereas the second example sets up custom Route Middleware: ```powershell Start-PodeServer { - # 1. apply the auth check as global middleware - (auth check basic) | Add-PodeMiddleware -Name 'GlobalAuthValidation' + # 1. apply as global middleware + Get-PodeAuthMiddleware -Name 'Login' | Add-PodeMiddleware -Name 'GlobalAuthValidation' - # 2. or, apply auth check as custom route middleware - Add-PodeRoute -Method Get -Path '/users' -Middleware (auth check basic) -ScriptBlock { + # 2. or, apply as custom route middleware + Add-PodeRoute -Method Get -Path '/users' -Middleware (Get-PodeAuthMiddleware -Name 'Login') -ScriptBlock { # route logic } } ``` -On success, this action will then allow the `route` logic to be invoked. If [`session`](../../../Functions/Middleware/Session) [`middleware`](../../../Functions/Core/Middleware) has been configured then an authenticated session is also created for future requests, using a signed session-cookie. +On success, it will allow the Route logic to be invoked. If Session Middleware has been configured then an authenticated session is also created for future requests, using a signed session-cookie. -When the user makes another call using the same authenticated session and that cookie is passed, then the `auth check` action will detect the already authenticated session and skip the validator script. If you're using sessions and you don't want the `auth check` to check the session, or store the user against the session, then pass `-o @{ 'Session' = $false }` to the `auth check` call. - -#### Parameters - -The following table contains options that you can supply to an `auth check -o @{}` call - these are all optional: - -| Name | Description | Default | -| ---- | ----------- | ------- | -| FailureUrl | The URL to redirect to should authentication fail | empty | -| FailureMessage | Overrides any error message thrown during authentication validation | empty | -| FailureFlash | If true, any error message thrown will be loaded into the session's [`flash`](../../Misc/FlashMessages) messages under the `auth-error` key | false | -| SuccessUrl | The URL to redirect to should authentication succeed | empty | -| Session | If true, check if the session already has an authenticated user; storing the user in the session if they are authenticated | true | -| Login | If true, check the authentication status in the session and redirect to the SuccessUrl if the user is authenticated. Otherwise proceed to the page with no authentication required | false | -| Logout | If true, purge the session and redirect to the FailureUrl | false | - -!!! info - The `Login` option allows you to have authentication on your login pages, such that if there is no user in a current session then the login page displays - rather then being 401'd. Whereas if there is an authenticated user in the session it will auto-redirect to the `SuccessUrl`. +When the user makes another call using the same authenticated session and that cookie is present, then `Get-PodeAuthMiddleware` will detect the already authenticated session and skip validation. If you're using sessions and you don't want to check the session, or store the user against a session, then use the `-Sessionless` switch. ## Users -After a successful validation, an `Auth` object will be created for use against the Request. This `Auth` object will be accessible via the argument supplied to `routes` adn `middleware` (though it will only be available in middleware created after `auth check`). +After successful validation, an `Auth` object will be created for use against the current web event. This `Auth` object will be accessible via the argument supplied to Routes and Middleware (though it will only be available in Middleware created after the Middleware from `Get-PodeAuthMiddleware` is invoked). -The object will further contain: +The `Auth` object will also contain: | Name | Description | | ---- | ----------- | @@ -117,7 +102,7 @@ The object will further contain: The following example get the user's name from the `Auth` object: ```powershell -Add-PodeRoute -Method Get -Path '/' -Middleware (auth check form) -ScriptBlock { +Add-PodeRoute -Method Get -Path '/' -Middleware (Get-PodeAuthMiddleware -Name 'Login') -ScriptBlock { param($e) Write-PodeViewResponse -Path 'index' -Data @{ @@ -126,14 +111,14 @@ Add-PodeRoute -Method Get -Path '/' -Middleware (auth check form) -ScriptBlock { } ``` -## Inbuilt Validators +## Inbuilt Authenticators -Overtime Pode will start to support inbuilt validators for authentication - such as [Windows Active Directory](../Validators/WindowsAD). More information can be found in the validators section, but to use an inbuilt validator you just need to specify the name to the `auth` function. +Overtime Pode will start to support inbuilt authentication methods - such as [Windows Active Directory](../Validators/WindowsAD). More information can be found in the Validators section. -For example, the below would use the inbuilt validator for Windows AD: +For example, the below would use the inbuilt Windows AD authentication method: ```powershell Start-PodeServer { - auth use login -t basic -v 'windows-ad' + New-PodeAuthType -Basic | Add-PodeAuthWindowsAd -Name 'Login' } -``` \ No newline at end of file +``` diff --git a/docs/Tutorials/Authentication/Validators/WindowsAD.md b/docs/Tutorials/Authentication/Validators/WindowsAD.md index 00fe27eb3..8169fb1f8 100644 --- a/docs/Tutorials/Authentication/Validators/WindowsAD.md +++ b/docs/Tutorials/Authentication/Validators/WindowsAD.md @@ -1,21 +1,21 @@ # Windows AD !!! important - The Windows AD validator is only supported on Windows (PowerShell, and PS Core v6.1+ only). + The Windows AD authentication method is only supported on Windows (PowerShell, and PS Core v6.1+ only). ## Usage -To use the Windows AD validator you supply the name `windows-ad` to the `auth` function's `-v` parameter. The following example will validate a user's credentials, supplied via a web-form against the default DNS domain defined in `$env:USERDNSDOMAIN`: +To use Windows AD authentication you use the `Add-PodeAuthWindowsAd` function. The following example will validate a user's credentials, supplied via a web-form against the default DNS domain defined in `$env:USERDNSDOMAIN`: ```powershell Start-PodeServer { - auth use login -t form -v 'windows-ad' + New-PodeAuthType -Form | Add-PodeAuthWindowsAd -Name 'Login' } ``` ### User Object -The User object returned, and accessible on `routes` and other functions via `$e.Auth.User`, will contain the following information: +The User object returned, and accessible on Routes, and other functions via `$e.Auth.User`, will contain the following information: | Name | Type | Description | | ---- | ---- | ----------- | @@ -27,7 +27,7 @@ The User object returned, and accessible on `routes` and other functions via `$e Such as: ```powershell -Add-PodeRoute -Method Get -Path '/info' -Middleware (auth check login) -ScriptBlock { +Add-PodeRoute -Method Get -Path '/info' -Middleware (Get-PodeAuthMiddleware -Name 'Login') -ScriptBlock { param($e) Write-Host $e.Auth.User.Username } @@ -35,13 +35,11 @@ Add-PodeRoute -Method Get -Path '/info' -Middleware (auth check login) -ScriptBl ### Custom Domain -If you want to supply a custom DNS domain, then you can supply an `FQDN` in the options parameter of the `auth` function: +If you want to supply a custom DNS domain, then you can supply the `-FQDN` parameter: ```powershell Start-PodeServer { - auth use login -t form -v 'windows-ad' -o @{ - 'fqdn' = 'test.example.com' - } + New-PodeAuthType -Form | Add-PodeAuthWindowsAd -Name 'Login' -Fqdn 'test.example.com' } ``` @@ -51,9 +49,7 @@ You can supply a list of group names to validate that user's are a member of the ```powershell Start-PodeServer { - auth use login -t form -v 'windows-ad' -o @{ - 'groups' = @('admins', 'devops') - } + New-PodeAuthType -Form | Add-PodeAuthWindowsAd -Name 'Login' -Groups @('admins', 'devops') } ``` @@ -63,19 +59,17 @@ You can supply a list of authorised usernames to validate a user's access, after ```powershell Start-PodeServer { - auth use login -t form -v 'windows-ad' -o @{ - 'users' = @('jsnow', 'rsanchez') - } + New-PodeAuthType -Form | Add-PodeAuthWindowsAd -Name 'Login' -Users @('jsnow', 'rsanchez') } ``` ## Linux -The inbuilt validator only supports Windows, but you can use libraries such as [Novell.Directory.Ldap.NETStandard](https://www.nuget.org/packages/Novell.Directory.Ldap.NETStandard/) with dotnet core on *nix environments: +The inbuilt authentication only supports Windows, but you can use libraries such as [Novell.Directory.Ldap.NETStandard](https://www.nuget.org/packages/Novell.Directory.Ldap.NETStandard/) with dotnet core on *nix environments: ```powershell Start-PodeServer { - auth use login -t form -v { + New-PodeAuthType -Form | Add-PodeAuth -Name 'Login' -ScriptBlock { param ($username, $password) Add-Type -Path '' @@ -93,8 +87,10 @@ Start-PodeServer { } return @{ - 'user' = @{ 'username' = "\$username" } + User = @{ + Username = "\$username" + } } } } -``` \ No newline at end of file +``` diff --git a/docs/Tutorials/Middleware/Overview.md b/docs/Tutorials/Middleware/Overview.md index fc20bdf42..abef42177 100644 --- a/docs/Tutorials/Middleware/Overview.md +++ b/docs/Tutorials/Middleware/Overview.md @@ -40,11 +40,11 @@ Start-PodeServer { } ``` -Where as the following example is Middleware that will only be run on requests against the `/api` route. Here, it will run Basic authentication on every API request. You'll notice that this time we're piping the `auth check` in, this is because the `auth check` function returns valid Middleware but as a HashTable. +Where as the following example is Middleware that will only be run on requests against the `/api` route. Here, it will run Basic Authentication on every API request. You'll notice that this time we're piping `Get-PodeAuthMiddleware`, this is because it returns valid Middleware. ```powershell Start-PodeServer { - (auth check basic) | Add-PodeMiddleware -Name 'GlobalApiAuthCheck' -Route '/api' + Get-PodeAuthMiddleware -Name 'Main' | Add-PodeMiddleware -Name 'GlobalApiAuthCheck' -Route '/api' } ``` diff --git a/docs/Tutorials/Routes/FlashMessages.md b/docs/Tutorials/Routes/FlashMessages.md index 8f02ff5fa..c97546b33 100644 --- a/docs/Tutorials/Routes/FlashMessages.md +++ b/docs/Tutorials/Routes/FlashMessages.md @@ -73,21 +73,21 @@ With this, the two flash messages for `email-error` and `name-error` are automat ## Authentication -When doing authentication checks, normally if the check fails Pode will throw an error and return with a `401` status code. However, you can tell `auth check` calls to load these errors in the session's flash messages under an `auth-error` key. To do this, you specify the `FailureFlash` option as `$true` to the `auth check` call. +When doing authentication checks, normally if the check fails Pode will throw an error and return with a `401` status code. However, you can tell `Get-PodeAuthMiddleware` to load these errors in the Session's Flash messages under an `auth-error` key. To do this, you specify the `-EnableFlash` switch. -For example, here we have a login page, with the `post` login check. The check flags that any authentication errors should be loaded into the session's flash messages: +For example, here we have a login page, with the `POST` login check. The check flags that any authentication errors should be loaded into the session's flash messages: ```powershell -$auth_login_page = (auth check login -o @{ 'login' = $true; 'successUrl' = '/' }) -Add-PodeRoute -Method Get -Path '/login' -Middleware $auth_login_page -ScriptBlock { - Write-PodeViewResponse 'auth-login' -FlashMessages +$auth_login = Get-PodeAuthMiddleware -Name 'Login' -AutoLogin -SuccessUrl = '/' +Add-PodeRoute -Method Get -Path '/login' -Middleware $auth_login -ScriptBlock { + Write-PodeViewResponse -Path 'auth-login' -FlashMessages } -Add-PodeRoute -Method Post -Path '/login' -Middleware (auth check login -o @{ - 'failureUrl' = '/login'; - 'successUrl' = '/'; - 'failureFlash' = $true; -}) -ScriptBlock {} +Add-PodeRoute -Method Post -Path '/login' -Middleware (Get-PodeAuthMiddleware ` + -Name 'Login' ` + -FailureUrl '/login' ` + -SuccessUrl '/' ` + -EnableFlash) ``` Then, to load the authentication back for the user: @@ -113,4 +113,4 @@ Then, to load the authentication back for the user: -``` \ No newline at end of file +``` diff --git a/docs/Tutorials/Routes/LoginPage.md b/docs/Tutorials/Routes/LoginPage.md index f17e468db..8ff2dc42a 100644 --- a/docs/Tutorials/Routes/LoginPage.md +++ b/docs/Tutorials/Routes/LoginPage.md @@ -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 with Sessions. !!! info The full example can be seen on GitHub in `examples/web-auth-form.ps1`. @@ -37,77 +37,78 @@ Add-PodeEndpoint -Address *: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` 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` function. Here our sessions will last for 2 minutes, and will be extended on each request: ```powershell Enable-PodeSessionMiddleware -Secret 'schwifty' -Duration 120 -Extend ``` -Once we have the session middleware intialised, we need to configure the Form [`authentication`](../../../Functions/Middleware/Auth) - the username/password here are hardcoded, but normally you would validate against a database: +Once we have the Session Middleware initialised, we need to setup Form Authentication - the username/password here are hardcoded, but normally you would validate against a database: ```powershell -auth use form -v { +New-PodeAuthType -Form | Add-PodeAuth -Name 'Login' -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 @{ + User = @{ + ID ='M0R7Y302' + Name = 'Morty' + Type = 'Human' + } + } } # aww geez! no user was found - return $null + return @{ Message = 'Invalid details supplied' } } ``` -This is where it gets interesting, below is the Route for the root (`/`) endpoint. This will check the cookies in the request for a signed session cookie, if one is found then the `index.pode` page is displayed - after incrementing a page-view counter. However, if there is no session, or authentication fails, the user is redirected to the login page: +Below is the Route for the root (`/`) endpoint. This will check the cookies in the request for a signed session cookie, if one is found then the `index.pode` page is displayed - after incrementing a page-view counter. However, if there is no session, or authentication fails, the user is redirected to the login page: ```powershell -$auth_check = (auth check form -o @{ 'failureUrl' = '/login' }) +$auth_check = Get-PodeAuthMiddleware -Name 'Login' -FailureUrl '/login' Add-PodeRoute -Method Get -Path '/' -Middleware $auth_check -ScriptBlock { - param($s) + param($e) - $s.Session.Data.Views++ + $e.Session.Data.Views++ - Write-PodeViewResponse -Path 'index' -Data @{ - 'Username' = $s.Auth.User.Name; - 'Views' = $s.Session.Data.Views; + Write-PodeViewResponse -Path 'auth-home' -Data @{ + Username = $e.Auth.User.Name; + Views = $e.Session.Data.Views; } } ``` -Next we have the login `route`, which is actually two routes. The `GET /login` is the page itself, whereas the `POST /login` is the authentication part (the endpoint the `` element will hit). +Next we have the login Route, which is actually two routes. The `GET /login` is the page itself, whereas the `POST /login` is the authentication part (the endpoint the `` element's action will hit). -For the `POST` route, if authentication passes the user is logged in and redirected to the home page, but if it failed they're taken back to the login page. +For the `POST` Route, if Authentication passes the user is logged in and redirected to the home page, but if it failed they're taken back to the login page. -For the `GET` route we have a `<"login" = $true>` option; this basically means if the user navigates to the login page with an already validated session they're automatically taken back to the home page (the `successUrl`). However if they have no session or authentication fails then instead of a `403` being displayed, the login page is displayed instead. +For the `GET` Route we supply `-AutoLogin`, this basically means if the user navigates to the login page with an already verified session then they're automatically redirected to the home page (the `-SuccessUrl`). However, if they have no session or authentication fails then instead of a `403` being displayed, the login page is displayed instead. ```powershell -$auth_check_login_page = (auth check form -o @{ 'login' = $true; 'successUrl' = '/' }) -Add-PodeRoute -Method Get -Path '/login' -Middleware $auth_check_login_page -ScriptBlock { - param($s) - Write-PodeViewResponse -Path 'login' +# the login page itself +$auth_login = Get-PodeAuthMiddleware -Name 'Login' -AutoLogin -SuccessUrl = '/' +Add-PodeRoute -Method Get -Path '/login' -Middleware $auth_login -ScriptBlock { + Write-PodeViewResponse -Path 'auth-login' -FlashMessages } -$auth_check_login_post = (auth check form -o @{ - 'failureUrl' = '/login'; - 'successUrl' = '/'; -}) - -Add-PodeRoute -Method Post -Path '/login' -Middleware $auth_check_login_post -ScriptBlock {} +# the POST action for the +Add-PodeRoute -Method Post -Path '/login' -Middleware (Get-PodeAuthMiddleware ` + -Name 'Login' ` + -FailureUrl '/login' ` + -SuccessUrl '/' ` + -EnableFlash) ``` -Finally, we have the logout `route`. Here we have another option of `<"logout" = $true>`, which basically just means to kill the session and redirect to the login page: +Finally, we have the logout Route. Here we have another switch of `-Logout`, which just means to kill the session and redirect the user to the login page: ```powershell -$auth_logout = (auth check form -o @{ - 'logout' = $true; - 'failureUrl' = '/login'; -}) - -Add-PodeRoute -Method Post -Path '/logout' -Middleware $auth_logout -ScriptBlock {} +Add-PodeRoute -Method Post -Path '/logout' -Middleware (Get-PodeAuthMiddleware ` + -Name 'Login' ` + -FailureUrl '/login' ` + -Logout) ``` ## Full Server @@ -115,8 +116,6 @@ Add-PodeRoute -Method Post -Path '/logout' -Middleware $auth_logout -ScriptBlock This is the full code for the server above: ```powershell -Import-Module Pode - Start-PodeServer -Thread 2 { Add-PodeEndpoint -Address *:8080 -Protocol Http @@ -127,51 +126,55 @@ Start-PodeServer -Thread 2 { Enable-PodeSessionMiddleware -Secret 'schwifty' -Duration 120 -Extend # setup form authentication - auth use form -v { + New-PodeAuthType -Form | Add-PodeAuth -Name 'Login' -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 @{ + User = @{ + ID ='M0R7Y302' + Name = 'Morty' + Type = 'Human' + } + } } # aww geez! no user was found - return $null + return @{ Message = 'Invalid details supplied' } } # the "GET /" endpoint for the homepage - $auth_check = (auth check form -o @{ 'failureUrl' = '/login' }) + $auth_check = Get-PodeAuthMiddleware -Name 'Login' -FailureUrl '/login' Add-PodeRoute -Method Get -Path '/' -Middleware $auth_check -ScriptBlock { - param($s) + param($e) - $s.Session.Data.Views++ + $e.Session.Data.Views++ - Write-PodeViewResponse -Path 'index' -Data @{ - 'Username' = $s.Auth.User.Name; - 'Views' = $s.Session.Data.Views; + Write-PodeViewResponse -Path 'auth-home' -Data @{ + Username = $e.Auth.User.Name; + Views = $e.Session.Data.Views; } } # the "GET /login" endpoint for the login page - $auth_login_page = (auth check form -o @{ 'login' = $true; 'successUrl' = '/' }) - Add-PodeRoute -Method Get -Path '/login' -Middleware $auth_login_page -ScriptBlock { - Write-PodeViewResponse -Path 'login' + $auth_login = Get-PodeAuthMiddleware -Name 'Login' -AutoLogin -SuccessUrl = '/' + Add-PodeRoute -Method Get -Path '/login' -Middleware $auth_login -ScriptBlock { + Write-PodeViewResponse -Path 'auth-login' -FlashMessages } # the "POST /login" endpoint for user authentication - Add-PodeRoute -Method Post -Path '/login' -Middleware (auth check form -o @{ - 'failureUrl' = '/login'; - 'successUrl' = '/'; - }) -ScriptBlock {} + Add-PodeRoute -Method Post -Path '/login' -Middleware (Get-PodeAuthMiddleware ` + -Name 'Login' ` + -FailureUrl '/login' ` + -SuccessUrl '/' ` + -EnableFlash) # the "POST /logout" endpoint for ending the session - Add-PodeRoute -Method Post -Path '/logout' -Middleware (auth check form -o @{ - 'logout' = $true; - 'failureUrl' = '/login'; - }) -ScriptBlock {} + Add-PodeRoute -Method Post -Path '/logout' -Middleware (Get-PodeAuthMiddleware ` + -Name 'Login' ` + -FailureUrl '/login' ` + -Logout) } ``` @@ -232,4 +235,4 @@ The following are the web pages used above, as well as the CSS style. The web pa body { background-color: rebeccapurple; } -``` \ No newline at end of file +``` diff --git a/examples/web-auth-form.ps1 b/examples/web-auth-form.ps1 index 23900266f..c2227ee03 100644 --- a/examples/web-auth-form.ps1 +++ b/examples/web-auth-form.ps1 @@ -55,8 +55,8 @@ Start-PodeServer -Threads 2 { $e.Session.Data.Views++ Write-PodeViewResponse -Path 'auth-home' -Data @{ - 'Username' = $e.Auth.User.Name; - 'Views' = $e.Session.Data.Views; + Username = $e.Auth.User.Name; + Views = $e.Session.Data.Views; } } diff --git a/src/Public/Authentication.ps1 b/src/Public/Authentication.ps1 index 79933f24d..41af7c6aa 100644 --- a/src/Public/Authentication.ps1 +++ b/src/Public/Authentication.ps1 @@ -1,3 +1,50 @@ +<# +.SYNOPSIS +Create a new type of Authentication. + +.DESCRIPTION +Create a new type of Authentication, which is used to parse the Request for user credentials for validating. + +.PARAMETER Basic +If supplied, will use the inbuilt Basic Authentication credentials retriever. + +.PARAMETER Encoding +The Encoding to use when decoding the Basic Authorization header. + +.PARAMETER HeaderTag +The name of the type of Basic Authentication. + +.PARAMETER Form +If supplied, will use the inbuilt Form Authentication credentials retriever. + +.PARAMETER UsernameField +The name of the Username Field in the payload to retrieve the username. + +.PARAMETER PasswordField +The name of the Password Field in the payload to retrieve the password. + +.PARAMETER Custom +If supplied, will allow you to create a Custom Authentication credentials retriever. + +.PARAMETER ScriptBlock +The ScriptBlock to retrieve user credentials. + +.PARAMETER Options +Any custom Options to supply to a Custom Authentication type's ScriptBlock. + +.EXAMPLE +$basic_auth = New-PodeAuthType -Basic + +.EXAMPLE +$form_auth = New-PodeAuthType -Form -UsernameField 'Email' + +.EXAMPLE +$custom_auth = New-PodeAuthType -Custom -ScriptBlock { + $username = Get-PodeHeader -Name 'X-Username' + $password = Get-PodeHeader -Name 'X-Password' + return @($username, $password) +} +#> function New-PodeAuthType { [CmdletBinding(DefaultParameterSetName='Basic')] @@ -79,6 +126,40 @@ function New-PodeAuthType } } +<# +.SYNOPSIS +Adds a custom Authentication method for verifying users. + +.DESCRIPTION +Adds a custom Authentication method for verifying users. + +.PARAMETER Name +A unique Name for the Authentication method. + +.PARAMETER Type +The Type to use for retrieving credentials (From New-PodeAuthType). + +.PARAMETER ScriptBlock +The ScriptBlock defining logic that retrieves and verifys a user. + +.PARAMETER Options +Any custom Options to supply to the ScriptBlock. + +.EXAMPLE +New-PodeAuthType -Form | Add-PodeAuth -Name 'Main' -ScriptBlock { + param($user, $pass, $opts) + + if ($user -eq 'morty' -and $pass -eq 'evil') { + return @{ + User = @{ + Username = 'morty' + } + } + } + + return @{ Message = 'Invalid details supplied' } +} +#> function Add-PodeAuth { [CmdletBinding()] @@ -125,6 +206,34 @@ function Add-PodeAuth } } +<# +.SYNOPSIS +Adds the inbuilt Windows AD Authentication method for verifying users. + +.DESCRIPTION +Adds the inbuilt Windows AD Authentication method for verifying users. + +.PARAMETER Name +A unique Name for the Authentication method. + +.PARAMETER Type +The Type to use for retrieving credentials (From New-PodeAuthType). + +.PARAMETER Fqdn +A custom FQDN for the DNS of the AD you wish to authenticate against. + +.PARAMETER Groups +An array of Group names to only allow access. + +.PARAMETER Users +An array of Usernames to only allow access. + +.EXAMPLE +New-PodeAuthType -Form | Add-PodeAuthWindowsAd -Name 'WinAuth' + +.EXAMPLE +New-PodeAuthType -Basic | Add-PodeAuthWindowsAd -Name 'WinAuth' -Groups @('Developers') +#> function Add-PodeAuthWindowsAd { [CmdletBinding()] @@ -178,6 +287,19 @@ function Add-PodeAuthWindowsAd } } +<# +.SYNOPSIS +Remove a specific Authentication method. + +.DESCRIPTION +Remove a specific Authentication method. + +.PARAMETER Name +The Name of the Authentication method. + +.EXAMPLE +Remove-PodeAuth -Name 'Login' +#> function Remove-PodeAuth { [CmdletBinding()] @@ -190,6 +312,71 @@ function Remove-PodeAuth $PodeContext.Server.Authentications.Remove($Name) | Out-Null } +<# +.SYNOPSIS +Clear all defined Authentication methods. + +.DESCRIPTION +Clear all defined Authentication methods. + +.EXAMPLE +Clear-PodeAuth +#> +function Clear-PodeAuth +{ + [CmdletBinding()] + param() + + $PodeContext.Server.Authentications.Clear() +} + +<# +.SYNOPSIS +Returns Authentication Middleware that can be used globally, or an Routes. + +.DESCRIPTION +Returns Authentication Middleware that can be used globally, or an Routes. + +.PARAMETER Name +The Name of 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. + +.PARAMETER EnableFlash +If supplied, error messages will be added as Flash messages. + +.PARAMETER Sessionless +If supplied, authenticated users will not be stored in sessions, and sessions will not be used. + +.PARAMETER AutoLogin +If supplied, navigating to a login page with a valid session will redirect to the SuccessUrl. Otherwise the login page will be displayed. + +.PARAMETER Logout +If supplied, the current session will be purged, and the user will be redirected to the FailureUrl. + +.EXAMPLE +Add-PodeRoute -Method Get -Path '/' -Middleware (Get-PodeAuthMiddleware -Name 'Main') -ScriptBlock { + param($e) + $e.Auth.User | Out-Default +} + +.EXAMPLE +Get-PodeAuthMiddleware -Name 'BasicAuth' -Sessionless | Add-PodeMiddeware -Name 'GlobalAuth' + +.EXAMPLE +$login_check = Get-PodeAuthMiddleware -Name 'Main' -SuccessUrl '/' -AutoLogin + +Add-PodeRoute -Method Get -Path '/login' -Middleware $login_check -ScriptBlock { + Write-PodeViewResponse -Path 'login' -FlashMessages +} +#> function Get-PodeAuthMiddleware { [CmdletBinding()] diff --git a/src/Public/OldMiddleware.ps1 b/src/Public/OldMiddleware.ps1 deleted file mode 100644 index df4714006..000000000 --- a/src/Public/OldMiddleware.ps1 +++ /dev/null @@ -1,75 +0,0 @@ -function Auth -{ - param ( - [Parameter(Mandatory=$true)] - [ValidateSet('use', 'check')] - [Alias('a')] - [string] - $Action, - - [Parameter(Mandatory=$true)] - [ValidateNotNullOrEmpty()] - [Alias('n')] - [string] - $Name, - - [Parameter()] - [Alias('v')] - [object] - $Validator, - - [Parameter()] - [Alias('p')] - [scriptblock] - $Parser, - - [Parameter()] - [Alias('o')] - [hashtable] - $Options, - - [Parameter()] - [Alias('t')] - [string] - $Type, - - [switch] - [Alias('c')] - $Custom - ) - - # for the 'use' action, ensure we have a validator. and a parser for custom types - if ($Action -ieq 'use') { - # was a validator passed - if (Test-IsEmpty $Validator) { - throw "Authentication method '$($Name)' is missing required Validator script" - } - - # is the validator a string/scriptblock? - $vTypes = @('string', 'scriptblock') - if ($vTypes -inotcontains (Get-PodeType $Validator).Name) { - throw "Authentication method '$($Name)' has an invalid validator supplied, should be one of: $($vTypes -join ', ')" - } - - # don't fail if custom and type supplied, and it's already defined - if ($Custom) - { - $typeDefined = (![string]::IsNullOrWhiteSpace($Type) -and $PodeContext.Server.Authentications.ContainsKey($Type)) - if (!$typeDefined -and (Test-IsEmpty $Parser)) { - throw "Custom authentication method '$($Name)' is missing required Parser script" - } - } - } - - # invoke the appropriate auth logic for the action - switch ($Action.ToLowerInvariant()) - { - 'use' { - Invoke-PodeAuthUse -Name $Name -Type $Type -Validator $Validator -Parser $Parser -Options $Options -Custom:$Custom - } - - 'check' { - return (Invoke-PodeAuthCheck -Name $Name -Options $Options) - } - } -} \ No newline at end of file diff --git a/src/Public/Routes.ps1 b/src/Public/Routes.ps1 index 9a932c9c5..a26718a9c 100644 --- a/src/Public/Routes.ps1 +++ b/src/Public/Routes.ps1 @@ -72,7 +72,6 @@ function Add-PodeRoute [object[]] $Middleware, - #[Parameter(Mandatory=$true, ParameterSetName='Script')] [Parameter(ParameterSetName='Script')] [scriptblock] $ScriptBlock, diff --git a/tests/unit/Authentication.Tests.ps1 b/tests/unit/Authentication.Tests.ps1 index 309e11a01..ce3649554 100644 --- a/tests/unit/Authentication.Tests.ps1 +++ b/tests/unit/Authentication.Tests.ps1 @@ -9,7 +9,7 @@ Describe 'Set-PodeAuthStatus' { Mock Set-PodeResponseStatus {} It 'Redirects to a failure URL' { - Set-PodeAuthStatus -StatusCode 500 -Options @{'FailureUrl' = 'url'} | Should Be $false + Set-PodeAuthStatus -StatusCode 500 -Options @{'Failure' = @{ 'Url' = 'url'} } | Should Be $false Assert-MockCalled Move-PodeResponseUrl -Times 1 -Scope It Assert-MockCalled Set-PodeResponseStatus -Times 0 -Scope It } @@ -21,7 +21,7 @@ Describe 'Set-PodeAuthStatus' { } It 'Redirects to a success URL' { - Set-PodeAuthStatus -Options @{'SuccessUrl' = 'url'} | Should Be $false + Set-PodeAuthStatus -Options @{'Success' = @{ 'Url' = 'url' } } | Should Be $false Assert-MockCalled Move-PodeResponseUrl -Times 1 -Scope It Assert-MockCalled Set-PodeResponseStatus -Times 0 -Scope It } @@ -33,66 +33,95 @@ Describe 'Set-PodeAuthStatus' { } } -Describe 'Get-PodeAuthBasic' { - Context 'Invalid parameters supplied' { - It 'Throws empty name error' { - { Get-PodeAuthBasic -Name ([string]::Empty) -ScriptBlock {} } | Should Throw 'argument is null' - } - - It 'Throws null name error' { - { Get-PodeAuthBasic -Name $null -ScriptBlock {} } | Should Throw 'argument is null' - } - - It 'Throws null script error' { - { Get-PodeAuthBasic -ScriptBlock $null } | Should Throw 'argument is null' - } +Describe 'Get-PodeAuthBasicType' { + It 'Returns form auth type' { + $result = Get-PodeAuthBasicType + $result | Should Not Be $null + $result.GetType().Name | Should Be 'ScriptBlock' } +} - Context 'Valid parameters' { - It 'Returns auth data' { - $result = Get-PodeAuthBasic -Name 'Basic' -ScriptBlock { Write-Host 'Hello' } - - $result | Should Not Be $null - $result.Name | Should Be 'Basic' +Describe 'Get-PodeAuthFormType' { + It 'Returns basic auth type' { + $result = Get-PodeAuthFormType + $result | Should Not Be $null + $result.GetType().Name | Should Be 'ScriptBlock' + } +} - $result.Parser | Should Not Be $null - $result.Parser.GetType().Name | Should Be 'ScriptBlock' +Describe 'Get-PodeAuthInbuiltMethod' { + It 'Returns Windows AD auth' { + $result = Get-PodeAuthInbuiltMethod -Type WindowsAd + $result | Should Not Be $null + $result.GetType().Name | Should Be 'ScriptBlock' + } +} - $result.Validator | Should Not Be $null - $result.Validator.GetType().Name | Should Be 'ScriptBlock' - $result.Validator.ToString() | Should Be ({ Write-Host 'Hello' }).ToString() - } +Describe 'Get-PodeAuthMiddlewareScript' { + It 'Returns auth middleware' { + $result = Get-PodeAuthMiddlewareScript + $result | Should Not Be $null + $result.GetType().Name | Should Be 'ScriptBlock' } } -Describe 'Get-PodeAuthForm' { - Context 'Invalid parameters supplied' { - It 'Throws empty name error' { - { Get-PodeAuthForm -Name ([string]::Empty) -ScriptBlock {} } | Should Throw 'argument is null' +Describe 'Remove-PodeAuthSession' { + It 'Removes the user, and kills the session' { + Mock Remove-PodeSessionCookie {} + + $event = @{ + Auth = @{ User = @{} } + Session = @{ + Data = @{ + Auth = @{ User = @{} } + } + } + Middleware = @{ + Options = @{ + Failure = @{ + Url = 'http://fake.com' + } + } + } } - It 'Throws null name error' { - { Get-PodeAuthForm -Name $null -ScriptBlock {} } | Should Throw 'argument is null' - } + Remove-PodeAuthSession -Event $event - It 'Throws null script error' { - { Get-PodeAuthForm -ScriptBlock $null } | Should Throw 'argument is null' - } + $event.Auth.Count | Should Be 0 + $event.Auth.User | Should Be $null + $event.Session.Data.Auth | Should be $null + $event.Middleware.Options.Failure.Url | Should Be 'http://fake.com' + + Assert-MockCalled Remove-PodeSessionCookie -Times 1 -Scope It } - Context 'Valid parameters' { - It 'Returns auth data' { - $result = Get-PodeAuthForm -Name 'Form' -ScriptBlock { Write-Host 'Hello' } + It 'Removes the user, and kills the session, redirecting to root' { + Mock Remove-PodeSessionCookie {} + + $event = @{ + Auth = @{ User = @{} } + Session = @{ + Data = @{ + Auth = @{ User = @{} } + } + } + Middleware = @{ + Options = @{ + Failure = @{} + } + } + Request = @{ + Url = @{ AbsolutePath ='/' } + } + } - $result | Should Not Be $null - $result.Name | Should Be 'Form' + Remove-PodeAuthSession -Event $event - $result.Parser | Should Not Be $null - $result.Parser.GetType().Name | Should Be 'ScriptBlock' + $event.Auth.Count | Should Be 0 + $event.Auth.User | Should Be $null + $event.Session.Data.Auth | Should be $null + $event.Middleware.Options.Failure.Url | Should Be '/' - $result.Validator | Should Not Be $null - $result.Validator.GetType().Name | Should Be 'ScriptBlock' - $result.Validator.ToString() | Should Be ({ Write-Host 'Hello' }).ToString() - } + Assert-MockCalled Remove-PodeSessionCookie -Times 1 -Scope It } } \ No newline at end of file diff --git a/tests/unit/Routes.Tests.ps1 b/tests/unit/Routes.Tests.ps1 index 2d2aa7c79..f8e79032d 100644 --- a/tests/unit/Routes.Tests.ps1 +++ b/tests/unit/Routes.Tests.ps1 @@ -286,10 +286,6 @@ Describe 'Add-PodeRoute' { { Add-PodeRoute -Method GET -Path ([string]::Empty) -ScriptBlock {} } | Should Throw 'it is an empty string' } - It 'Throws null logic and middleware error' { - { Add-PodeRoute -Method GET -Path '/' -Middleware $null -ScriptBlock $null } | Should Throw 'because it is null' - } - It 'Throws error when scriptblock and file path supplied' { { Add-PodeRoute -Method GET -Path '/' -ScriptBlock { write-host 'hi' } -FilePath './path' } | Should Throw 'parameter set cannot be resolved' }