-- Daniel Latter <[email protected]> wrote
(on Tuesday, 07 September 2010, 08:39 PM +0100):
> One more question if I may.
> Rather than verifying the token at the method level what do you think
> about verifying the token in the request constructor?
>
> What I mean is that for each server method that you want "protected"
> you would have to verify the token in the method body. But if you do
> this in the extended request constructor say, you would only need to
> do this once.?
It depends on what the token represents.
Typically, the token will represent an identity, and you will need to do
two different actions:
* Verify and obtain the identity
* Validate whether or not the identity has permissions to perform the
current action.
The first should likely be done outside handling the call; if it's an
invalid token, you can return a fault response immediately:
$token = $request->getToken();
$authenticator = new AuthenticationAdapter($token);
$result = $authenticator->authenticate();
if (!$result->isValid()) {
$response = new Zend_XmlRpc_Fault(401, 'Invalid token');
header('Content-Type: text/xml');
echo $response;
exit();
}
$identity = $result->getIdentity();
You would then pass the *identity* to your service objects -- and yes,
the constructor is a great place -- or do it via a setter:
// via constructor
$service = new SomeService($identity);
// via setter
$service = new SomeService();
$service->setIdentity($identity);
Now, within the service, you may have fine-grained or broad access --
broad access means the user has access to all methods or none at all,
while fine-grained would refer to access being determined per-method. In
each case, you compare it to the ACLs:
if (!$acl->isAllowed($identity, $this, 'somepermission')) {
throw new RestrictedAccessException();
}
(The above assumes your service class acts as an ACL resource.)
Where they vary are the permissions you check against, and where you
check. If you're doing broad access, you can likely omit the third
argument to isAllowed, as you're querying on all permissions:
if (!$acl->isAllowed($identity, $this)) { }
As such, you might want to do this in the constructor -- and if you do,
you have two choices for when/where to do it:
* Prior to attaching the service to the server. As such, you'd create
the Fault object manually as I did in the above example when
authenticating the token.
* Alternately, attach the class to the server by name, specifying a
constructor argument of the identity:
$server->setClass('SomeService', 'some', $identity);
(Second argument is the XML-RPC "namespace" under which the classes
methods will be grouped.)
When you do that, the exception will be caught by the server as soon as
it tries to handle it -- and if you have a number of classes attached,
this is a good way to reduce how much up-front coding and checking you
need to do.
If doing fine-grained, you'll likely determine permissions per-method,
you _must_ do the checks in each method. I usually develop a helper for
this:
protected function validateAcls($action)
{
if (!$this->getAcl()->isAllowed($this->getIdentity(), $this, $action)) {
throw new RestrictedAccessException();
}
}
and then pop the following line into the top of all ACL'd methods:
public function foo()
{
$this->validateAcls(__FUNCTION__);
// ...
}
You'll note I use a discrete exception for invalid ACL assertions. I do
this so that I can leverage another aspect of the server: having fault
responses for known exceptions.
Zend_XmlRpc_Server_Fault::attachFaultExceptions('RestrictedAccessException');
Any exception passed this way can return its message and code in the
returned fault -- which makes it very easy to document in your API why
calls might be failing.
Hope that helps!
> On Monday, September 6, 2010, Daniel Latter <[email protected]> wrote:
> > Excellent, thanks a bunch for the quick response. - and the slightly
> > improved approach.
> >
> > Greatly appreciated.
> >
> > Daniel
> >
> > On 6 September 2010 16:52, Matthew Weier O'Phinney <[email protected]> wrote:
> >> -- Daniel Latter <[email protected]> wrote
> >> (on Monday, 06 September 2010, 04:43 PM +0100):
> >>> Sorry, I missed part of the message:
> >>>
> >>> Would you just specify the token here:
> >>>
> >>> client = new Zend_XmlRpc_Client('http://localhost:10088/xmlrpcserver');
> >>> $timesheets = $client->getProxy('timesheets');
> >>> $resp = $timesheets->addHours($token, $hours);
> >>
> >> Yes, exactly -- you add the token as the first or last argument to every
> >> method, and then the request object on the server side strips it out,
> >> and you inject it into service classes.
> >>
> >>
> >>> ---------- Forwarded message ----------
> >>> From: Daniel Latter <[email protected]>
> >>> Date: 6 September 2010 16:40
> >>> Subject: Zend_XmlRpc token passing
> >>> To: Zend Framework General <[email protected]>
> >>>
> >>>
> >>> hi,
> >>>
> >>> RE: Matthew Weier O'Phinney
> >>>
> >>> On a Nabble post you refer to a piece of code that enables the passing
> >>> of a token:
> >>>
> >>> class My_XmlRpc_Request extends Zend_XmlRpc_Request_Http
> >>> {
> >>>
> >>> public function __construct()
> >>> {
> >>> parent::__construct();
> >>>
> >>> if ($this->getMethod() != 'login') {
> >>> $params = $this->getParams();
> >>> $token = array_shift($params);
> >>> $this->setParams($params);
> >>>
> >>> // Verify the token, and then add it to the registry...
> >>> Zend_Registry::set('token', $token);
> >>> }
> >>> }
> >>> }
> >>>
> >>> I am correct in thinking that all service method(s) will stay the
> >>> same, i.e. - have no reference to the token?
> >>>
> >>> so like this:
> >>>
> >>> /**
> >>> * Add timesheet hours for a candidate
> >>> *
> >>> * @param array Hours for a working week
> >>> * @return array
> >>> */
> >>> public function addHours($hours) {
> >>>
> >>> $timesheetService = new Service_Timesheet();
> >>> $resp =
> >>> $timesheetService->addCandidateTimesheetHours($hours);
> >>>
> >>> return $resp;
> >>> }
> >>>
> >>>
> >>> and NOT like this:
> >>>
> >>> /**
> >>> * Add timesheet hours for a candidate
> >>> *
> >>> * @param string token
> >>> * @param array Hours for a working week
> >>> * @return array
> >>> */
> >>> public function addHours($token, $hours) {
> >>>
> >>> $timesheetService = new Service_Timesheet();
> >>> $resp =
> >>> $timesheetService->addCandidateTimesheetHours($hours);
> >>>
> >>> return $resp;
> >>> }
> >>>
> >>>
> >>> TIA
> >>>
> >>> Daniel.
> >>>
> >>
> >> --
> >> Matthew Weier O'Phinney
> >> Project Lead
>
--
Matthew Weier O'Phinney
Project Lead | [email protected]
Zend Framework | http://framework.zend.com/
PGP key: http://framework.zend.com/zf-matthew-pgp-key.asc