The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
=head1 NAME

Jifty::Manual::AccessControl - Using Jifty's default ACL system

=head1 DESCRIPTION


Out of the box Jifty-based applications have an ACL system.  The system 
automatically validates ACLs on L<Jifty::Record> objects by calling the method
C<current_user_can> before any create, read, update, or delete operation.
In all cases, the arguments passed to the CRUD operation are passed as 
extra arguments to C<current_user_can>.

On C<create()>, we reject the operation if C<current_user_can('create')>
returns FALSE.

On C<_value()> or C<I<somefieldname>>, we reject the operation
if C<current_user_can('read')> returns false.

On C<_set()> or C<I<set_somefieldname>>, we reject the operation
if C<current_user_can('write')> returns false.


On C<delete()>, we reject the operation  if C<current_user_can('delete')>
returns false.

Out of the box, C<current_user_can> returns 1. When you want to actually 
check ACLs, you'll need to override C<current_user_can()> in your
C<Jifty::Record> subclass.

It's likely that at some point, you'll decide you want to ask other
questions on certain types of operations.  Say, you only want to let
administrators update the C<paid_account> field. In that case, you'd override
C<check_update_rights()> to look for the C<admin> right rather than the 
C<update> right, if the C<FIELD> is C<paid_account>.

=head1 ENABLING ACCESS CONTROL USING THE USER PLUGIN

To painlessly enable the AccessControl subsystem, a User plugin is available
with an authentication plugin, the C<Authentication::Password> plugin may get 
enabled. This is done in the F<etc/config.yml> configuration file.

    Plugins:
      - Authentication::Password: {}

Then, create an C<App::Model::User> class that will be override with
C<Jifty::Plugin::User::Mixin::Model::User> and an authentication plugin 
C<Jifty::Plugin::Authentication::Password::Mixin::Model::User>
, for example:

    use strict;
    use warnings;

    package App::Model::User;

    use Jifty::DBI::Schema;

    use App::Record schema {
    };

    use Jifty::Plugin::User::Mixin::Model::User;
    use Jifty::Plugin::Authentication::Password::Mixin::Model::User;

    # Your model-specific methods go here.

    1;

Next, create the table in your database using the F<jifty> executable
like C<./bin/jifty schema --setup>.

=head2 Expanding the Model

The model that manages C<User> Records is not limited to the plugin's
definition. It can be expanded by providing an additional schema
definition. Every column here will be added to the plugin's
columns. Simply add a schema definition block like this:

    use Jifty::DBI::Schema;
    use App::Record schema {
        column 'extra_column_name';

        column 'mygroup' =>
               valid_values are qw/admin moderator user/,
               default is 'user';

        # more columns if necessary
    };

The full syntax for defining a schema can be found in
L<Jifty::Manual::Models> or in L<Jifty::DBI::Schema>.

If you want to manage an admin group, you must protect the group column 
as only a superuser can change it.
Then, you override C<current_user_can> in C<App::Model::User> 

    sub current_user_can {
        my $self = shift;
        my $type = shift;
        my %args = (@_);

        return 0 
            if ( $type eq 'update'
                and !$self->current_user->is_superuser
                and $args{'column'} eq 'mygroup' ); 


        return 1;
    }

Defining a method C<_init> in your C<App::CurrentUser> class gives you
a chance to add more data to the C<CurrentUser> object. This method
will automatically get called after the Plugin's C<_init> is done.

    package App::CurrentUser;

    use strict;
    use warnings;

    use base qw(Jifty::CurrentUser);

    __PACKAGE__->mk_accessors(qw(group));

    sub _init {
        my $self = shift;
        my %args = (@_);

        if (keys %args) {
            $self->user_object(App::Model::User->new(current_user => $self));
            $self->user_object->load_by_cols(%args);

            if ( $self->user_object->mygroup eq 'admin') {
                $self->is_superuser(1);
            };

            $self->group($self->user_object->mygroup);
        };
        $self->SUPER::_init(%args);
    };

With your C<App::CurrentUser>, users in group admin are superuser and you can 
use C<< Jifty->web->current_user->group >> in your application.

=head2 Templates defined by the C<Authentication::Password> plugin

To avoid the need for repetitive work, the C<Authentication::Password> plugin already
defines a couple of usable templates:

=over 4

=item F</login>

provides a login screen with a signup option. After
successful login, the current continuation is called. If no
continuation exists, the template sitting at the base URL (F</>) is called.

=item F</logout>

logs out the current user.

=item F</signup>

allows a user to sign up himself/herself. By default
a confirmation mail is sent out that has to get followed by
the user.

=item F</passwordreminder>

after entering his/her mail address, the user will receive a mail that
contains a link to F</let/reset_lost_password>.

=item F</let/confirm_email>

is called in the mail and results in accepting the user.

=item F</let/reset_lost_password>

enabled by the passwordreminder template, this template allows a user
to reenter a password for future use.

=back

=head2 Doing checks at other places in your code

If you need to check more than Model-based record operations you will
have to do some coding on your own. C<< Jifty->web->current_user >> provides a
C<App::CurrentUser> object that can get queried about the current user.
This object provides some convenience methods:

=over 4

=item C<username>

returns the name of the current user or C<undef> if not logged in.

=item C<id>

returns the id of the current user or C<undef> if not logged in.

=back

=head1 SEE ALSO

L<Jifty::CurrentUser>, L<Jifty::Record>, L<Jifty::RightsFrom>

=cut