2021-06-19 06:23:34 +02:00
|
|
|
# File: Capabilites.pm
|
|
|
|
#
|
|
|
|
# Purpose: Fine-grained user permissions.
|
|
|
|
|
2021-07-11 00:00:22 +02:00
|
|
|
# SPDX-FileCopyrightText: 2021 Pragmatic Software <pragma78@gmail.com>
|
|
|
|
# SPDX-License-Identifier: MIT
|
2020-02-03 18:50:38 +01:00
|
|
|
|
2021-07-21 07:44:51 +02:00
|
|
|
package PBot::Core::Capabilities;
|
|
|
|
use parent 'PBot::Core::Class';
|
2020-02-03 18:50:38 +01:00
|
|
|
|
2021-06-19 06:23:34 +02:00
|
|
|
use PBot::Imports;
|
2020-02-03 18:50:38 +01:00
|
|
|
|
|
|
|
sub initialize {
|
2020-02-15 23:38:32 +01:00
|
|
|
my ($self, %conf) = @_;
|
2021-06-05 22:20:03 +02:00
|
|
|
|
|
|
|
# capabilities file
|
2020-02-15 23:38:32 +01:00
|
|
|
my $filename = $conf{filename} // $self->{pbot}->{registry}->get_value('general', 'data_dir') . '/capabilities';
|
2021-06-05 22:20:03 +02:00
|
|
|
|
|
|
|
# capabilities hash table
|
2021-07-24 04:22:25 +02:00
|
|
|
$self->{caps} = PBot::Core::Storage::HashObject->new(
|
2021-07-24 03:26:45 +02:00
|
|
|
pbot => $self->{pbot},
|
|
|
|
name => 'Capabilities',
|
|
|
|
filename => $filename,
|
|
|
|
);
|
2021-06-05 22:20:03 +02:00
|
|
|
|
|
|
|
# load capabilities
|
2020-02-15 23:38:32 +01:00
|
|
|
$self->{caps}->load;
|
|
|
|
|
|
|
|
# add some capabilities used in this file
|
|
|
|
$self->add('can-modify-capabilities', undef, 1);
|
|
|
|
$self->add('can-group-capabilities', undef, 1);
|
|
|
|
|
2021-07-21 06:38:07 +02:00
|
|
|
# add some misc capabilities
|
2020-02-15 23:38:32 +01:00
|
|
|
$self->add('is-whitelisted', undef, 1);
|
2020-02-03 18:50:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
sub has {
|
2020-02-15 23:38:32 +01:00
|
|
|
my ($self, $cap, $subcap, $depth) = @_;
|
|
|
|
my $cap_data = $self->{caps}->get_data($cap);
|
2020-02-18 18:50:02 +01:00
|
|
|
|
2020-02-15 23:38:32 +01:00
|
|
|
return 0 if not defined $cap_data;
|
2020-02-18 18:50:02 +01:00
|
|
|
|
|
|
|
if ($cap eq $subcap) {
|
|
|
|
return 0 if exists $cap_data->{$subcap} and not $cap_data->{$subcap};
|
|
|
|
return 1;
|
|
|
|
}
|
2020-02-15 23:38:32 +01:00
|
|
|
|
2021-06-05 22:20:03 +02:00
|
|
|
$depth //= 10; # set depth to 10 if it's not defined
|
|
|
|
|
2020-02-15 23:38:32 +01:00
|
|
|
if (--$depth <= 0) {
|
2021-07-21 07:44:51 +02:00
|
|
|
$self->{pbot}->{logger}->log("Max recursion reached for PBot::Core::Capabilities->has($cap, $subcap)\n");
|
2020-02-15 23:38:32 +01:00
|
|
|
return 0;
|
|
|
|
}
|
2020-02-03 18:50:38 +01:00
|
|
|
|
2020-02-15 23:38:32 +01:00
|
|
|
foreach my $c ($self->{caps}->get_keys($cap)) {
|
|
|
|
return 1 if $c eq $subcap and $cap_data->{$c};
|
|
|
|
return 1 if $self->has($c, $subcap, $depth);
|
|
|
|
}
|
2021-06-05 22:20:03 +02:00
|
|
|
|
2020-02-15 23:38:32 +01:00
|
|
|
return 0;
|
2020-02-03 18:50:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
sub userhas {
|
2020-02-15 23:38:32 +01:00
|
|
|
my ($self, $user, $cap) = @_;
|
2021-06-05 22:20:03 +02:00
|
|
|
|
2020-02-15 23:38:32 +01:00
|
|
|
return 0 if not defined $user;
|
|
|
|
return 1 if $user->{$cap};
|
2021-06-05 22:20:03 +02:00
|
|
|
|
2020-02-15 23:38:32 +01:00
|
|
|
foreach my $key (keys %$user) {
|
|
|
|
next if $key eq '_name';
|
|
|
|
next if not $user->{$key};
|
|
|
|
return 1 if $self->has($key, $cap);
|
|
|
|
}
|
2021-06-05 22:20:03 +02:00
|
|
|
|
2020-02-15 23:38:32 +01:00
|
|
|
return 0;
|
2020-02-03 18:50:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
sub exists {
|
2020-02-15 23:38:32 +01:00
|
|
|
my ($self, $cap) = @_;
|
2021-06-05 22:20:03 +02:00
|
|
|
|
2020-02-15 23:38:32 +01:00
|
|
|
$cap = lc $cap;
|
2021-06-05 22:20:03 +02:00
|
|
|
|
2020-02-15 23:38:32 +01:00
|
|
|
foreach my $c ($self->{caps}->get_keys) {
|
|
|
|
return 1 if $c eq $cap;
|
2021-06-05 22:20:03 +02:00
|
|
|
|
2020-04-24 01:19:36 +02:00
|
|
|
foreach my $sub_cap ($self->{caps}->get_keys($c)) {
|
|
|
|
return 1 if $sub_cap eq $cap;
|
|
|
|
}
|
2020-02-03 18:50:38 +01:00
|
|
|
}
|
2021-06-05 22:20:03 +02:00
|
|
|
|
2020-02-15 23:38:32 +01:00
|
|
|
return 0;
|
2020-02-03 18:50:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
sub add {
|
2020-02-15 23:38:32 +01:00
|
|
|
my ($self, $cap, $subcap, $dontsave) = @_;
|
2021-06-05 22:20:03 +02:00
|
|
|
|
|
|
|
$cap = lc $cap;
|
|
|
|
|
2020-02-15 23:38:32 +01:00
|
|
|
if (not defined $subcap) {
|
2020-04-24 01:19:36 +02:00
|
|
|
if (not $self->{caps}->exists($cap)) {
|
|
|
|
$self->{caps}->add($cap, {}, $dontsave);
|
|
|
|
}
|
2020-02-03 18:50:38 +01:00
|
|
|
} else {
|
2020-04-24 01:19:36 +02:00
|
|
|
if ($self->{caps}->exists($cap)) {
|
|
|
|
$self->{caps}->set($cap, $subcap, 1, $dontsave);
|
|
|
|
} else {
|
|
|
|
$self->{caps}->add($cap, { $subcap => 1 }, $dontsave);
|
|
|
|
}
|
2020-02-03 18:50:38 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sub remove {
|
2020-02-15 23:38:32 +01:00
|
|
|
my ($self, $cap, $subcap) = @_;
|
2021-06-05 22:20:03 +02:00
|
|
|
|
2020-02-15 23:38:32 +01:00
|
|
|
$cap = lc $cap;
|
2021-06-05 22:20:03 +02:00
|
|
|
|
2020-02-15 23:38:32 +01:00
|
|
|
if (not defined $subcap) {
|
|
|
|
foreach my $c ($self->{caps}->get_keys) {
|
2020-04-24 01:19:36 +02:00
|
|
|
foreach my $sub_cap ($self->{caps}->get_keys($c)) {
|
|
|
|
$self->{caps}->remove($c, $sub_cap, 1) if $sub_cap eq $cap;
|
|
|
|
}
|
2020-02-15 23:38:32 +01:00
|
|
|
$self->{caps}->remove($c, undef, 1) if $c eq $cap;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
$self->{caps}->remove($cap, $subcap, 1) if $self->{caps}->exists($cap);
|
2020-02-04 03:52:21 +01:00
|
|
|
}
|
2021-06-05 22:20:03 +02:00
|
|
|
|
2020-02-15 23:38:32 +01:00
|
|
|
$self->{caps}->save;
|
2020-02-03 18:50:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
sub rebuild_botowner_capabilities {
|
2020-02-15 23:38:32 +01:00
|
|
|
my ($self) = @_;
|
2021-06-05 22:20:03 +02:00
|
|
|
|
2020-02-15 23:38:32 +01:00
|
|
|
$self->{caps}->remove('botowner', undef, 1);
|
2021-06-05 22:20:03 +02:00
|
|
|
|
|
|
|
foreach my $cap ($self->{caps}->get_keys) {
|
|
|
|
$self->add('botowner', $cap, 1);
|
|
|
|
}
|
2020-02-03 18:50:38 +01:00
|
|
|
}
|
|
|
|
|
2020-02-03 22:03:08 +01:00
|
|
|
sub list {
|
2020-02-15 23:38:32 +01:00
|
|
|
my ($self, $capability) = @_;
|
2021-06-05 22:20:03 +02:00
|
|
|
|
|
|
|
if (defined $capability and not $self->{caps}->exists($capability)) {
|
|
|
|
return "No such capability $capability.";
|
|
|
|
}
|
2020-02-15 23:38:32 +01:00
|
|
|
|
|
|
|
my @caps;
|
|
|
|
my @groups;
|
|
|
|
my @standalones;
|
|
|
|
my $result;
|
|
|
|
|
|
|
|
if (not defined $capability) {
|
|
|
|
@caps = sort $self->{caps}->get_keys;
|
|
|
|
$result = 'Capabilities: ';
|
2020-02-03 22:03:08 +01:00
|
|
|
} else {
|
2020-02-15 23:38:32 +01:00
|
|
|
@caps = sort $self->{caps}->get_keys($capability);
|
2021-06-05 22:20:03 +02:00
|
|
|
|
|
|
|
if (not @caps) {
|
|
|
|
return "Capability $capability has no grouped capabilities."
|
|
|
|
}
|
|
|
|
|
2020-02-15 23:38:32 +01:00
|
|
|
$result = "Grouped capabilities for $capability: ";
|
2020-02-03 22:03:08 +01:00
|
|
|
}
|
|
|
|
|
2020-02-15 23:38:32 +01:00
|
|
|
# first list all capabilities that have sub-capabilities (i.e. grouped capabilities)
|
|
|
|
# then list stand-alone capabilities
|
|
|
|
foreach my $cap (@caps) {
|
|
|
|
my $count = $self->{caps}->get_keys($cap);
|
2021-06-05 22:20:03 +02:00
|
|
|
|
|
|
|
if ($count > 0) {
|
|
|
|
push @groups, "$cap ($count cap" . ($count == 1 ? '' : 's') . ")";
|
|
|
|
} else {
|
|
|
|
push @standalones, $cap;
|
|
|
|
}
|
2020-02-03 18:50:38 +01:00
|
|
|
}
|
2021-06-05 22:20:03 +02:00
|
|
|
|
2020-02-15 23:38:32 +01:00
|
|
|
$result .= join ', ', @groups, @standalones;
|
2021-06-05 22:20:03 +02:00
|
|
|
|
2020-02-15 23:38:32 +01:00
|
|
|
return $result;
|
|
|
|
}
|
2020-02-03 18:50:38 +01:00
|
|
|
|
|
|
|
1;
|