├── .scrutinizer.yml ├── .travis.yml ├── LICENSE ├── README.md ├── composer.json ├── phpunit.xml └── src ├── AbstractAnonymizer.php ├── Commands ├── AnonymizationInstallCommand.php ├── DbAnonymizeCommand.php ├── MakeAnonymizerCommand.php └── stubs │ ├── DatabaseAnonymizer.stub │ ├── UsersAnonymizer.stub │ └── anonymizer.stub └── ServiceProvider.php /.scrutinizer.yml: -------------------------------------------------------------------------------- 1 | filter: 2 | paths: 3 | - 'src/*' 4 | excluded_paths: 5 | - 'vendor/*' 6 | - 'tests/*' 7 | tools: 8 | php_cs_fixer: 9 | config: { level: psr2 } 10 | checks: 11 | php: 12 | code_rating: true 13 | duplication: true -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 5.6 5 | - 7.0 6 | - 7.1 7 | - 7.2 8 | - 7.3 9 | 10 | before_script: 11 | - composer self-update 12 | - composer install --prefer-source --no-interaction 13 | 14 | script: 15 | - vendor/bin/phpunit 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Nekrasov Ilya 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Latest Stable Version](https://poser.pugx.org/arrilot/laravel-data-anonymization/v/stable.svg)](https://packagist.org/packages/arrilot/laravel-data-anonymization/) 2 | [![Total Downloads](https://img.shields.io/packagist/dt/arrilot/laravel-data-anonymization.svg?style=flat)](https://packagist.org/packages/Arrilot/laravel-data-anonymization) 3 | [![Scrutinizer Quality Score](https://scrutinizer-ci.com/g/arrilot/laravel-data-anonymization/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/arrilot/laravel-data-anonymization/) 4 | 5 | # Laravel Data Anonymization 6 | 7 | * This is a bridge package for a full integration of [arrilot/data-anonymization](https://github.com/arrilot/data-anonymization) into Laravel framework. 8 | 9 | ## Installation 10 | 11 | 1. ```composer require arrilot/laravel-data-anonymization``` 12 | 13 | 2. Add `"Database\\Anonymization\\": "database/anonymization/",` to `composer.json -> autoload -> psr-4` 14 | 15 | 4. `php artisan anonymization:install` 16 | 17 | 18 | ## Usage 19 | 20 | The package is designed to be as much consistent with Laravel built-in seeders as possible. 21 | 22 | ### Bootstrapping 23 | 24 | `php artisan anonymization:install` creates two files: 25 | 26 | 1) `database/anonymization/DatabaseAnonymizer.php` 27 | 28 | ```php 29 | call(UserTableAnonymizer::class); 45 | } 46 | } 47 | 48 | ``` 49 | 50 | 2) `database/anonymization/UserTableAnonymizer.php` 51 | 52 | ```php 53 | table('users', function (Blueprint $table) { 72 | 73 | $table->column('email')->replaceWith(function(Faker $faker) { 74 | return $faker->unique()->email; 75 | }); 76 | 77 | $table->column('name')->replaceWith('John Doe'); 78 | }); 79 | } 80 | } 81 | 82 | ``` 83 | 84 | `DatabaseAnonymizer` is an entry point into anonymization. It runs other anonymizers. 85 | `UsersAnonymizer` is a useful built-in example. You can modify it and create other anonymizers for other tables using generator. 86 | 87 | ### Generator command 88 | 89 | `php artisan make:anonymizer AccountsAnonymizer`. Similar to `make:seeder` 90 | 91 | ### Running the anonymization 92 | 93 | Anonymization is performed using `php artisan db:anonymize` command. 94 | Its signature is identical with `db:seed` command. 95 | 96 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "arrilot/laravel-data-anonymization", 3 | "license": "MIT", 4 | "description": "Laravel bridge for arrilot/data-anonymization", 5 | "keywords": [ 6 | "anonymization", 7 | "database", 8 | "laravel" 9 | ], 10 | "authors": [ 11 | { 12 | "name": "Nekrasov Ilya", 13 | "email": "nekrasov.ilya90@gmail.com" 14 | } 15 | ], 16 | "homepage": "https://github.com/arrilot/laravel-data-anonymization", 17 | "require": { 18 | "php": ">=5.5.9", 19 | "arrilot/data-anonymization": "~1.0", 20 | "illuminate/support": ">=8.0", 21 | "illuminate/contracts": ">=8.0", 22 | "illuminate/console": ">=8.0" 23 | }, 24 | "require-dev": { 25 | "phpunit/phpunit": "~4.0", 26 | "mockery/mockery": "~0.9" 27 | }, 28 | "autoload": { 29 | "psr-4": { 30 | "Arrilot\\LaravelDataAnonymization\\": "src/" 31 | } 32 | }, 33 | "autoload-dev": { 34 | "psr-4": { 35 | "Arrilot\\Tests\\LaravelDataAnonymization\\": "tests/" 36 | } 37 | }, 38 | "extra": { 39 | "laravel": { 40 | "providers": [ 41 | "Arrilot\\LaravelDataAnonymization\\ServiceProvider" 42 | ] 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 15 | tests 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/AbstractAnonymizer.php: -------------------------------------------------------------------------------- 1 | core = $core; 32 | } 33 | 34 | /** 35 | * Run the anonymization. 36 | * 37 | * @return void 38 | */ 39 | abstract public function run(); 40 | 41 | /** 42 | * Call selected anonymizer. 43 | * 44 | * @param string $class 45 | * 46 | * @return void 47 | */ 48 | public function call($class) 49 | { 50 | $this->resolve($class)->run(); 51 | 52 | if (isset($this->command)) { 53 | $this->command->getOutput()->writeln("Anonymized: $class"); 54 | } 55 | } 56 | 57 | /** 58 | * Describe a table with a given callback. 59 | * 60 | * @param string $name 61 | * @param callable $callback 62 | * 63 | * @return void 64 | */ 65 | public function table($name, callable $callback) 66 | { 67 | $this->core->table($name, $callback); 68 | } 69 | 70 | /** 71 | * Set the console command instance. 72 | * 73 | * @param \Illuminate\Console\Command $command 74 | * 75 | * @return $this 76 | */ 77 | public function setCommand(Command $command) 78 | { 79 | $this->command = $command; 80 | 81 | return $this; 82 | } 83 | 84 | /** 85 | * Resolve an instance of the given seeder class. 86 | * 87 | * @param string $class 88 | * 89 | * @return AbstractAnonymizer 90 | */ 91 | protected function resolve($class) 92 | { 93 | $instance = new $class($this->core); 94 | 95 | if (isset($this->command)) { 96 | $instance->setCommand($this->command); 97 | } 98 | 99 | return $instance; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/Commands/AnonymizationInstallCommand.php: -------------------------------------------------------------------------------- 1 | files = $files; 51 | $this->composer = $composer; 52 | } 53 | 54 | public function handle() 55 | { 56 | return $this->fire(); 57 | } 58 | 59 | /** 60 | * Execute the console command. 61 | * 62 | * @return void 63 | */ 64 | public function fire() 65 | { 66 | $dir = $this->laravel->databasePath() . '/anonymization'; 67 | 68 | $this->createDirectory($dir); 69 | $this->createAnonymizer($dir, 'DatabaseAnonymizer'); 70 | $this->createAnonymizer($dir, 'UsersAnonymizer'); 71 | 72 | $this->composer->dumpAutoloads(); 73 | 74 | $this->info("Installation completed"); 75 | 76 | } 77 | 78 | /** 79 | * Create directory for anonymizers. 80 | * 81 | * @param string $dir 82 | */ 83 | protected function createDirectory($dir) 84 | { 85 | if ($this->files->isDirectory($dir)) { 86 | $this->error("Directory {$dir} already exists"); 87 | 88 | return; 89 | } 90 | 91 | $this->files->makeDirectory($dir); 92 | } 93 | 94 | /** 95 | * Create Anonymizer class. 96 | * 97 | * @param string $dir 98 | * 99 | * @return void 100 | */ 101 | protected function createAnonymizer($dir, $class) 102 | { 103 | $path = "{$dir}/{$class}.php"; 104 | 105 | if ($this->files->exists($path)) { 106 | $this->error("File {$path} already exists"); 107 | 108 | return; 109 | } 110 | 111 | $this->files->copy(__DIR__.'/stubs/'.$class.'.stub', $path); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/Commands/DbAnonymizeCommand.php: -------------------------------------------------------------------------------- 1 | fire(); 50 | } 51 | 52 | /** 53 | * Execute the console command. 54 | * 55 | * @return void 56 | */ 57 | public function fire() 58 | { 59 | if (! $this->confirmToProceed()) { 60 | return; 61 | } 62 | 63 | $coreAnonymizer = $this->getCoreAnonymizer(); 64 | 65 | // collect configuration into $coreAnonymizer 66 | $this->getAnonymizer($coreAnonymizer)->run(); 67 | 68 | // change database 69 | $coreAnonymizer->run(); 70 | } 71 | 72 | /** 73 | * Get an anonymizer instance from the container. 74 | * 75 | * @param CoreAnonymizer $coreAnonymizer 76 | * 77 | * @return AbstractAnonymizer 78 | */ 79 | protected function getAnonymizer($coreAnonymizer) 80 | { 81 | $className = $this->input->getOption('class'); 82 | 83 | return (new $className($coreAnonymizer))->setCommand($this); 84 | } 85 | 86 | /** 87 | * Get core anonymizer from parent package. 88 | * 89 | * @return CoreAnonymizer 90 | */ 91 | protected function getCoreAnonymizer() 92 | { 93 | $db = $this->getDatabaseConfiguration($this->input->getOption('database')); 94 | 95 | $databaseInteractor = new SqlDatabase($db['dsn'], $db['username'], $db['password']); 96 | $generator = $this->laravel->make(FakerGenerator::class); 97 | 98 | return new CoreAnonymizer($databaseInteractor, $generator); 99 | } 100 | 101 | /** 102 | * Get database configuration from laravel config 103 | * 104 | * @param string $selected 105 | * 106 | * @return array 107 | */ 108 | protected function getDatabaseConfiguration($selected) 109 | { 110 | $database = $selected ?: $this->laravel['config']['database.default']; 111 | 112 | $connection = $this->laravel['config']['database.connections.'.$database]; 113 | 114 | $host = $connection['host'] ?? $connection['write']['host'][0] ?? '127.0.0.1'; 115 | 116 | return [ 117 | 'dsn' => "{$connection['driver']}:dbname={$connection['database']};host={$host};port={$connection['port']};charset={$connection['charset']}", 118 | 'username' => $connection['username'], 119 | 'password' => $connection['password'], 120 | ]; 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/Commands/MakeAnonymizerCommand.php: -------------------------------------------------------------------------------- 1 | laravel->databasePath().'/anonymization/'.$name.'.php'; 61 | } 62 | 63 | /** 64 | * Parse the class name and format according to the root namespace. 65 | * 66 | * @param string $name 67 | * @return string 68 | */ 69 | protected function qualifyClass($name) 70 | { 71 | return $name; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/Commands/stubs/DatabaseAnonymizer.stub: -------------------------------------------------------------------------------- 1 | call(UsersAnonymizer::class); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Commands/stubs/UsersAnonymizer.stub: -------------------------------------------------------------------------------- 1 | table('users', function (Blueprint $table) { 19 | 20 | $table->column('email')->replaceWith(function(Faker $faker) { 21 | return $faker->unique()->email; 22 | }); 23 | 24 | $table->column('name')->replaceWith('John Doe'); 25 | }); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Commands/stubs/anonymizer.stub: -------------------------------------------------------------------------------- 1 | table('users', function (Blueprint $table) { 19 | // $table->column('name')->replaceWith('John Doe'); 20 | }); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/ServiceProvider.php: -------------------------------------------------------------------------------- 1 | registerInstallCommand(); 21 | 22 | $this->registerAnonymizeCommand(); 23 | 24 | $this->registerMakeAnonymizerCommand(); 25 | } 26 | 27 | /** 28 | * Register anonymization:install command. 29 | * 30 | * @return void 31 | */ 32 | protected function registerInstallCommand() 33 | { 34 | $this->app->singleton('command.anonymization.install', function ($app) { 35 | return new AnonymizationInstallCommand($app['files'], $app['composer']); 36 | }); 37 | 38 | $this->commands('command.anonymization.install'); 39 | } 40 | 41 | /** 42 | * Register db:anonymize command. 43 | * 44 | * @return void 45 | */ 46 | protected function registerAnonymizeCommand() 47 | { 48 | $this->app->singleton('command.db.anonymize', function ($app) { 49 | return new DbAnonymizeCommand(); 50 | }); 51 | 52 | $this->commands('command.db.anonymize'); 53 | } 54 | 55 | /** 56 | * Register make:anonymizer command. 57 | * 58 | * @return void 59 | */ 60 | protected function registerMakeAnonymizerCommand() 61 | { 62 | $this->app->singleton('command.make.anonymizer', function ($app) { 63 | return new MakeAnonymizerCommand($app['files'], $app['composer']); 64 | }); 65 | 66 | $this->commands('command.make.anonymizer'); 67 | } 68 | } 69 | --------------------------------------------------------------------------------