mark: A photo of Mark kneeling on top of the Taal Volcano in the Philippines. It was a long hike. (Default)
Mark Smith ([staff profile] mark) wrote in [site community profile] changelog2009-09-06 06:21 pm

[dw-free] implement admin/impersonate.bml as dw-free

[commit: http://hg.dwscoalition.org/dw-free/rev/a81a6da8ecc1]

http://bugs.dwscoalition.org/show_bug.cgi?id=178

Add /admin/impersonate tool.

Patch by [personal profile] afuna.

Files modified:
  • cgi-bin/LJ/User.pm
  • htdocs/admin/impersonate.bml
  • htdocs/admin/impersonate.bml.text
  • htdocs/admin/userlog.bml
--------------------------------------------------------------------------------
diff -r 4388e4653fad -r a81a6da8ecc1 cgi-bin/LJ/User.pm
--- a/cgi-bin/LJ/User.pm	Sun Sep 06 18:04:56 2009 +0000
+++ b/cgi-bin/LJ/User.pm	Sun Sep 06 18:21:55 2009 +0000
@@ -927,9 +927,12 @@ sub logout_all {
     $u->_logout_common;
 }
 
+sub make_fake_login_session {
+    return $_[0]->make_login_session( 'once', undef, 1 );
+}
 
 sub make_login_session {
-    my ($u, $exptype, $ipfixed) = @_;
+    my ( $u, $exptype, $ipfixed, $fake_login ) = @_;
     $exptype ||= 'short';
     return 0 unless $u;
 
@@ -940,15 +943,18 @@ sub make_login_session {
         'exptype' => $exptype,
         'ipfixed' => $ipfixed,
     };
+    $sess_opts->{nolog} = 1 if $fake_login;
 
     my $sess = LJ::Session->create($u, %$sess_opts);
     $sess->update_master_cookie;
 
     LJ::User->set_remote($u);
 
-    # add a uniqmap row if we don't have one already
-    my $uniq = LJ::UniqCookie->current_uniq;
-    LJ::UniqCookie->save_mapping($uniq => $u);
+    unless ( $fake_login ) {
+        # add a uniqmap row if we don't have one already
+        my $uniq = LJ::UniqCookie->current_uniq;
+        LJ::UniqCookie->save_mapping($uniq => $u);
+    }
 
     # restore scheme and language
     my $bl = LJ::Lang::get_lang($u->prop('browselang'));
@@ -978,11 +984,13 @@ sub make_login_session {
         "expiretime" => $etime,
     });
 
-    # activity for cluster usage tracking
-    LJ::mark_user_active($u, 'login');
-
-    # activity for global account number tracking
-    $u->note_activity('A');
+    unless ( $fake_login ) {
+        # activity for cluster usage tracking
+        LJ::mark_user_active($u, 'login');
+    
+        # activity for global account number tracking
+        $u->note_activity('A');
+    }
 
     return 1;
 }
diff -r 4388e4653fad -r a81a6da8ecc1 htdocs/admin/impersonate.bml
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/htdocs/admin/impersonate.bml	Sun Sep 06 18:21:55 2009 +0000
@@ -0,0 +1,90 @@
+<?_c
+#
+# admin/impersonate.bml
+#
+# Allow someone trusted to log in as another user for a limited
+# amount of time.
+#
+# Authors:
+#      Afuna <coder.dw@afunamatata.com>
+#
+# Copyright (c) 2009 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'.
+#
+_c?><?page
+body<=
+<?_code
+{
+    use strict;
+    use vars qw/ %POST @errors /;
+
+    if ( $LJ::USE_SSL && !$LJ::IS_SSL ) {
+        return BML::redirect( "$LJ::SSLROOT/admin/impersonate" );
+    }
+
+    my $remote = LJ::get_remote();
+    return "<?needlogin?>" unless $remote;
+
+    my @displayprivs = ( "canview:*" );
+    my $numprivs = @displayprivs;
+
+    return BML::ml( "admin.noprivserror", { numprivs => $numprivs, needprivs => "<b>" . join( ", ", @displayprivs ) . "</b>" } )
+        unless $remote->has_priv( canview => '*' );
+
+    my $ret;
+
+    if ( LJ::did_post() && LJ::check_referer( '/admin/impersonate.bml' ) ) {
+        return LJ::error_list( $ML{'error.invalidform'} ) unless LJ::check_form_auth();
+
+        my $u = LJ::load_user( $POST{username} );
+        push @errors, BML::ml( '.error.invaliduser', { user => LJ::ehtml( $POST{username} ) } ) unless $u;
+
+        my $password = $POST{password};
+        push @errors, $ML{'.error.invalidpassword'} unless $password && $password eq $remote->password;
+
+        my $reason = LJ::ehtml( LJ::trim( $POST{reason} ) );
+        push @errors, $ML{'.error.emptyreason'} unless $reason;
+
+        $remote->logout;
+
+        if ( $u->make_fake_login_session ) {
+            # log for auditing
+            $remote->log_event( 'impersonator', { actiontarget => $u->id, remote => $remote, reason => $reason } );
+            $u->log_event( 'impersonated', { actiontarget => $u->id, remote => $remote, reason => $reason } );
+            LJ::statushistory_add( $u->id, $remote->id, 'impersonate', $reason );
+    
+            return BML::redirect( $LJ::SITEROOT );
+
+        } else {
+            push @errors, $ML{'.error.failedlogin'};
+        }
+    }
+
+    $ret .= LJ::error_list( @errors ) if @errors;
+
+    $ret .= "<form method='POST'>";
+    $ret .= LJ::form_auth();
+
+    $ret .= LJ::labelfy( 'impersonate_username', $ML{'.form.username' } );
+    $ret .= LJ::html_text( { id => 'impersonate_username', name => 'username', maxlength => '25', size => '25', value => $POST{username} } ) . "<br />";
+
+    $ret .= LJ::labelfy( 'impersonate_password', $ML{'.form.password'} );
+    $ret .= LJ::html_text( { id => 'impersonate_password', name => 'password', type => 'password', size => '25' } ) . "<br />";
+
+    $ret .= LJ::labelfy( 'impersonate_reason', $ML{'.form.reason'} );
+    $ret .= LJ::html_text( { id => 'impersonate_reason', name => 'reason', maxlength => '255', size => '50', value => $POST{reason} } ) . "<br />";
+    $ret .= "<input type='submit' value='Submit' />";
+    $ret .= "</form>";
+
+    return $ret;
+}
+_code?>
+<=body
+title=><?_ml .title _ml?>
+head<=
+<?_code return $headextra; _code?>
+<=head
+page?>
diff -r 4388e4653fad -r a81a6da8ecc1 htdocs/admin/impersonate.bml.text
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/htdocs/admin/impersonate.bml.text	Sun Sep 06 18:21:55 2009 +0000
@@ -0,0 +1,15 @@
+.error.emptyreason=A reason is required.
+
+.error.failedlogin=Attempt to impersonate failed.
+
+.error.invalidpassword=Password is incorrect.
+
+.error.invaliduser=Could not load user '[[user]]'.
+
+.form.password=Password:
+
+.form.reason=Reason:
+
+.form.username=Usename:
+
+.title=Impersonate
diff -r 4388e4653fad -r a81a6da8ecc1 htdocs/admin/userlog.bml
--- a/htdocs/admin/userlog.bml	Sun Sep 06 18:04:56 2009 +0000
+++ b/htdocs/admin/userlog.bml	Sun Sep 06 18:21:55 2009 +0000
@@ -109,6 +109,11 @@ FORM
             # TODO: parse out e_unixtime and s_unixtime and display?
         } elsif ($row->{action} eq 'delete_userpic') {
             $action = "Deleted userpic #$extra->{picid}";
+        } elsif ( $row->{action} eq 'impersonated' ) {
+            $action = "Was impersonated: " . LJ::ehtml( $extra->{reason} );
+        } elsif ( $row->{action} eq 'impersonator' ) {
+            my $u = LJ::load_userid( $row->{actiontarget} );
+            $action = "Did impersonate on " . ( $u ? $u->ljuser_display : "(no target)" ) . ": " .  LJ::ehtml( $extra->{reason} );
         } elsif (my $info = LJ::run_hook('userlog_rows', $row)) {
             $action = $info;
         } else {
--------------------------------------------------------------------------------