238 lines
8 KiB
Perl
238 lines
8 KiB
Perl
#!/usr/bin/perl
|
|
#
|
|
# DW::Controller::Admin::Priv
|
|
#
|
|
# Manage privileges for a given user, or see who has a given privilege.
|
|
#
|
|
# Authors:
|
|
# Jen Griffin <kareila@livejournal.com>
|
|
#
|
|
# Copyright (c) 2020 by Dreamwidth Studios, LLC.
|
|
#
|
|
# This program is free software; you may redistribute it and/or modify it under
|
|
# the same terms as Perl itself. For a copy of the license, please reference
|
|
# 'perldoc perlartistic' or 'perldoc perlgpl'.
|
|
#
|
|
|
|
package DW::Controller::Admin::Priv;
|
|
|
|
use strict;
|
|
|
|
use DW::Controller;
|
|
use DW::Controller::Admin;
|
|
use DW::Routing;
|
|
use DW::Template;
|
|
use DW::FormErrors;
|
|
|
|
DW::Controller::Admin->register_admin_page(
|
|
'/',
|
|
path => 'priv/',
|
|
ml_scope => '/admin/priv/index.tt',
|
|
|
|
# ironically the privs page has no check for privs, it's public
|
|
);
|
|
|
|
DW::Routing->register_string( "/admin/priv/index", \&main_controller, app => 1 );
|
|
|
|
sub main_controller {
|
|
my ( $ok, $rv ) = controller( form_auth => 1 );
|
|
return $rv unless $ok;
|
|
|
|
my $r = DW::Request->get;
|
|
my $form_args = $r->did_post ? $r->post_args : $r->get_args;
|
|
my $vars = {};
|
|
|
|
my $scope = '/admin/priv/index.tt';
|
|
|
|
my $remote = $rv->{remote};
|
|
|
|
# see which privileges remote can grant
|
|
$remote->load_user_privs('admin');
|
|
|
|
# returns true if the remote user can grant the given priv
|
|
$vars->{remote_can_grant} = sub {
|
|
my ( $priv, $arg ) = @_;
|
|
return 0 unless $priv;
|
|
return $remote->has_priv( 'admin', "$priv/$arg" ) if $arg;
|
|
return $remote->has_priv( 'admin', $priv );
|
|
};
|
|
|
|
# load all privilege info from the database
|
|
|
|
my $dbh = LJ::get_db_writer();
|
|
|
|
$vars->{privs} = $dbh->selectall_arrayref(
|
|
"SELECT prlid, privcode, privname, des, is_public, scope "
|
|
. "FROM priv_list ORDER BY privcode",
|
|
{ Slice => {} }
|
|
);
|
|
|
|
$vars->{priv_by_id} = {};
|
|
$vars->{map_codeid} = {};
|
|
|
|
foreach ( @{ $vars->{privs} } ) {
|
|
$vars->{priv_by_id}->{ $_->{prlid} } = $_;
|
|
$vars->{map_codeid}->{ $_->{privcode} } = $_->{prlid};
|
|
}
|
|
|
|
my $mode = $form_args->{mode};
|
|
|
|
$mode ||= "viewpriv" if $form_args->{priv};
|
|
$mode ||= "viewuser" if $form_args->{user};
|
|
|
|
return DW::Template->render_template( 'admin/priv/index.tt', $vars )
|
|
unless $mode;
|
|
|
|
# toggle for switching to relevant display-only mode
|
|
my $switch_to_view_mode = sub {
|
|
$mode = { userchange => 'viewuser', privchange => 'viewpriv' }->{$mode};
|
|
};
|
|
|
|
$switch_to_view_mode->() if $form_args->{'submit:refresh'}; # no changes
|
|
|
|
if ( $mode eq "userchange" || $mode eq "privchange" ) {
|
|
|
|
return error_ml("$scope.error.needpost") unless $r->did_post;
|
|
|
|
my $remote_can_grant = $vars->{remote_can_grant};
|
|
my $map_codeid = $vars->{map_codeid};
|
|
|
|
my $errors = DW::FormErrors->new;
|
|
my $success = DW::FormErrors->new;
|
|
|
|
foreach my $key ( keys %$form_args ) {
|
|
next unless $key =~ /^revoke:(\d+):(\d+)$/;
|
|
my ( $prmid, $del_userid1 ) = ( $1, $2 );
|
|
|
|
my ( $del_userid2, $prlid, $arg ) =
|
|
$dbh->selectrow_array( "SELECT userid, prlid, arg FROM priv_map WHERE prmid=?",
|
|
undef, $prmid );
|
|
|
|
my $privcode = $vars->{priv_by_id}->{$prlid}->{privcode};
|
|
|
|
if ( !$remote_can_grant->( $privcode, $arg ) ) {
|
|
$errors->add( '', '.error.access.remove', { privcode => $privcode } );
|
|
}
|
|
elsif ( $del_userid1 == $del_userid2 ) {
|
|
$dbh->do("DELETE FROM priv_map WHERE prmid=$prmid");
|
|
LJ::statushistory_add( $del_userid1, $remote->userid, "privdel",
|
|
sprintf( 'Denying: "%s" with arg "%s"', $privcode, $arg || '' ) );
|
|
$success->add( '', '.success.remove' );
|
|
}
|
|
}
|
|
|
|
# these actions are the same on both sides, just different data sources
|
|
my $process_grant = sub {
|
|
my ( $user, $privid, $arg ) = @_;
|
|
|
|
my $u = LJ::load_user($user);
|
|
return error_ml("$scope.error.invaliduser") unless $u;
|
|
|
|
my $privcode = $vars->{priv_by_id}->{$privid}->{privcode};
|
|
$arg //= $form_args->{arg};
|
|
my $pname = join( ' ', grep { $_ } $privcode, $arg );
|
|
|
|
if ( !$privcode ) {
|
|
$errors->add( '', '.error.unknownpriv' );
|
|
}
|
|
elsif ( !$remote_can_grant->( $privcode, $arg ) ) {
|
|
$errors->add( '', '.error.access.grant', { privcode => $pname } );
|
|
}
|
|
elsif ( $u->has_priv( $privcode, $arg ) ) {
|
|
$errors->add( '', '.error.already', { privcode => $pname } );
|
|
}
|
|
else {
|
|
$dbh->do( "INSERT INTO priv_map (prmid, userid, prlid, arg) VALUES (NULL, ?, ?, ?)",
|
|
undef, $u->userid, $privid, $arg );
|
|
LJ::statushistory_add( $u->userid, $remote->userid, "privadd",
|
|
sprintf( 'Granting: "%s" with arg "%s"', $privcode, $arg || '' ) );
|
|
$success->add( '', '.success.grant', { privcode => $pname } );
|
|
}
|
|
};
|
|
|
|
$process_grant->( $form_args->{user}, $form_args->{grantpriv} + 0 )
|
|
if $form_args->{grantpriv};
|
|
|
|
$process_grant->( $form_args->{grantuser}, $map_codeid->{ $form_args->{priv} } )
|
|
if $form_args->{grantuser};
|
|
|
|
if ( $form_args->{grantpkg} ) {
|
|
my $ps = $dbh->selectall_arrayref(
|
|
'SELECT privname, privarg FROM priv_packages_content WHERE pkgid = ?',
|
|
undef, $form_args->{grantpkg} );
|
|
$process_grant->( $form_args->{user}, $map_codeid->{ $_->[0] }, $_->[1] )
|
|
foreach @{ $ps || [] };
|
|
}
|
|
|
|
$vars->{errors} = $errors;
|
|
$vars->{success} = $success;
|
|
|
|
# continue executing related display code below
|
|
$switch_to_view_mode->();
|
|
}
|
|
|
|
if ( $mode eq "viewuser" ) {
|
|
|
|
my $user = LJ::canonical_username( $form_args->{user} );
|
|
$vars->{u} = LJ::load_user($user);
|
|
|
|
return error_ml("$scope.error.invaliduser") unless $vars->{u};
|
|
|
|
$vars->{remote} = $remote;
|
|
|
|
$vars->{userprivs} = $dbh->selectall_arrayref(
|
|
"SELECT pm.prmid, pm.prlid, pm.arg FROM priv_map pm, priv_list pl"
|
|
. " WHERE pm.prlid=pl.prlid AND pm.userid=?"
|
|
. " ORDER BY pl.privcode, pm.arg",
|
|
{ Slice => {} },
|
|
$vars->{u}->id,
|
|
);
|
|
|
|
my @privmenu = ( '', '' );
|
|
push( @privmenu, $_->{prlid}, $_->{privcode} ) foreach @{ $vars->{privs} };
|
|
$vars->{privmenu} = \@privmenu;
|
|
|
|
# include list of priv packages
|
|
|
|
my @pkgmenu = ( '', '' );
|
|
my $pkgs = $dbh->selectall_arrayref( "SELECT pkgid, name FROM priv_packages ORDER BY name",
|
|
{ Slice => {} } );
|
|
push( @pkgmenu, $_->{pkgid}, '#' . $_->{name} ) foreach @$pkgs;
|
|
$vars->{pkgmenu} = \@pkgmenu;
|
|
|
|
return DW::Template->render_template( 'admin/priv/viewuser.tt', $vars );
|
|
}
|
|
|
|
if ( $mode eq "viewpriv" ) {
|
|
|
|
my $privid = $vars->{map_codeid}->{ $form_args->{priv} };
|
|
|
|
return error_ml("$scope.error.invalidpriv") unless $privid;
|
|
|
|
$vars->{arg} = $form_args->{viewarg};
|
|
|
|
my $qarg = '';
|
|
$qarg = "AND pm.arg=" . $dbh->quote( $vars->{arg} ) if $vars->{arg};
|
|
|
|
$vars->{skip} = $form_args->{skip} || 0;
|
|
$vars->{limit} = 100;
|
|
|
|
$vars->{privusers} = $dbh->selectall_arrayref(
|
|
"SELECT pm.prmid, u.user, u.userid, pm.arg FROM priv_map pm, useridmap u"
|
|
. " WHERE pm.prlid=? AND pm.userid=u.userid $qarg"
|
|
. " ORDER BY u.user, pm.arg LIMIT ?, ?",
|
|
{ Slice => {} }, $privid, $vars->{skip}, $vars->{limit},
|
|
);
|
|
|
|
$vars->{pinfo} = $vars->{priv_by_id}->{$privid};
|
|
$vars->{pcode} = $vars->{pinfo}->{privcode};
|
|
$vars->{pname} = join( ' ', grep { $_ } $vars->{pcode}, $vars->{arg} );
|
|
|
|
return DW::Template->render_template( 'admin/priv/viewpriv.tt', $vars );
|
|
}
|
|
|
|
# if we get here, there was a problem
|
|
return error_ml('error.invalidform');
|
|
}
|
|
|
|
1;
|