Skip to content

Commit

Permalink
Add Request Signing & verify functionality
Browse files Browse the repository at this point in the history
Refactor flow to add the Supports function to signing methods
  • Loading branch information
kjansenpay committed Nov 28, 2023
1 parent 0fd135a commit e227473
Show file tree
Hide file tree
Showing 21 changed files with 1,789 additions and 0 deletions.
861 changes: 861 additions & 0 deletions .editorconfig

Large diffs are not rendered by default.

36 changes: 36 additions & 0 deletions .github/workflows/code-analysis.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: Static Code Analysis

on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]

permissions:
contents: read

jobs:
build:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3

- name: Validate composer.json and composer.lock
run: composer validate --strict

- name: Cache Composer packages
id: composer-cache
uses: actions/cache@v3
with:
path: vendor
key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }}
restore-keys: |
${{ runner.os }}-php-
- name: Install dependencies
run: composer install --prefer-dist --no-progress

- name: Run php tests
run: composer run-script analyse
42 changes: 42 additions & 0 deletions .github/workflows/phpunit.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: PHPUnit tests

on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]

permissions:
contents: read

jobs:
build:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3

- name: Validate composer.json and composer.lock
run: composer validate --strict

- name: Cache Composer packages
id: composer-cache
uses: actions/cache@v3
with:
path: vendor
key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }}
restore-keys: |
${{ runner.os }}-php-
- name: Install dependencies
run: composer install --prefer-dist --no-progress

- name: Run php tests
run: composer run-script phpunit-clover

- name: Upload coverage results to Coveralls
env:
COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }}
run: |
vendor/bin/php-coveralls --coverage_clover=build/logs/clover.xml -v
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
vendor/
coverage-report/
.idea/
.phpunit.result.cache
composer.lock
82 changes: 82 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1 +1,83 @@
# Request Signing

[![Static Code Analysis](https://github.com/paynl/request-signing/actions/workflows/code-analysis.yaml/badge.svg)](https://github.com/paynl/request-signing/actions/workflows/code-analysis.yaml)
[![PHPUnit tests](https://github.com/paynl/request-signing/actions/workflows/phpunit.yaml/badge.svg)](https://github.com/paynl/request-signing/actions/workflows/phpunit.yaml)
[![Coverage Status](https://coveralls.io/repos/github/paynl/request-signing/badge.svg?branch=main)](https://coveralls.io/github/paynl/request-signing?branch=main)

This package adds functionality to sign & verify requests sent by the Pay. platform.

## Requirements

To install this package you need:

* PHP >= 7.4;
* composer.

## Installation

```
composer require paynl/psr-server-request
```

## Usage

The `PayNL\RequestSigning\RequestSigningService` simplifies both signing and verifying requests, avoiding the need for manual class instantiation.

The constructor function of this service requires an array of _SingingMethods_ that you wish to support. Each signing method has different needs and functionality, and which one(s) you opt to support falls under your discretion.

By providing the service with the array of these chosen methods, the service can handle the configuration and functionality. This decouples the setup process from your main application logic, allowing a more streamlined integration.

### Signing
The below code snippet shows the basis of singing a request using this package:
```php
use PayNL\RequestSigning\RequestSigningService;
use PayNL\RequestSigning\Constant\SignatureMethod;
use PayNL\RequestSigning\Methods\HmacSignature;

// Instantiate the RequestSigningService by providing it with your supported SigningMethods
$signingService = new RequestSigningService([
new HmacSignature(
new SignatureKeyRepository() // Your implementation of the HmacSignatureKeyRepositoryInterface
)
]);

// Create your PSR Request, in this example we use the Nyholm PSR7 Request
$request = new \Nyholm\Psr7\Request('POST', 'https://pay.nl', [], '{"hello": "world"}')

// Sign the request providing the id of the key you want to use, the algorithm and the signature method
// This will return the given request with the signature headers attached to it
$signedRequest = $signingService->sign($request, 'SL-1234-1234', 'sha512', SignatureMethod::HMAC);
```

### Verify
The below code snippet shows the basis of verifying a request signed using the above-mentioned method:
```php
use PayNL\RequestSigning\RequestSigningService;

// Instantiate the RequestSigningService by providing it with your supported SigningMethods
$signingService = new RequestSigningService([
new HmacSignature(
new SignatureKeyRepository() // Your implementation of the HmacSignatureKeyRepositoryInterface
)
]);

// Retrieve your request, in this example we'll use the paynl/psr-server-request package to create a PSR Server Request from the PHP Global Variables
$request = create_psr_server_request();

// Pass this request to the verify method. The request argument is optional, if not provided it will attempt to create a request using the above-mentioned method.
$requestValid = $signingService->verify($request);
```

### Exception handling

The request signing service and the underlying signing / verifying methods may throw exceptions when unexpected values are encountered, these are:
- SignatureKeyNotFound, this exception must be thrown when the implementation of the `PayNL\RequestSigning\Repository\SignatureKeyRepositoryInterface` can not find the key based on the provided id;
- UnknownSigningMethodException, this exception will be thrown by the singing / verifying methods when they are requested to sign / verify a request with an algorithm they do not support;
- UnsupportedHashingAlgorithmException, this exception will be thrown by the `PayNL\RequestSigning\RequestSigningService` when it is requested to sign / verify a request with a method it doesn't support.

### Supported Signing / Verifying methods
#### HMAC
The `PayNL\RequestSigning\Methods\HmacSignature` class enables the signing and verification of requests made with HMAC signatures.
To utilize this class's method, a single argument is required for its constructor. This argument is of type `PayNL\RequestSigning\Repository\HmacSignatureKeyRepositoryInterface`.

_That is all you need to know to integrate this package, happy coding!_
56 changes: 56 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
{
"name": "paynl/request-signing",
"description": "A package to sign and verify request sent by PAY.",
"type": "library",
"license": "proprietary",
"minimum-stability": "stable",
"authors": [
{
"name": "Kevin Jansen",
"email": "[email protected]",
"role": "Maintainer"
},
{
"name": "Wesley de Kanter",
"email": "[email protected]",
"role": "Maintainer"
}
],
"support" : {
"email" : "[email protected]"
},
"require": {
"php": "^7.4 | ^8",
"psr/http-factory": "^1.0",
"paynl/psr-server-request": "^1.0"
},
"require-dev": {
"psr/http-message": "^2.0",
"phpstan/phpstan": "^1.10",
"phpunit/phpunit": "^9",
"squizlabs/php_codesniffer": "^3.7",
"php-coveralls/php-coveralls": "^2.7",
"phpunit/phpcov": "^8.2"
},
"autoload": {
"psr-4": {
"PayNL\\RequestSigning\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"PayNL\\RequestSigning\\Tests\\": "tests/"
}
},
"scripts": {
"phpcs": "vendor/bin/phpcs --standard=phpcs.xml",
"phpcbf" : "vendor/bin/phpcbf",
"phpstan": "vendor/bin/phpstan",
"phpunit" : "vendor/bin/phpunit",
"phpunit-clover": "XDEBUG_MODE=coverage vendor/bin/phpunit --coverage-clover build/logs/clover.xml",
"analyse": [
"@phpcs",
"@phpstan"
]
}
}
14 changes: 14 additions & 0 deletions phpcs.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.0"?>
<ruleset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="vendor/squizlabs/php_codesniffer/phpcs.xsd">
<arg name="basepath" value="."/>
<arg name="extensions" value="php"/>
<arg name="parallel" value="80"/>
<arg name="colors"/>

<!-- Show progress of the run and show sniff names -->
<arg value="ps"/>

<file>src</file>

<rule ref="PSR12" />
</ruleset>
5 changes: 5 additions & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
parameters:
level: max
paths:
- src
treatPhpDocTypesAsCertain: false
29 changes: 29 additions & 0 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>

<!-- https://phpunit.readthedocs.io/en/latest/configuration.html -->
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
colors="true"
bootstrap="vendor/autoload.php"
defaultTestSuite="default"
>
<php>
<ini name="display_errors" value="1"/>
<ini name="error_reporting" value="-1"/>
<ini name="memory_limit" value="-1"/>
<server name="APP_ENV" value="test" force="true"/>
<server name="SHELL_VERBOSITY" value="-1"/>
</php>

<testsuites>
<testsuite name="default">
<directory>tests</directory>
</testsuite>
</testsuites>

<coverage processUncoveredFiles="true">
<include>
<directory suffix=".php">src</directory>
</include>
</coverage>
</phpunit>
14 changes: 14 additions & 0 deletions src/Constant/SignatureMethod.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

/**
* This class contains constant values for different signature methods used by PayNL for request signing.
*/

declare(strict_types=1);

namespace PayNL\RequestSigning\Constant;

final class SignatureMethod
{
public const HMAC = 'HMAC';
}
15 changes: 15 additions & 0 deletions src/Exception/SignatureKeyNotFoundException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

declare(strict_types=1);

namespace PayNL\RequestSigning\Exception;

use Exception;

final class SignatureKeyNotFoundException extends Exception
{
public static function forKeyId(string $keyId): self
{
return new self(sprintf('No key found for KeyID [%s].', $keyId));
}
}
15 changes: 15 additions & 0 deletions src/Exception/UnknownSigningMethodException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

declare(strict_types=1);

namespace PayNL\RequestSigning\Exception;

use RuntimeException;

final class UnknownSigningMethodException extends RuntimeException
{
public static function forUnknownMethod(string $method): self
{
return new self(sprintf('Unknown signing method [%s].', $method));
}
}
15 changes: 15 additions & 0 deletions src/Exception/UnsupportedHashingAlgorithmException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

declare(strict_types=1);

namespace PayNL\RequestSigning\Exception;

use Exception;

final class UnsupportedHashingAlgorithmException extends Exception
{
public static function forAlgorithm(string $algorithm): self
{
return new self(sprintf('Unsupported hashing algorithm [%s].', $algorithm));
}
}
Loading

0 comments on commit e227473

Please sign in to comment.