Authorization-AccessControl

 view release on metacpan or  search on metacpan

lib/Authorization/AccessControl/Grant.pm  view on Meta::CPAN

package Authorization::AccessControl::Grant 0.04;
use v5.26;
use warnings;

# ABSTRACT: Encapsulation of the parameters of a privilege grant

use Data::Compare;
use Readonly;
use Scalar::Util qw(looks_like_number);

use experimental qw(signatures);

use overload
  '""' => 'to_string';

sub new($class, %params) {
  my $role         = delete($params{role});
  my $resource     = delete($params{resource});
  my $action       = delete($params{action});
  my $restrictions = delete($params{restrictions});
  $restrictions = {} unless (defined($restrictions));

  die("Unsupported params: ", join(', ', keys(%params))) if (keys(%params));
  die("Role must be a non-empty string") if (defined($role) && (ref($role) || $role eq ''));
  die("Resource is required")           unless ($resource              && !ref($resource));
  die("Action is required")             unless ($action                && !ref($action));
  die("Restrictions must be a HashRef") unless (defined($restrictions) && ref($restrictions) eq 'HASH');

  Readonly::Scalar my $data => {
    _role         => $role,
    _resource     => $resource,
    _action       => $action,
    _restrictions => $restrictions
  };

  bless($data, $class);
}

sub to_string($self, @params) {
  my $role         = $self->{_role} ? '[' . $self->{_role} . '] ' : '';
  my $restrictions = '';
  foreach (keys($self->{_restrictions}->%*)) {
    my $v;
    if    ($self->{_restrictions}->{$_})                    {$v = $self->{_restrictions}->{$_}}
    elsif (looks_like_number($self->{_restrictions}->{$_})) {$v = 0}
    else                                                    {$v = 'false'}
    $restrictions .= "$_=$v,";
  }
  chop($restrictions);
  $role . $self->{_resource} . ' => ' . $self->{_action} . '(' . $restrictions . ')';
}

sub role($self) {
  $self->{_role};
}

sub resource($self) {
  $self->{_resource};
}

sub action($self) {
  $self->{_action};
}

sub restrictions($self) {
  $self->{_restrictions};
}

sub _satisfies_role($self, @roles) {
  return 1 unless ($self->{_role});
  return (grep {$_ eq $self->{_role}} @roles) > 0;
}

sub _satisfies_resource($self, $resource) {
  return 0 unless (defined($resource));
  return $self->{_resource} eq $resource;
}

sub _satisfies_action($self, $action) {
  return 0 unless (defined($action));
  return $self->{_action} eq $action;
}

sub _satisfies_restrictions($self, $attributes) {
  my %attrs = $attributes->%*;
  delete($attrs{$_}) foreach (grep {!exists($self->{_restrictions}->{$_})} keys(%attrs));
  my $v = Compare($self->{_restrictions}, \%attrs);
  return $v;
}

sub is_equal($self, $priv) {
  return 0 unless (($self->role // '') eq ($priv->role // ''));
  return 0 unless ($self->resource eq $priv->resource);
  return 0 unless ($self->action eq $priv->action);
  return 0 unless (Compare($self->restrictions, $priv->restrictions));
  return 1;
}

sub accepts($self, %params) {
  my ($roles, $resource, $action, $attributes) = @params{qw(roles resource action attributes)};

  return 0 unless ($self->_satisfies_resource($resource));
  return 0 unless ($self->_satisfies_action($action));
  return 0 unless ($self->_satisfies_role(($roles // [])->@*));
  return 0 unless ($self->_satisfies_restrictions($attributes // {}));



( run in 1.507 second using v1.01-cache-2.11-cpan-39bf76dae61 )