# This code was forked from the LiveJournal project owned and operated # by Live Journal, Inc. The code has been modified and expanded by # Dreamwidth Studios, LLC. These files were originally licensed under # the terms of the license supplied by Live Journal, Inc, which can # currently be found at: # # http://code.livejournal.org/trac/livejournal/browser/trunk/LICENSE-LiveJournal.txt # # In accordance with the original license, this code and all its # modifications are provided under the GNU General Public License. # A copy of that license can be found in the LICENSE file included as # part of this distribution. package LJ::Console::Command::PrivPackage; use strict; use base qw(LJ::Console::Command); use Carp qw(croak); sub cmd { "priv_package" } sub desc { "Manage packages of admin privs. Basic workflow: priv_package create mypkg \"Test Package\", priv_package add mypkg admin:*, priv_package list. To actually grant a package to someone, priv grant #mypkg username. Works for revoke as well. Requires priv: admin."; } sub args_desc { [ 'command' => 'One of "list", "create", "add", "remove", "delete".', 'package' => 'The package to operate on. Use a short name.', 'arg' => 'If command is "list", no argument to see all packages, or provide a package to see the privs inside. For "create" and "delete" of a package, no argument. For "add" and "remove", arg is the privilege being granted in "privname:privarg" format.', ] } sub usage { ' [ [ ] ]' } sub can_execute { my $remote = LJ::get_remote(); return $remote && $remote->has_priv("admin"); } sub execute { my ( $self, $cmd, $pkg, $arg, @args ) = @_; return $self->error("This command takes one, two, or three arguments. Consult the reference.") unless $cmd && scalar(@args) == 0; return $self->error("Invalid command. Consult the reference.") unless $cmd =~ /^(?:list|create|add|remove|delete)$/; return $self->error("'$cmd' requires an argument.") if $cmd =~ /^(?:add|remove)$/ && !$arg; $pkg =~ s/^#//; # just in case they put a # on it my $dbh = LJ::get_db_writer(); my $remote = LJ::get_remote(); my ( $pkgid, $cpkg ) = $dbh->selectrow_array( 'SELECT pkgid, name FROM priv_packages WHERE name = ?', undef, $pkg ); return $self->error( "Database error: " . $dbh->errstr ) if $dbh->err; # canonical package name is "#" plus whatever is in the db $cpkg = defined $cpkg ? "#$cpkg" : undef; # list created packages, or contents of one if ( $cmd eq 'list' ) { if ( $pkg && $pkg ne 'all' ) { return $self->error("Package with that name does not exist") unless $pkgid; my $contents = $dbh->selectall_arrayref( 'SELECT privname, privarg FROM priv_packages_content WHERE pkgid = ?', undef, $pkgid ); return $self->error( "Database error: " . $dbh->errstr ) if $dbh->err; $self->info("Contents of $cpkg:"); foreach my $row ( @{ $contents || [] } ) { $self->info(" $row->[0]:$row->[1]"); } } else { my $packages = $dbh->selectall_arrayref( 'SELECT pkgid, name, lastmoduserid, lastmodtime FROM priv_packages ' . 'ORDER BY name' ); return $self->error( "Database error: " . $dbh->errstr ) if $dbh->err; $self->info("Available packages:"); foreach my $row ( @{ $packages || [] } ) { my $u = LJ::load_userid( $row->[2] ); my $time = LJ::mysql_time( $row->[3] ); $self->info( sprintf( "%5d %-20s%-20s\%s", $row->[0], $row->[1], $u->{user}, $time ) ); } } return 1; # create a package } elsif ( $cmd eq 'create' ) { return $self->error("Package with that name already exists.") if $pkgid; return $self->error( "Package names can only contain letters, numbers, underscores, hyphens, or underscores." ) unless $pkg =~ /^[a-z0-9_\-:\(\)\[\]]+$/i; $dbh->do( "INSERT INTO priv_packages (pkgid, name, lastmoduserid, lastmodtime) " . "VALUES (NULL, ?, ?, UNIX_TIMESTAMP())", undef, $pkg, $remote->id ); return $self->print("Package '$pkg' created."); # delete a package } elsif ( $cmd eq 'delete' ) { return $self->error("Package with that name does not exist.") unless $pkgid; $dbh->do( "DELETE FROM priv_packages WHERE pkgid = ?", undef, $pkgid ); return $self->error( "Database error: " . $dbh->errstr ) if $dbh->err; $dbh->do( "DELETE FROM priv_packages_content WHERE pkgid = ?", undef, $pkgid ); return $self->error( "Database error: " . $dbh->errstr ) if $dbh->err; return $self->print("Package '$cpkg' deleted."); # add or remove a privilige to a package } elsif ( $cmd eq 'add' || $cmd eq 'remove' ) { return $self->error("Package with that name does not exist.") unless $pkgid; my ( $pname, $parg ) = split( /:/, $arg ); return $self->error( "Argument must be in format of 'priv:arg' with optional arg. (The colon is required.)") unless $pname && defined $parg; # valid priv or not my $valid = $dbh->selectrow_array( 'SELECT COUNT(*) FROM priv_list WHERE privcode = ?', undef, $pname ); return $self->error("'$pname' is not a valid privilege.") unless $valid; # exists or not my $exists = $dbh->selectrow_array( 'SELECT COUNT(*) FROM priv_packages_content ' . 'WHERE pkgid = ? AND privname = ? AND privarg = ?', undef, $pkgid, $pname, $parg ); return $self->error( "Database error: " . $dbh->errstr ) if $dbh->err; if ( $cmd eq 'add' ) { return $self->error("Privilege already exists in package.") if $exists; $dbh->do( "INSERT INTO priv_packages_content (pkgid, privname, privarg) VALUES (?, ?, ?)", undef, $pkgid, $pname, $parg ); return $self->error( "Database error: " . $dbh->errstr ) if $dbh->err; $dbh->do( "UPDATE priv_packages SET lastmoduserid = ?, lastmodtime = UNIX_TIMESTAMP() WHERE pkgid = ?", undef, $remote->id, $pkgid ); return $self->print("Privilege ($pname:$parg) added to package $cpkg."); } else { # a removal return $self->error("Privilege does not exist in package.") unless $exists; $dbh->do( "DELETE FROM priv_packages_content WHERE pkgid = ? AND privname = ? AND privarg = ?", undef, $pkgid, $pname, $parg ); return $self->error( "Database error: " . $dbh->errstr ) if $dbh->err; $dbh->do( "UPDATE priv_packages SET lastmoduserid = ?, lastmodtime = UNIX_TIMESTAMP() WHERE pkgid = ?", undef, $remote->id, $pkgid ); return $self->print("Privilege ($pname:$parg) removed from package $cpkg."); } } } 1;