Skip to content

Commit

Permalink
Add an implementation to read the existing permissions data (port fro…
Browse files Browse the repository at this point in the history
…m MOD_rights)

Add permission lockdown to the /admin section of website
  • Loading branch information
djwinter committed Jun 24, 2016
1 parent 3e08a6a commit 1e42ff8
Show file tree
Hide file tree
Showing 6 changed files with 160 additions and 13 deletions.
2 changes: 2 additions & 0 deletions config/access_control.global.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,6 @@ security:
- { path: ^/tour, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/rox/in, roles: IS_AUTHENTICATED_ANONYMOUSLY }

- { path: ^/admin, roles: ROLE_ADMIN }

- { path: ^/, roles: ROLE_USER }
30 changes: 30 additions & 0 deletions doc/book/permissions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Permissions

The entire `/admin` section of the site is restricted to users with `ROLE_ADMIN`,
which maps to having the 'Admin' right. The user's roles are loaded upon
login via [Rox\Member\Model\Member](module/Member/src/Model/Member.php)::getRoles()

This needs to be scaled back to use the correct rights in certain
controllers of the admin section. The `^/admin` line should be removed from
`access_control.global.yml` and permission checks added to controller actions.

To check for the admin role:
```php
if (!$this->getAuthChecker()->isGranted('ROLE_ADMIN')) {
throw new AccessDeniedHttpException();
}
```

To check for a right:
```php
if (!$this->getMember()->getRightLevel('Logs')) {
throw new AccessDeniedHttpException();
}
```

To check for a right with certain scope:
```php
if (!$this->getMember()->getRightLevel('Words', 'en')) {
throw new AccessDeniedHttpException();
}
```
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ pages:
- index.md
- Reference:
- 'Project Structure': structure.md
- Permissions: permissions.md
- Twig: twig.md
- 'Error Handling': error_handling.md
- Deprecated: deprecated.md
13 changes: 13 additions & 0 deletions module/Auth/src/Model/Right.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

namespace Rox\Auth\Model;

use Rox\Core\Model\AbstractModel;

class Right extends AbstractModel
{
/**
* @var string
*/
protected $table = 'rights';
}
105 changes: 92 additions & 13 deletions module/Member/src/Model/Member.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@
* @property Collection $groups
* @property Collection $trads
* @property Collection $preferences
* @property Collection|MemberRight[] $rights
* @property integer $id
* @method Builder|HasMany hasMany($a, $b, $c)
* @method Builder|HasMany hasMany($related, $foreignKey = null, $localKey = null)
*
* @SuppressWarnings(PHPMD.TooManyPublicMethods)
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
Expand Down Expand Up @@ -168,6 +169,25 @@ public function preferences()
return $pivot;
}

/**
* Sets eager loading for the Right definition of the MemberRight, because
* a MemberRight by itself doesn't tell us what the right actually is.
*
* Alternative way to do this is like preferences, which a Member would
* belong to many Rights, and the Level, Scope, Comment, etc would become
* pivot values.
*
* @return HasMany
*/
public function rights()
{
$relation = $this->hasMany(MemberRight::class, 'IdMember', 'id');

$relation->getQuery()->with('right');

return $relation;
}

/**
* @param $username
*
Expand Down Expand Up @@ -282,23 +302,27 @@ public function getTradByLanguage($language, $tradId)
/**
* Returns the roles granted to the user.
*
* <code>
* public function getRoles()
* {
* return array('ROLE_USER');
* }
* </code>
* Symfony only calls this once when logging in. The results are stored
* in the session. So the queries behind checking the admin role are not
* repeating for each request.
*
* Alternatively, the roles might be stored on a ``roles`` property,
* and populated in any number of different ways when the user object
* is created.
* @todo There are a few rows for admin rights where level = 0
*
* @return (Role|string)[] The user roles
* @return string[]
*/
public function getRoles()
{
// TODO: Implement getRoles() method.
return ['ROLE_USER'];
// Grant user role to everyone
$roles = [
'ROLE_USER',
];

// Grant admin role if member has admin rights
if ($this->getRightLevel('Admin')) {
$roles[] = 'ROLE_ADMIN';
}

return $roles;
}

/**
Expand Down Expand Up @@ -353,4 +377,59 @@ public function getPotentialGuests() {

return $potentialGuests;
}

/**
* $rightName is case sensitive
*
* Port of MOD_right_flag::hasRight
*
* @param $rightName
* @param $scope
*
* @return int
*/
public function getRightLevel($rightName, $scope = null)
{
$rights = $this->rights;

$right = $rights->where('right.Name', $rightName)->first();

// Right doesn't exist
if (!$right) {
return 0;
}

// If the right level is 0, no need to check the scope.
if ((int) $right->Level === 0) {
return 0;
}

// If no scope was requested, just return the level
if (!$scope) {
return (int) $right->Level;
}

// Break the available scopes into an array and clean them
$scopes = explode(',', $right->Scope);

// Trim quotes and spaces (\x20)
$scopes = array_map(function ($value) {
return trim($value, "\"\x20");
}, $scopes);

$scopes = array_map('strtolower', $scopes);

// If one of the available scopes is 'all', then allow any requested
// scope.
if (in_array('all', $scopes, true)) {
return (int) $right->Level;
}

// Requested scope isn't available
if (!in_array(strtolower($scope), $scopes, true)) {
return 0;
}

return (int) $right->Level;
}
}
22 changes: 22 additions & 0 deletions module/Member/src/Model/MemberRight.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

namespace Rox\Member\Model;

use Rox\Auth\Model\Right;
use Rox\Core\Model\AbstractModel;

/**
* @property Right $right
*/
class MemberRight extends AbstractModel
{
/**
* @var string
*/
protected $table = 'rightsvolunteers';

public function right()
{
return $this->hasOne(Right::class, 'id', 'IdRight');
}
}

0 comments on commit 1e42ff8

Please sign in to comment.