NAME
Class::Facet - Capability-discipline facet construct for Perl.
SYNOPSIS
In the class to facetize:
package Foo::TheRealOne;
use Class::Facet;
sub get_this { ... }
sub get_that { ... }
sub set_this { ... }
sub set_that { ... }
sub get_substuff { ... } # Returns an object
sub facet_readonly {
my ($self) = @_;
my $facet_object =
Class::Facet->make("Foo::ReadOnlyFacet", $self);
$facet_object->facet_rescind if $self->in_a_bad_mood;
return $facet_object;
}
Meanwhile, in a nearby package (often in the same .pm file):
package Foo::ReadOnlyFacet;
use Class::Facet;
BEGIN {
Class::Facet->from(__PACKAGE__, "Foo::TheRealOne");
Class::Facet->on_error(__PACKAGE__,
sub { my ($class, %args) = @_;
die "forbidden method $args{-method}" });
Class::Facet->delegate(__PACKAGE__, qw(get_this get_that));
}
sub get_substuff {
my ($facetself, $origself) = Class::Facet->selves(\@_);
return $origself->get_substuff(@_)->facet_readonly;
}
or if you don't like BEGIN blocks, then replace the one above with:
use Class::Facet from => "Foo::TheRealOne",
on_error => sub {
my ($class, %args) = @_;
die "forbidden method $args{-method}";
},
delegate => [ qw(get_this get_that) ];
DESCRIPTION
Facets are a working concept of the E secure programming language (see "REFERENCES") that in turn has its roots in a method of secure programming known as capability discipline. Facets are a powerful yet simple mechanism for writing secure code; as well as for refactoring code into becoming secure, provided that said code already be object-oriented (but see "DISCLAIMER").
Definitions
As demonstrated in "SYNOPSIS", a facet object is simply a delegate object (akin to what CPAN's Class::Delegate does) that only provides access to a subset of the methods in the original object, or restricts the range of arguments or return values to said methods. The facet object is blessed into a facet class, here Foo::ReadOnlyFacet.
Purpose
The facet object closely follows the API of the original object, and is intended to be used in lieu of the real thing in unsuspecting third-party code. By carefully selecting what the facet object can and cannot do, one is able to restrict the privilege level handed down to said third-party code in an extremely fine-grained fashion. A very common application of this technique (although by no means the only one) is the read-only facet, demonstrated in the synopsis: the facet object is only allowed to call the read accessors of the original one, and cannot alter it.
So how secure is this?
In itself, not much: Perl is not a capability secure language, and by reading the source code of this module, the astute reader will find numerous ways to call methods that were purportedly faceted out, thereby defeating any security that Class::Facet seems to provide. Still, Class::Facet alone is helpful for applying capability discipline, that is, providing defense-in-depty to bona fide code that already respects the Law of Demeter (see "REFERENCES"). The aforementioned read-only facet is an example of this. Capability discipline in general (and Class::Facet in particular) cover the bases of high-level programming mistakes such as privilege management; it is no substitute for the tips and tricks of "perlsec", that describes how to prevent low-level vulnerabilities.
On the other hand, when combining Class::Facet with "Safe" and caperl (see "REFERENCES"), a whole lot more security can be achieved. (To be written.)
Inheriting facets
TODO: To be written.
CLASS METHODS
import
Called at compile time for each occurence of use Class::Facet
; converts the parameters, if any, into the calls to methods "from", "on_error" and "delegate" such that e.g.
use Class::Facet from => "foo";
use Class::Facet delegate => [ "bar", "baz" ];
are translated respectively into
Class::Facet->from($callerpackage, "foo");
Class::Facet->delegate($callerpackage, "bar", "baz");
and so on. Arguments to "use" are interpreted pair-wise, so that
use Class::Facet from => "foo", delegate => [ "bar", "baz" ];
is again equivalent to the above.
from($facetclass, $origclass)
Indicates that $facetclass is to be a facet class from $origclass. This method must be called first before any Class::Facet operation on $facetclass.
delegate($facetclass, $methodname)
Indicates that the method named $methodname is to be delegated to the original object without altering the parameters or the return value. Is mostly equivalent to declaring a sub like this:
sub foo {
my (undef, $origself) = Class::Facet->selves(\@_);
unshift(@_, $origself);
goto $origself->can("foo");
}
except that the error management is better.
on_error($facetclass, $sub)
Installs $sub as the error management callback method for $facetclass. $sub will always be called as a class method in void context, and should throw an exception with "die" in perlfunc, Exception::Class or some such, and not return. As shown in "SYNOPSIS", $sub should accept the following named parameters:
- -file
- -line
-
The filename and line number of the place in the code that invoked the faulty operation.
- -facetclass
-
The facet class against which the error sub is being invoked. This will be $facetclass, unless $sub is the error management routine for several facets at once.
- -reason
-
The reason why the error is thrown, as the name of the method in Class::Facet that triggered the error, or one of the special values
facet_error
(meaning that "facet_error" was invoked manually) orforbidden_method
(if one tries to invoke a forbidden method through the facet object). - -details (optional)
-
A message in english explaining the reason of the error.
- -method (optional)
-
Set when trying to invoke a method through a facet object, but this method is neither delegated (using "delegate") nor defined in the facet package.
The default implementation (if on_error()
is not called) is to throw a text message in english using "die" in perlfunc that contains a subset of the aforementioned information.
make($facetclass, $origobject)
Returns a facet of $object in class $facetclass. The returned facet object is an ordinary hashref-based object, constructed like this:
bless { delegate => $origobject }, $facetclass;
and Class::Facet will never use any other field in blessed hash besides delegate
. The facet class and facet constructor are therefore free to add their own fields into the facet object.
selves($argslistref)
Interprets $argslistref as a reference to the argument list (@_) of a class method, and modifies it in place by removing the first argument (as "shift" in perlfunc would do). Returns a a ($facetself, $origself) pair where $facetself is the facet object, and $origself is the original object.
This class method is useful for creating custom facet methods, such as the get_substuff
example in "SYNOPSIS".
MIRANDA METHODS
These methods can be called from any facet object, regardless of how restricted the facet class; in capability discipline parlance, they can thus be interpreted as unremovable rights, just like those enumerated in the miranda warning given by the police officer upon arresting you.
facet_rescind()
Turns the facet into a useless object, that will not accept any further method call.
facet_error(%named_args)
Throws an exception by invoking the error mechanism configured with "on_error" for this facet class. This may be used from inside a facetized method, so as to make error handling uniform.
TODO
Release as a separate CPAN module.
Add faceting support for private fields, using tied objects (yow!)
REFERENCES
Capabilities and secure programming: http://www.c2.com/cgi/wiki?CapabilitySecurityModel, http://www.erights.org/elib/capability/ode/index.html
The Law of Demeter, a well-known best practice in object-oriented programming that also happens to be a preliminary step to capability discipline: http://www.c2.com/cgi/wiki?LawOfDemeter
The E programming language: http://wiki.erights.org/wiki/Walnut
Capabilities in Perl: http://caperl.links.org/
The concept of facets: http://www.c2.com/cgi/wiki?FacetPattern, http://wiki.erights.org/wiki/Walnut/Secure_Distributed_Computing/Capability_Patterns#Facets
Why facets and inheritance don't mix: FIXME add link
DISCLAIMER
Users of this package should be warned that Class::Facet doesn't provide any actual security of its own, as stated in "So how secure is this?". The author therefore makes no warranty, implied or otherwise, about the suitability of this software for any purpose whatsoever.
The authors shall not in any case be liable for special, incidental, consequential, indirect or other similar damages arising from the use of this software.
Your mileage will vary. If in any doubt do not use it.