Improved docs on $allowed_actions

Added section to "Controllers" and "Form" topics,
added $allowed_actions definitions to all controller examples
This commit is contained in:
Ingo Schommer 2013-01-28 22:35:32 +01:00
parent 50995fbecb
commit 3e27d27f7a
8 changed files with 153 additions and 53 deletions

View File

@ -68,6 +68,9 @@ You can have more customized logic and interface feedback through a custom contr
:::php :::php
<?php <?php
class MyController extends Controller { class MyController extends Controller {
static $allowed_actions = array('Form');
protected $template = "BlankPage"; protected $template = "BlankPage";
function Link($action = null) { function Link($action = null) {

View File

@ -83,7 +83,8 @@ You can access the following controller-method with /team/signup
class Team extends DataObject {} class Team extends DataObject {}
class Team_Controller extends Controller { class Team_Controller extends Controller {
function signup($id, $otherId) { static $allowed_actions = array('signup');
public function signup($id, $otherId) {
return $this->renderWith('MyTemplate'); return $this->renderWith('MyTemplate');
} }
} }

View File

@ -98,6 +98,7 @@ This code provides a good template:
:::php :::php
class MyProcess extends Controller { class MyProcess extends Controller {
public static $allowed_actions = array('index');
function index() { function index() {
set_time_limit(0); set_time_limit(0);
while(memory_get_usage() < 32*1024*1024) { while(memory_get_usage() < 32*1024*1024) {

View File

@ -3,8 +3,7 @@
Base controller class. You will extend this to take granular control over the actions and url handling of aspects of Base controller class. You will extend this to take granular control over the actions and url handling of aspects of
your SilverStripe site. your SilverStripe site.
## Usage
## Example
`mysite/code/Controllers/FastFood.php` `mysite/code/Controllers/FastFood.php`
@ -12,7 +11,8 @@ your SilverStripe site.
<?php <?php
class FastFood_Controller extends Controller { class FastFood_Controller extends Controller {
function order($arguments) { public static $allowed_actions = array('order');
public function order(SS_HTTPRequest $request) {
print_r($arguments); print_r($arguments);
} }
} }
@ -37,6 +37,96 @@ Request for `/fastfood/order/24/cheesefries` would result in the following to th
[Name] => cheesefries [Name] => cheesefries
) )
<div class="warning" markdown='1'>
SilverStripe automatically adds a URL routing entry based on the controller's class name,
so a `MyController` class is accessible through `http://yourdomain.com/MyController`.
</div>
## Access Control
### Through $allowed_actions
All public methods on a controller are accessible by their name through the `$Action`
part of the URL routing, so a `MyController->mymethod()` is accessible at
`http://yourdomain.com/MyController/mymethod`. This is not always desireable,
since methods can return internal information, or change state in a way
that's not intended to be used through a URL endpoint.
SilverStripe strongly recommends securing your controllers
through defining a `$allowed_actions` array on the class,
which allows whitelisting of methods, as well as a concise
way to perform checks against permission codes or custom logic.
:::php
class MyController extends Controller {
public static $allowed_actions = array(
// someaction can be accessed by anyone, any time
'someaction',
// So can otheraction
'otheraction' => true,
// restrictedaction can only be people with ADMIN privilege
'restrictedaction' => 'ADMIN',
// complexaction can only be accessed if $this->canComplexAction() returns true
'complexaction' '->canComplexAction'
);
}
There's a couple of rules guiding these checks:
* Each controller is only responsible for access control on the methods it defines
* If a method on a parent class is overwritten, access control for it has to be redefined as well
* An action named "index" is whitelisted by default
* A wildcard (`*`) can be used to define access control for all methods (incl. methods on parent classes)
* Specific method entries in `$allowed_actions` overrule any `*` settings
* Methods returning forms also count as actions which need to be defined
* Form action methods (targets of `FormAction`) should NOT be included in `$allowed_actions`,
they're handled separately through the form routing (see the ["forms" topic](/topics/forms))
* `$allowed_actions` can be defined on `Extension` classes applying to the controller.
If the permission check fails, SilverStripe will return a "403 Forbidden" HTTP status.
### Through the action
Each method responding to a URL can also implement custom permission checks,
e.g. to handle responses conditionally on the passed request data.
:::php
class MyController extends Controller {
public static $allowed_actions = array('myaction');
public function myaction($request) {
if(!$request->getVar('apikey')) {
return $this->httpError(403, 'No API key provided');
}
return 'valid';
}
}
Unless you transform the response later in the request processing,
it'll look pretty ugly to the user. Alternatively, you can use
`ErrorPage::response_for(<status-code>)` to return a more specialized layout.
Note: This is recommended as an addition for `$allowed_actions`, in order to handle
more complex checks, rather than a replacement.
### Through the init() method
After checking for allowed_actions, each controller invokes its `init()` method,
which is typically used to set up common state in the controller, and
include JavaScript and CSS files in the output which are used for any action.
If an `init()` method returns a `SS_HTTPResponse` with either a 3xx or 4xx HTTP
status code, it'll abort execution. This behaviour can be used to implement
permission checks.
:::php
class MyController extends Controller {
public static $allowed_actions = array();
public function init() {
parent::init();
if(!Permission::check('ADMIN')) return $this->httpError(403);
}
}
## URL Handling ## URL Handling
@ -51,6 +141,7 @@ the case below we also want any orders coming through `/fastfood/drivethrough/`
:::php :::php
class FastFood_Controller extends Controller { class FastFood_Controller extends Controller {
static $allowed_actions = array('drivethrough');
public static $url_handlers = array( public static $url_handlers = array(
'drivethrough/$Action/$ID/$Name' => 'order' 'drivethrough/$Action/$ID/$Name' => 'order'
); );

View File

@ -67,7 +67,8 @@ Example:
:::php :::php
class MyController extends Controller { class MyController extends Controller {
function myurlaction($RAW_urlParams) { static $allowed_actions = array('myurlaction');
public function myurlaction($RAW_urlParams) {
$SQL_urlParams = Convert::raw2sql($RAW_urlParams); // works recursively on an array $SQL_urlParams = Convert::raw2sql($RAW_urlParams); // works recursively on an array
$objs = DataObject::get('Player', "Name = '{$SQL_data[OtherID]}'"); $objs = DataObject::get('Player', "Name = '{$SQL_data[OtherID]}'");
// ... // ...
@ -81,7 +82,6 @@ This means if you've got a chain of functions passing data through, escaping sho
:::php :::php
class MyController extends Controller { class MyController extends Controller {
/** /**
* @param array $RAW_data All names in an indexed array (not SQL-safe) * @param array $RAW_data All names in an indexed array (not SQL-safe)
*/ */
function saveAllNames($RAW_data) { function saveAllNames($RAW_data) {
@ -210,7 +210,8 @@ PHP:
:::php :::php
class MyController extends Controller { class MyController extends Controller {
function search($request) { static $allowed_actions = array('search');
public function search($request) {
$htmlTitle = '<p>Your results for:' . Convert::raw2xml($request->getVar('Query')) . '</p>'; $htmlTitle = '<p>Your results for:' . Convert::raw2xml($request->getVar('Query')) . '</p>';
return $this->customise(array( return $this->customise(array(
'Query' => DBField::create('Text', $request->getVar('Query')), 'Query' => DBField::create('Text', $request->getVar('Query')),
@ -239,7 +240,8 @@ PHP:
:::php :::php
class MyController extends Controller { class MyController extends Controller {
function search($request) { static $allowed_actions = array('search');
public function search($request) {
$rssRelativeLink = "/rss?Query=" . urlencode($_REQUEST['query']) . "&sortOrder=asc"; $rssRelativeLink = "/rss?Query=" . urlencode($_REQUEST['query']) . "&sortOrder=asc";
$rssLink = Controller::join_links($this->Link(), $rssRelativeLink); $rssLink = Controller::join_links($this->Link(), $rssRelativeLink);
return $this->customise(array( return $this->customise(array(

View File

@ -29,6 +29,8 @@ the form in a method on *HomePage_Controller*.
:::php :::php
class HomePage_Controller extends Page_Controller { class HomePage_Controller extends Page_Controller {
static $allowed_actions = array('BrowserPollForm');
// ... // ...
function BrowserPollForm() { function BrowserPollForm() {