Skip to content

Commit

Permalink
Improve CI & PHPDoc
Browse files Browse the repository at this point in the history
  • Loading branch information
maxhelias committed Apr 7, 2023
1 parent a70c467 commit 1835307
Show file tree
Hide file tree
Showing 66 changed files with 1,190 additions and 993 deletions.
10 changes: 5 additions & 5 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
# https://www.kernel.org/pub/software/scm/git/docs/gitattributes.html

# Ignore all test and documentation with "export-ignore".
/.github export-ignore
/tests export-ignore
/.gitattributes export-ignore
/.gitignore export-ignore
/phpunit.xml.dist export-ignore
/.github export-ignore
/tests export-ignore
/.gitattributes export-ignore
/.gitignore export-ignore
/phpunit.xml export-ignore
22 changes: 22 additions & 0 deletions .github/stale.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 60

# Number of days of inactivity before a stale issue is closed
daysUntilClose: 7

# Issues with these labels will never be considered stale
exemptLabels:
- bug
- enhancement
- RFC

# Label to use when marking an issue as stale
staleLabel: stale

# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false
43 changes: 0 additions & 43 deletions .github/workflows/ci.yml

This file was deleted.

93 changes: 93 additions & 0 deletions .github/workflows/phpunit.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
name: PHPUnit

on:
push:
pull_request:

concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true

jobs:

test:
name: "PHP ${{ matrix.php }} - Symfony ${{ matrix.symfony }}"
runs-on: ubuntu-latest
continue-on-error: ${{ matrix.can-fail }}

strategy:
fail-fast: false
matrix:
include:
# Lowest Deps
- php: 8.0
symfony: 5.4.*
coverage: 'none'
composer-flags: '--prefer-stable --prefer-lowest'
can-fail: false
# LTS with latest stable PHP
- php: latest
symfony: 5.4.*
coverage: 'none'
composer-flags: '--prefer-stable'
can-fail: false
# Active release
- php: latest
symfony: 6.2.*
coverage: pcov
composer-flags: '--prefer-stable'
can-fail: false
# Development release
- php: nightly
symfony: 6.3.*@dev
coverage: 'none'
composer-flags: ''
can-fail: true

steps:
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 2

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
tools: composer:v2, flex
coverage: ${{ matrix.coverage }}
ini-values: date.timezone=UTC,memory_limit=-1,session.gc_probability=0,apc.enable_cli=1,zend.assertions=1
env:
fail-fast: true

- name: Set Composer stability
if: matrix.symfony == '6.3.*@dev'
run: "composer config minimum-stability dev"

- name: Get composer cache directory
id: composer-cache
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT

- name: Cache composer dependencies
uses: actions/cache@v3
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: php-${{ matrix.php }}-symfony-${{ matrix.symfony }}-composer-${{ hashFiles('**/composer.json') }}
restore-keys: php-${{ matrix.php }}-symfony-${{ matrix.symfony }}-composer-

- name: Install Composer dependencies
run: composer update ${{ matrix.composer-flags }} --no-interaction --no-progress --optimize-autoloader
env:
SYMFONY_REQUIRE: ${{ matrix.symfony }}

- name: Run tests
run: composer test

- name: Monitor coverage
if: matrix.coverage != 'none'
uses: slavcodev/coverage-monitor-action@v1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
coverage_path: coverage-report.xml
threshold_alert: 60
threshold_warning: 80
9 changes: 3 additions & 6 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
/.idea/
/tests/App/var
/tests/coverage/
/vendor/
.DS_Store
composer.lock
.php_cs.cache
.phpunit.result.cache
phpunit.xml
composer.lock
coverage-report.xml
12 changes: 5 additions & 7 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
MIT License

Copyright (c) [year] [fullname]
Copyright (c) 2021-present OD&B

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
Expand All @@ -17,5 +15,5 @@ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
106 changes: 41 additions & 65 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Doctrine Ciphersweet Encryption Bundle

## Introduction

This bundle aims to make life easier to developers who want to set encrypted fields in their entities thanks to Ciphersweet library.
This bundle is inspired from the talk given at Afup ForumPHP : [REX sur le chiffrement de base de données](https://afup.org/talks/3455-rex-sur-le-chiffrement-de-base-de-donnees).
We also used the WIP public repository : https://github.com/PhilETaylor/doctrine-ciphersweet.
Expand Down Expand Up @@ -40,7 +41,8 @@ return [
## Usage

### 1. Setup DOCTRINE_CIPHERSWEET_KEY environment key
This bundle comes with several commands and annotations but first of all, you'll need to setup the `DOCTRINE_CIPHERSWEET_KEY` secret environment key.

This bundle comes with several commands and annotations/attributes but first of all, you'll need to setup the `DOCTRINE_CIPHERSWEET_KEY` secret environment key.
First of all, you can just init a random one in your environment file.

```php
Expand All @@ -60,31 +62,33 @@ php bin/console odb:enc:generate-string-key | php bin/console secrets:set DOCTRI
```
Then remove the entry in your .env file

### 2. Add annotations to your entities
This bundle comes with 2 annotations in order to set encryption fields :
### 2. Add annotations / attributes to your entities

This bundle comes with 2 annotations / attributes in order to set encryption fields :

- @EncryptedField : Marks the field as encrypted and will automatically use Ciphersweet library to encrypt/decrypt on onFlush and onLoad events
- @IndexableField : Marks the field as searchable and several indexes can be generated in a separate table in order to search by terms starting by or ending by.
- **EncryptedField** : Marks the field as encrypted and will automatically use Ciphersweet library to encrypt/decrypt on onFlush and onLoad events
- **IndexableField** : Marks the field as searchable and several indexes can be generated in a separate table in order to search by terms starting by or ending by.

#### @EncrytedField
This annotation comes with 3 options:
#### EncrytedField
This annotation/attribute comes with 3 options:

- (int) $filterBits : Number of bits used for encryption (Default : 32)
- (bool) $indexable : Activate a default index for exact search apart of the @IndexableField annotation. If true, will try to set Index string data into a field named with a "_bi" suffix
- (string) $mappedTypedProperty: If you want to encrypt data other than strings (other types currenty supported : int, float, bool), you'll need to set a string field used by doctrine for persistance purpose instead of your raw field. Then use this parameter to automatically hydrate decrypted data into the target field.
- (int) $filterBits : Number of bits used for encryption. (Default : `32`)
- (bool) $indexable : Activate a default index for exact search apart of the **IndexableField** annotation. If true, will try to set **index** string data into a field named with a "_bi" suffix. (Default : `true`)
- (string) $mappedTypedProperty: If you want to encrypt data other than strings (other types currenty supported : int, float, bool), you'll need to set a string field used by doctrine for persistance purpose instead of your raw field. Then use this parameter to automatically hydrate decrypted data into the target field. (Default : `null`)

#### @IndexableField
This annotation comes with 4 options:
#### IndexableField
This annotation/attribute comes with 4 options:

- (bool) $autoRefresh : Automatically regenerate related indexes to an entity upon persist or update event.
- (string) $indexesEntityClass : Name of the entity class that will store the indexes (can be mutualized)
- (string) $valuePreprocessMethod : Before indexes generation, you may need to clean your input in order to reduce the number of indexes to generate (trim value, slug it, etc.). You can do it by setting this option. For the moment, the method mention can only by related to the current entity class.
- (array) $indexesGenerationMethods : List of methods used to generate several searchable values from the original one. For example the `ValueStartingByGenerator` can take the value "abcdef" in order to generate indexes for ["a", "ab", "abc', "abcd", ...]. So that you can search entities with a field starting by those values.
- (bool) $fastIndexing : If true, will use a faster indexing method.
- (bool) $autoRefresh : Automatically regenerate related indexes to an entity upon persist or update event. (Default : `true`)
- (array) $indexesGenerationMethods : List of methods used to generate several searchable values from the original one. For example the `ValueStartingByGenerator` can take the value "abcdef" in order to generate indexes for ["a", "ab", "abc', "abcd", ...]. So that you can search entities with a field starting by those values. (Default : `[]`)
- (string) $valuePreprocessMethod : Before indexes generation, you may need to clean your input in order to reduce the number of indexes to generate (trim value, slug it, etc.). You can do it by setting this option. For the moment, the method mention can only by related to the current entity class. (Default : `null`)
- (bool) $fastIndexing : If true, will use a faster indexing method. (Default : `true`)

### 3. Generating indexes

To make the entities searchable, the library provides a feature called "Blind Index" which is a unique index calculated from the original value.
By default, we provide a default index field to every encrypted ones (using the $indexable option). If you need to setup a search of values starting by a term, you'll need the `@IndexableField` annotation and set a dedicated indexes table.
By default, we provide a default index field to every encrypted ones (using the $indexable option). If you need to setup a search of values starting by a term, you'll need the `IndexableField` annotation/attribute and set a dedicated indexes table.
This dedicated entity must implement the `Odandb\DoctrineCiphersweetEncryptionBundle\Entity\IndexedEntityInterface` and you can use the `Odandb\DoctrineCiphersweetEncryptionBundle\Entity\IndexedEntityTrait` to make your life easier.

Basically, this table will be composed of those columns :
Expand Down Expand Up @@ -117,55 +121,38 @@ In this mode, the command will split the work in smaller chuncks and start subpr
Refer to the command for more informations.

### 4. Here is a full example :

```php
/**
*
* @ORM\Entity(repositoryClass="App\Repository\MySecretEntityRepository")
* @ORM\Table(indexes={
* @ORM\Index(name="anum_blind_idx", columns={"account_number_bi"}),
* })
*/
#[ORM\Entity(repositoryClass: App\Repository\MySecretEntityRepository::class)
#[ORM\Index(name: 'anum_blind_idx', columns: ['account_number_bi'])]
class MySecretEntity
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer')]
private int $id;

/**
* @var string
* @ORM\Column(type="string", length=36)
*/
#[ORM\Column(type: 'string', length: 36)]
private string $uuid;

/**
* @ORM\Column(type="string")
*
* @EncryptedField
* @IndexableField(indexesEntityClass="App\Entity\MySecretEntityIndexes", autoRefresh=false, indexesGenerationMethods={"ValueStartingBy"}, valuePreprocessMethod="cleanAccountNumber")
*/
#[ORM\Column(type: 'string')]
#[EncryptedField]
#[IndexableField(indexesEntityClass: App\Entity\MySecretEntityIndexes::class, autoRefresh: false, indexesGenerationMethods: ['ValueStartingBy'], valuePreprocessMethod: 'cleanAccountNumber')]
private string $accountNumber;

/**
* @ORM\Column(type="string", length=10)
*/
#[ORM\Column(type: 'string', length: 10)]
private string $accountNumberBi;

private int $secretNumber;

/**
* @ORM\Column(type="string")
*
* @EncryptedField(mappedTypedProperty="secretNumber", indexable=false)
*/
#[ORM\Column(type: 'string')]
#[EncryptedField(mappedTypedProperty: 'secretNumber', indexable: false)]
private string $secretNumberEncrypted;

/**
* @var Collection|null
* @ORM\OneToMany(targetEntity="MySecretEntityIndexes", mappedBy="targetEntity", cascade={"persist"})
*/
#[ORM\OneToMany(targetEntity: MySecretEntityIndexes::class, mappedBy: 'targetEntity', cascade: ['persist'])]
private ?Collection $indexes;

/**
Expand All @@ -191,33 +178,22 @@ class MySecretEntity

use Odandb\DoctrineCiphersweetEncryptionBundle\Entity\IndexedEntityInterface;
use Odandb\DoctrineCiphersweetEncryptionBundle\Entity\IndexedEntityTrait;

/**
* Class storing indexes for MySecretEntity.
*
* @ORM\Entity(repositoryClass="App\Repository\MySecretEntityIndexesRepository")
* @ORM\Table(indexes={
* @ORM\Index(name="blind_idx", columns={"index_bi"}),
* @ORM\Index(name="field_and_blind_idx", columns={"fieldname", "index_bi"})
* })
*/
#[ORM\Entity(repositoryClass: App\Repository\MySecretEntityIndexesRepository::class)
#[ORM\Index(name: 'blind_idx', columns: ['index_bi'])]
#[ORM\Index(name: 'field_and_blind_idx', columns: ['fieldname', 'index_bi'])]
class MySecretEntityIndexes implements IndexedEntityInterface
{
use IndexedEntityTrait;

/**
* @var MySecretEntity|null
*
* @ORM\ManyToOne(targetEntity="App\Entity\MySecretEntity", inversedBy="indexes")
* @ORM\JoinColumn(name="target_entity_id", referencedColumnName="id", onDelete="CASCADE")
*/
#[ORM\ManyToOne(targetEntity: App\Entity\MySecretEntity::class, inversedBy: 'indexes')]
#[ORM\JoinColumn(name: 'target_entity_id', referencedColumnName: 'id', onDelete: 'CASCADE')]
protected object $targetEntity;
}
```

## More to come

- [ ] Tests
- [ ] Add Indexes generator methods
- [ ] Your suggestions here ...


Loading

0 comments on commit 1835307

Please sign in to comment.