Overview
Authorisation can either be used in conjunction with Authentication and Routes, or on its 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
, and then an Access method using 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:
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 is 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 set an Access method where a User must be in every Group that a Route specifies:
New-PodeAccessScheme -Type Group | Add-PodeAccess -Name 'GroupExample' -Match All
User Access Lookup
When using Access methods with Authentication and Routes, Pode will look up 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
:
Type | Property |
---|---|
Role | Roles |
Group | Groups |
Scope | Scopes |
User | Username |
Custom | n/a - you must supply a -Path or -ScriptBlock to New-PodeAccessScheme |
You can override this default lookup in one of two ways, by either supplying a custom property -Path
or a -ScriptBlock
for a more advanced lookup (ie: external sources).
Note
If you're using Access methods in a more adhoc manner via 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
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:
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 elsewhere (ie: external source), then you can supply a -ScriptBlock
on 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
, just the -ArgumentList
values are supplied.
For example, if the Role values you need to retrieve are stored in some SQL database:
$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:
$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
.
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
. 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 is 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:
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 and Routes. When used together, Pode will automatically validate Route Authorisation 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
, and other Route functions, using the -Access
parameter.
On Add-PodeRoute
and Add-PodeRouteGroup
there are also the following parameters: -Role
, -Group
, -Scope
, and -User
. You can supply one or 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:
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:
Invoke-RestMethod -Uri http://localhost:8080/route1 -Method Get -Headers @{ Authorization = 'Basic bW9ydHk6cGlja2xl' }
But calling the following will fail with a 403:
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
. 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, it becomes a new access method that you can supply to -Access
on 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
.
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 an Operations Group:
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 set up a more Custom authorisation policy on Routes you can create a custom Access scheme by supplying -Custom
to New-PodeAccessScheme
, and adding custom access values to a Route using 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
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:
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
.
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:
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
, will supply values as the first set of parameters to the -ScriptBlock
defined on New-PodeAccessScheme
.