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] changelog2010-04-23 01:34 am

[dw-free] Add priv-checking option to DW::Controller::controller()

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

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

Add privcheck mode to controller base, so TT pages can easily check for
privileges.

Patch by [personal profile] pauamma.

Files modified:
  • cgi-bin/DW/Controller.pm
  • cgi-bin/LJ/User.pm
--------------------------------------------------------------------------------
diff -r eec75d6d72ef -r 0d9755820438 cgi-bin/DW/Controller.pm
--- a/cgi-bin/DW/Controller.pm	Fri Apr 23 01:29:23 2010 +0000
+++ b/cgi-bin/DW/Controller.pm	Fri Apr 23 01:34:39 2010 +0000
@@ -7,7 +7,7 @@
 # Authors:
 #      Mark Smith <mark@dreamwidth.org>
 #
-# Copyright (c) 2009 by Dreamwidth Studios, LLC.
+# Copyright (c) 2009-2010 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
@@ -55,6 +55,30 @@ sub success_ml {
 }
 
 # helper controller.  give it a few arguments and it does nice things for you.
+#
+# Supported arguments: (1 stands for any true value, 0 for any false value)
+# - anonymous => 1 -- lets anonymous (not logged in) visitors view the page
+# - anonymous => 0 -- doesn't (default)
+# - authas => 1 -- allows ?authas= in URL, generates authas form (not permitted
+#                  if anonymous => 1 specified)
+# - authas => 0 -- doesn't (default)
+# - specify_user => 1 -- allows ?user= in URL (Note: requesting both authas and
+#                        specify_user is allowed, but probably not a good idea)
+# - specify_user => 0 -- doesn't (default)
+# - privcheck => $privs -- user must be logged in and have at least one priv of
+#                          the ones in this arrayref.
+#                          Example: [ "faqedit:guides", "faqcat", "admin:*" ]
+#
+# Returns one of:
+# - 0, $error_text (if there's an error)
+# - 1, $hashref (if everything looks good)
+#
+# Returned hashref can be passed to DW::Template->render_template as the 2nd
+# argument, and has the following keys:
+# - remote -- the remote user object or undef (LJ::get_remote())
+# - u -- user object for username in ?user= or ?authas= if present and valid,
+#        otherwise same as remote
+# - authas_html -- HTML for the "switch user" form
 sub controller {
     my ( %args ) = @_;
 
@@ -62,8 +86,12 @@ sub controller {
     my $fail = sub { return ( 0, $_[0] ); };
     my $ok   = sub { return ( 1, $vars ); };
 
-    # ensure the arguments make sense... anonymous means we cannot authas
-    delete $args{authas} if $args{anonymous};
+    # some argument combinations are invalid, so just die.  this is something that should
+    # be caught in development...
+    die "Invalid usage of controller, check your calling arguments.\n"
+        if ( $args{authas} && $args{specify_user} ) ||
+           ( $args{authas} && $args{anonymous} ) ||
+           ( $args{privcheck} && $args{anonymous} );
 
     # 'anonymous' pages must declare themselves, else we assume that a remote is
     # necessary as most pages require a user
@@ -89,6 +117,25 @@ sub controller {
         $vars->{authas_html} = LJ::make_authas_select( $vars->{remote}, { authas => $vars->{u}->user } );
     }
 
+    # check user is suitably privved
+    if ( my $privs = $args{privcheck} ) {
+        # if they just gave us a string, throw it in an array
+        $privs = [ $privs ] unless ref $privs eq 'ARRAY';
+
+        # now iterate over the array and check.  the user must have ANY
+        # of the privs to pass the test.
+        my $has_one = 0;
+        foreach my $priv ( @$privs ) {
+            last if $has_one = $vars->{remote}->has_priv( $priv );
+        }
+
+        # now if they have none, throw an error message
+        return $fail->( error_ml( 'admin.noprivserror',
+                    { numprivs => scalar @$privs,
+                      needprivs => join( ', ', sort @$privs ) } ) )
+            unless $has_one;
+    }
+
     # everything good... let the caller know they can continue
     return $ok->();
 }
diff -r eec75d6d72ef -r 0d9755820438 cgi-bin/LJ/User.pm
--- a/cgi-bin/LJ/User.pm	Fri Apr 23 01:29:23 2010 +0000
+++ b/cgi-bin/LJ/User.pm	Fri Apr 23 01:34:39 2010 +0000
@@ -4974,6 +4974,16 @@ sub has_priv {
 sub has_priv {
     my ( $u, $priv, $arg ) = @_;
 
+    # check to see if the priv is packed, and unpack it if so, this allows
+    # someone to call $u->has_priv( "foo:*" ) instead of $u->has_priv( foo => '*' )
+    # which is helpful for some callers.
+    ( $priv, $arg ) = ( $1, $2 )
+        if $priv =~ /^(.+?):(.+)$/;
+
+    # at this point, if they didn't provide us with a priv, bail out
+    return 0 unless $priv;
+
+    # load what privileges the user has, if we haven't
     LJ::load_user_privs($u, $priv)
         unless $u->{'_privloaded'}->{$priv};
 
--------------------------------------------------------------------------------