Skip to content
Open
Show file tree
Hide file tree
Changes from 18 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/config/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ open_conext_engine_block:
eb.enable_sso_session_cookie: "%feature_enable_sso_session_cookie%"
eb.feature_enable_idp_initiated_flow: "%feature_enable_idp_initiated_flow%"
eb.stepup.sfo.override_engine_entityid: "%feature_stepup_sfo_override_engine_entityid%"

eb.feature_enable_sram_interrupt: "%feature_enable_sram_interrupt%"

swiftmailer:
transport: "%mailer_transport%"
Expand Down
18 changes: 18 additions & 0 deletions app/config/parameters.yml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ parameters:
feature_enable_consent: true
feature_stepup_sfo_override_engine_entityid: false
feature_enable_idp_initiated_flow: true
feature_enable_sram_interrupt: false

##########################################################################################
## PROFILE SETTINGS
Expand Down Expand Up @@ -311,3 +312,20 @@ parameters:
# used in the authentication log record. The attributeName will be searched in the response attributes and if present
# the log data will be enriched. The values of the response attributes are the final values after ARP and Attribute Manipulation.
auth.log.attributes: []

##########################################################################################
## SRAM Settings
##########################################################################################
## Config for connecting with SBS server
## base_url must end with /. Locations must not start with /.
sram.api_token: xxx
sram.base_url: 'https://engine.dev.openconext.local/functional-testing/'
sram.authz_location: authz
sram.attributes_location: attributes
sram.interrupt_location: interrupt
sram.verify_peer: false
sram.allowed_attributes:
- 'urn:mace:dir:attribute-def:eduPersonEntitlement'
- 'urn:mace:dir:attribute-def:eduPersonPrincipalName'
- 'urn:mace:dir:attribute-def:uid'
- 'urn:oid:1.3.6.1.4.1.24552.500.1.1.1.13'
17 changes: 9 additions & 8 deletions docs/filter_commands.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# EngineBlock Input and Output Command Chains

EngineBlock pre-processes incoming and outgoing SAML Responses using so-called Filters. These filters provide specific,
critical functionality, by invoking a sequence of Filter Commands. However, it is not easily discoverable what these
Filters and Filter Commands exactly do and how they work. This document outlines how these Filters and Filter Commands
critical functionality, by invoking a sequence of Filter Commands. However, it is not easily discoverable what these
Filters and Filter Commands exactly do and how they work. This document outlines how these Filters and Filter Commands
work and what each filter command does.

The chains are:
Expand All @@ -13,11 +13,11 @@ The specific commands can be found in the [`library\EngineBlock\Corto\Filter\Com

## Input and Output Filters

These are called by [`ProxyServer`][ps], through [`filterOutputAssertionAttributes`][fOAA] and
These are called by [`ProxyServer`][ps], through [`filterOutputAssertionAttributes`][fOAA] and
[`filterInputAssertionAttributes`][fIAA] calling [`callAttributeFilter`][cAF], which invokes the actual Filter Commands.

Each Filter then executes Filter Commands in a specified order for Input (between receiving Assertion from IdP and
Consent) and Output (after Consent, before sending Response to SP).
Consent) and Output (after Consent, before sending Response to SP).
What the filter does is:
```
Loop over given Filter Commands, for each Command:
Expand All @@ -30,7 +30,7 @@ Loop over given Filter Commands, for each Command:
set the collabPersonId (either: string stored in session, string found in Response, string found in responseAttributes, string found in nameId response or null, in that order)
execute the command
```
During the loop, the Response, responseAttributes and collabPersonId are retrieved from the previous command and are
During the loop, the Response, responseAttributes and collabPersonId are retrieved from the previous command and are
used by the commands that follows.

A command can also stop filtering by calling `$this->stopFiltering();`
Expand Down Expand Up @@ -67,7 +67,7 @@ Uses:
- EngineBlock_Saml2_ResponseAnnotationDecorator
- responseAttributes

### NormalizeAttributes
### NormalizeAttributes
Convert all OID attributes to URN and remove the OID variant

Depends on:
Expand Down Expand Up @@ -193,7 +193,7 @@ Modifies:
See: [Engineblock Attribute Aggregation](attribute_aggregation.md) for more information.

### EnforcePolicy
Makes a call to the external PolicyDecisionPoint service. This returns a response which details whether or not the
Makes a call to the external PolicyDecisionPoint service. This returns a response which details whether or not the
current User is allowed access to the Service Provider. For more information see [the PDP repository README][pdp-repo]

Depends On:
Expand Down Expand Up @@ -343,7 +343,8 @@ Uses:
- OpenConext\EngineBlock\Metadata\Entity\IdentityProvider
- EngineBlock_Saml2_AuthnRequestAnnotationDecorator


### SRAM test filter
When enabled and the SP has the collab_enabled coin, the SBS integration flow will be activated allowing SRAM integration.



Expand Down
12 changes: 12 additions & 0 deletions library/EngineBlock/Application/DiContainer.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
use OpenConext\EngineBlock\Stepup\StepupEntityFactory;
use OpenConext\EngineBlock\Stepup\StepupGatewayCallOutHelper;
use OpenConext\EngineBlock\Validator\AllowedSchemeValidator;
use OpenConext\EngineBlockBundle\Sbs\SbsAttributeMerger;
use OpenConext\EngineBlockBundle\Sbs\SbsClientInterface;
use Symfony\Component\DependencyInjection\ContainerInterface as SymfonyContainerInterface;

class EngineBlock_Application_DiContainer extends Pimple
Expand Down Expand Up @@ -306,6 +308,16 @@ protected function getSymfonyContainer()
return $this->container;
}

public function getSbsAttributeMerger(): SbsAttributeMerger
{
return $this->container->get('engineblock.sbs.attribute_merger');
}

public function getSbsClient(): SbsClientInterface
{
return $this->container->get('engineblock.sbs.sbs_client');
}

public function getPdpClient()
{
return $this->container->get('engineblock.pdp.pdp_client');
Expand Down
34 changes: 33 additions & 1 deletion library/EngineBlock/Application/TestDiContainer.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
*/

use OpenConext\EngineBlock\Stepup\StepupEndpoint;
use OpenConext\EngineBlockBundle\Configuration\FeatureConfigurationInterface;
use OpenConext\EngineBlockBundle\Pdp\PdpClientInterface;
use OpenConext\EngineBlockBundle\Sbs\SbsClientInterface;

/**
* Creates mocked versions of dependencies for unit testing
Expand All @@ -29,6 +31,16 @@ class EngineBlock_Application_TestDiContainer extends EngineBlock_Application_Di
*/
private $pdpClient;

/**
* @var SbsClientInterface|null
*/
private $sbsClient;

/**
* @var FeatureConfigurationInterface|null
*/
private $featureConfiguration;

public function getXmlConverter()
{
return Phake::mock('EngineBlock_Corto_XmlToArray');
Expand All @@ -49,11 +61,31 @@ public function getPdpClient()
return $this->pdpClient ?? parent::getPdpClient();
}

public function setPdpClient(PdpClientInterface $pdpClient)
public function setPdpClient(?PdpClientInterface $pdpClient)
{
$this->pdpClient = $pdpClient;
}

public function setSbsClient(?SbsClientInterface $sbsClient)
{
$this->sbsClient = $sbsClient;
}

public function getSbsClient(): SbsClientInterface
{
return $this->sbsClient ?? parent::getSbsClient();
}

public function setFeatureConfiguration(?FeatureConfigurationInterface $featureConfiguration)
{
$this->featureConfiguration = $featureConfiguration;
}

public function getFeatureConfiguration(): FeatureConfigurationInterface
{
return $this->featureConfiguration ?? parent::getFeatureConfiguration();
}

public function getConsentFactory()
{
$consentFactoryMock = Phake::mock('EngineBlock_Corto_Model_Consent_Factory');
Expand Down
5 changes: 5 additions & 0 deletions library/EngineBlock/Corto/Adapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,11 @@ public function processWayf()
$this->_callCortoServiceUri('continueToIdp');
}

public function processSRAMInterrupt()
{
$this->_callCortoServiceUri('SRAMInterruptService');
}

public function processConsent()
{
$this->_callCortoServiceUri('processConsentService');
Expand Down
108 changes: 108 additions & 0 deletions library/EngineBlock/Corto/Filter/Command/SRAMInterruptFilter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
<?php

use OpenConext\EngineBlockBundle\Configuration\FeatureConfigurationInterface;
use OpenConext\EngineBlockBundle\Exception\InvalidSbsResponseException;
use OpenConext\EngineBlockBundle\Sbs\Dto\AuthzRequest;
use OpenConext\EngineBlockBundle\Sbs\SbsAttributeMerger;

/**
* Copyright 2021 Stichting Kennisnet
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

class EngineBlock_Corto_Filter_Command_SRAMInterruptFilter extends EngineBlock_Corto_Filter_Command_Abstract
implements EngineBlock_Corto_Filter_Command_ResponseAttributesModificationInterface
{
/**
* {@inheritdoc}
*/
public function getResponseAttributes()
{
return $this->_responseAttributes;
}

public function getResponse()
{
return $this->_response;
}

public function execute(): void
{
$log = EngineBlock_ApplicationSingleton::getLog();

if (!$this->getFeatureConfiguration()->isEnabled('eb.feature_enable_sram_interrupt')) {
return;
}

if ($this->_serviceProvider->getCoins()->collabEnabled() === false) {
return;
}

try {
$request = $this->buildRequest();

$interruptResponse = $this->getSbsClient()->authz($request);

if ($interruptResponse->msg === 'interrupt') {
$log->info("SBS interrupt reason: " . $interruptResponse->message);
$this->_response->setSRAMInterruptNonce($interruptResponse->nonce);
} elseif ($interruptResponse->msg === 'authorized' && !empty($interruptResponse->attributes)) {
$this->_responseAttributes = $this->getSbsAttributeMerger()->mergeAttributes($this->_responseAttributes, $interruptResponse->attributes);
} else {
throw new InvalidSbsResponseException(sprintf('Invalid SBS response received: %s', $interruptResponse->msg));
}
} catch (Throwable $e){
throw new EngineBlock_Exception_SbsCheckFailed('The SBS server could not be queried: ' . $e->getMessage());
}
}

private function getSbsClient()
{
return EngineBlock_ApplicationSingleton::getInstance()->getDiContainer()->getSbsClient();
}

private function getFeatureConfiguration(): FeatureConfigurationInterface
{
return EngineBlock_ApplicationSingleton::getInstance()->getDiContainer()->getFeatureConfiguration();
}

private function getSbsAttributeMerger(): SbsAttributeMerger
{
return EngineBlock_ApplicationSingleton::getInstance()->getDiContainer()->getSbsAttributeMerger();
}

/**
* @return AuthzRequest
* @throws EngineBlock_Corto_ProxyServer_Exception
*/
private function buildRequest(): AuthzRequest
{
$attributes = $this->getResponseAttributes();
$id = $this->_request->getId();

$user_id = $this->_collabPersonId ?? "";
$eppn = $attributes['urn:mace:dir:attribute-def:eduPersonPrincipalName'][0] ?? "";
$continue_url = $this->_server->getUrl('SRAMInterruptService', '') . "?ID=$id";
$service_id = $this->_serviceProvider->entityId;
$issuer_id = $this->_identityProvider->entityId;

return AuthzRequest::create(
$user_id,
$eppn,
$continue_url,
$service_id,
$issuer_id
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
* limitations under the License.
*/

use OpenConext\EngineBlock\Metadata\Entity\ServiceProvider;

/**
* Validate if the IDP sending this response is allowed to connect to the SP that made the request.
**/
Expand All @@ -24,6 +26,11 @@ class EngineBlock_Corto_Filter_Command_ValidateAllowedConnection extends EngineB
public function execute()
{
$sp = $this->_serviceProvider;

if ($this->sbsFlowActive($sp)) {
return;
}

// When dealing with an SP that acts as a trusted proxy, we should perform the validatoin on the proxying SP
// and not the proxy itself.
if ($sp->getCoins()->isTrustedProxy()) {
Expand All @@ -41,4 +48,9 @@ public function execute()
);
}
}

private function sbsFlowActive(ServiceProvider $sp)
{
return $sp->getCoins()->collabEnabled();
}
}
4 changes: 4 additions & 0 deletions library/EngineBlock/Corto/Filter/Input.php
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,10 @@ public function getCommands()
$diContainer->getAttributeAggregationClient()
),

// Check if we need to callout to SRAM to enforce AUP's
// Add SRAM attributes if not
new EngineBlock_Corto_Filter_Command_SRAMInterruptFilter(),

// Check if the Policy Decision Point needs to be consulted for this request
new EngineBlock_Corto_Filter_Command_EnforcePolicy(),

Expand Down
Loading