Juan Pablo Ramirez - Nicolas Masson
Based in Luxembourg
Open source password manager
Installable on any Linux server + hosted on cloud
Based on OpenGPG
Collaborative
RESTFull API
Built in CakePHP
TDD oriented
Schema creation
Cleanup
Test Fixtures
Feature: User permission
Background:
Given I create a user with id 504
Scenario:
Given I log in with permission 'Users'
When I call get 'users/view/504'
Then I shall be granted access.
Scenario:
Given I log in with permission 'Admin'
When I call get 'users/view/504'
Then I shall be granted access.
Scenario:
Given I log in with permission 'Foo'
When I call get 'users/view/504'
Then I shall be redirected.
Includes DB interactions
public function testUserPermission()
{
// Arrange
$user = UserFactory::make()->withPermission('Foo')->getEntity();
// Act
$act = $this->Users->hasPermission($user, 'Foo');
// Assert
$this->assertTrue($act);
}
public function dataProvider()
{
return [
['Admin', true],
['Guru', true],
['Foo', true],
['Bar', false],
];
}
/** @dataProvider dataProvider */
public function testUserHasPermissionFoo(
string $perm,
bool $expect
)
{
$user = UserFactory::make()->withPermission($perm)->getEntity();
$act = $this->Users->hasPermission($user, 'Foo');
$this->assertSame($expect, $act);
}
Readable
Easy to write
Run fast
namespace App\Model\Table;
use Cake\ORM\Table;
class UsersTable extends Table
{
public function initialize(array $config): void
{
$this->belongsToMany('UsersGroups');
}
}
namespace App\Model\Table;
use Cake\ORM\Table;
class UsersGroupsTable extends Table
{
public function initialize(array $config): void
{
$this
->belongsToMany('Users')
->belongsToMany('Permissions');
}
}
namespace App\Model\Table;
use Cake\ORM\Table;
class PermissionsTable extends Table
{
public function initialize(array $config): void
{
$this->belongsToMany('UsersGroups');
}
}
// Retrieve all users, join permissions
$users = $this->Users->find()
->contain('UsersGroups.Permissions')
->toArray();
// Filter users by permissions, join permissions
$admins = $this->Users->find()
->matching('UsersGroups.Permissions', function ($q) {
return $q->where(['Permissions.name' => 'Admin')
})
->toArray();
// With Database access
public function hasPermission(int $userId, string $perm): bool
{
return $this->Users->find()
->innerJoinWith('UsersGroups.Permissions', function ($q) use ($perm) {
return $q->where(['Permissions.name' => $perm)
})
->where(['Users.id' => $userId])
->count() > 0;
}
// Without Database access
public function hasPermission(User $user, string $perm): bool
{
return in_array(
$perm,
Hash::extract($user, 'users_groups.{n}.permissions.{n}.name')
)
}
protected function initialize(): void
{
$this->getTable()->belongsToMany('UsersGroups');
}
protected function getRootTableRegistryName(): string
{
return 'Users';
}
protected function setDefaultTemplate(): void
{
$this->setDefaultData(function(Generator $faker) {
return [
'username' => $faker->username,
'email' => $faker->email,
];
});
}
$user = UserFactory::make()->getEntity();
UserFactory::make(4)->getEntities();
UserFactory::make(4)->persist();
UserFactory::make(['username' => 'Foo'])->persist();
UserFactory::make([
['username' => 'Foo'],
['username' => 'Bar'],
])->persist();
UserFactory::make(3)
->with('UsersGroups.Permissions', ['name' => 'Foo'])
->getEntity();
UserFactory::make()
->with('UsersGroups.Permissions', [
['name' => 'Foo'],
['name' => 'Bar'],
])
->getEntity();
UserFactory::make()
->admin()
->getEntity();
UserFactory::make()
->with('UsersGroups.Permissions',
PermissionFactory::make()->guru()
)
->getEntity();
UserFactory::make()
->with('UsersGroups.Permissions', [
PermissionFactory::make()->admin(),
PermissionFactory::make()->guru(),
])
->getEntity();
public function testUserPermission()
{
// Arrange
$user = UserFactory::make()
->with('UsersGroups.Permissions', ['name' => 'Foo'])
->getEntity();
// Act
$act = $this->Users->hasPermission($user, 'Foo');
// Assert
$this->assertTrue($act);
}
public function dataProviderForTestPermission()
{
return [['Admin',true],['Guru',true],['Foo',true],['Bar',false]];
}
/** @dataProvider dataProviderForTestPermission */
public function testPermission(string $p, bool $exp)
{
$user = UserFactory::make()->withPermission($p)->getEntity();
$act = $this->Users->hasPermission($user, 'Foo');
$this->assertSame($exp, $act);
}
Feature: User permission
Background:
Given I create a user with id 504
Scenario:
Given I log in with permission 'Users'
When I call get 'users/view/504'
Then I shall be granted access.
Scenario:
Given I log in with permission 'Admin'
When I call get 'users/view/504'
Then I shall be granted access.
Scenario:
Given I log in with permission 'Guru'
When I call get 'users/view/504'
Then I shall be granted access.
Scenario:
Given I log in with permission 'Foo'
When I call get 'users/view/504'
Then I shall be redirected.
Time (Y axis) vs Number of tests (X axis)
Default template
Association builder
Persist or not
Random Ids
Framework agnostic
Grow solid with the CakePHP Fixture Factories
vierge-noire/cakephp-fixture-factories
Join us to extend the trigger-based database cleaner
vierge-noire/test-database-cleaner
Credits: Juan Pablo Ramirez - Nicolas Masson