[MKDoc-commit] Patch to add user-group functionality (Sam Tregar).

bruno at mkdoc.demon.co.uk bruno at mkdoc.demon.co.uk
Wed Jan 26 13:19:54 GMT 2005


Log Message:
-----------
Patch to add user-group functionality (Sam Tregar). Note: 3 SQL tables need
to be created to avoid breaking existing sites.

Tags:
----
mkdoc-1-6

Modified Files:
--------------
    mkd/MKDoc/Site/ConfigWriter:
        Httpd_Conf.pm
    mkd/MKDoc/Site/Deploy/DB:
        Schema.pm
    mkd/flo/plugin/Admin:
        UserDelete.pm
        UserInsert.pm
        UserModify.pm
    mkd/templates/admin/user_insert:
        en.html
    mkd/templates/admin/user_modify:
        en.html

Added Files:
-----------
    mkd/MKDoc/Handler:
        GroupAuthz.pm

-------------- next part --------------
--- /dev/null
+++ MKDoc/Handler/GroupAuthz.pm
@@ -0,0 +1,123 @@
+# ----------------------------------------------------------------------------
+# MKDoc::Handler::GroupAuthz
+# ----------------------------------------------------------------------------
+# Author: Sam Tregar
+# Copyright: (c) MKDoc Holdings Ltd, 2005
+#
+# This file is part of MKDoc. 
+# 
+# MKDoc is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+# 
+# MKDoc is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with MKDoc; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+#
+# ---------------------------------------------------------------------------
+package MKDoc::Handler::GroupAuthz;
+use strict;
+use warnings;
+use Apache::Constants qw/:common/;
+use flo::Standard;
+use strict;
+use Carp;
+
+=head1 NAME
+
+MKDoc::Handler::GroupAuthz - enforce group-based security
+
+=head1 SYNOPSIS
+
+In httpd.conf for an authenticating host (like the default users.* setup):
+
+  PerlAuthzHandler MKDoc::Handler::GroupAuthz
+
+In httpd.conf for a non-authenticating host (like the default www.* setup):
+
+  <Location />
+    PerlModule MKDoc::Handler::GroupAuthz
+    PerlAuthenHandler MKDoc::Handler::GroupAuthz->null_authen_handler
+    PerlAuthzHandler  MKDoc::Handler::GroupAuthz
+    AuthName "Group Authorization"
+    AuthType GroupAuthz
+    require valid-group
+  </Location>
+
+=head1 DESCRIPTION
+
+Checks for group authorization in MKDoc's Grp tables.  If the document
+requested is assigned to one or more groups in Document_Grp then the
+user must be assigned to one of the groups in Editor_Grp.
+
+Users may be assigned to groups in the user editor by the admin.
+Groups are managed directly in the database (for now).
+
+=head1 AUTHOR
+
+Sam Tregar <sam at tregar.com>
+
+=cut
+
+sub handler {
+    my $r    = shift;
+    my $user = $::MKD_USER;
+
+    # get the current document, locally setting PATH_INFO which is
+    # needed by the current_document() code but not yet setup by
+    # Apache at this stage
+    my $document = do {
+        local $ENV{PATH_INFO} = $r->path_info;
+        flo::Standard::current_document();
+    };
+
+    # if it wasn't found then this request can't be group protected
+    # (could be a CSS link, an image, etc)
+    return OK unless $document;
+
+    # check if this document is assigned to one or more groups
+    my $document_grp_t = flo::Standard::table('Document_Grp');
+    my @res = $document_grp_t->select (
+	cols => 'Grp_ID',
+        where => lib::sql::Condition->new(Document_ID => $document->id)
+                                      )->fetch_all();
+
+    # no results means this document is available to all
+    return OK unless @res;
+
+    # if the user isn't correctly logged in then they can't see this
+    # page
+    return FORBIDDEN unless $user and ref $user and $user->can('id');
+
+    # get a list of the user's groups.  (It would be nice to just do a
+    # query against Editor_Grp like (editor_id = ? AND (grp_id = ? OR
+    # ...)) but I can't figure out how to get lib::sql to do anything
+    # that complicated.  SQL replacements are great, huh?)
+    my $editor_grp_t = flo::Standard::table('Editor_Grp');
+    my $con          = lib::sql::Condition->new(Editor_ID => $user->id);
+    my @groups       = $editor_grp_t->select(cols  => 'Grp_ID',
+                                             where => $con)->fetch_all;
+
+    # no results means the user wasn't in any of the groups, denied
+    return FORBIDDEN unless @groups;
+
+    # allow through if the user is in one of the document's groups
+    my %groups = map { ($_->{Grp_ID}, 1) } @groups;
+    return OK if grep { $groups{$_->{Grp_ID}} } @res;
+
+    # otherwise, no dice
+    return FORBIDDEN;
+}
+
+# an authen handler that does nothing.  This is needed to allow the
+# group authz mechanism to work on www.* which doesn't do
+# authentication.
+sub null_authen_handler ($$) { OK }
+
+1;
Index: Httpd_Conf.pm
===================================================================
RCS file: /var/spool/cvs/mkd/MKDoc/Site/ConfigWriter/Httpd_Conf.pm,v
retrieving revision 1.1.2.28
retrieving revision 1.1.2.29
diff -LMKDoc/Site/ConfigWriter/Httpd_Conf.pm -LMKDoc/Site/ConfigWriter/Httpd_Conf.pm -u -r1.1.2.28 -r1.1.2.29
--- MKDoc/Site/ConfigWriter/Httpd_Conf.pm
+++ MKDoc/Site/ConfigWriter/Httpd_Conf.pm
@@ -208,6 +208,7 @@
 #Editor: vim:syn=apache
 <Location />
   PerlAuthenHandler MKDoc::Handler::Authenticate
+  PerlAuthzHandler  MKDoc::Handler::GroupAuthz
   AuthName "Please enter your user credentials"
   AuthType Basic
   require valid-user
@@ -239,6 +240,16 @@
   PerlInitHandler                       MKDoc::Handler::Initialize
   Include                               $SITE_DIR/httpd/httpd-static.conf
   Include                               $SITE_DIR/httpd/httpd-mkdoc.conf
+
+  <Location />
+    PerlModule MKDoc::Handler::GroupAuthz
+    PerlAuthenHandler MKDoc::Handler::GroupAuthz->null_authen_handler
+    PerlAuthzHandler  MKDoc::Handler::GroupAuthz
+    AuthName "Group Authorization"
+    AuthType GroupAuthz
+    require valid-group
+  </Location>
+
 </VirtualHost>
 
 <VirtualHost *>
Index: Schema.pm
===================================================================
RCS file: /var/spool/cvs/mkd/MKDoc/Site/Deploy/DB/Schema.pm,v
retrieving revision 1.1.2.6
retrieving revision 1.1.2.7
diff -LMKDoc/Site/Deploy/DB/Schema.pm -LMKDoc/Site/Deploy/DB/Schema.pm -u -r1.1.2.6 -r1.1.2.7
--- MKDoc/Site/Deploy/DB/Schema.pm
+++ MKDoc/Site/Deploy/DB/Schema.pm
@@ -364,6 +364,63 @@
    fk     => { Document => { Document_ID => 'ID' } },
   );
 
+## GRP TABLE - holds user groups.  (The table can't be named Group
+## because group is a reserved word in SQL.)
+new lib::sql::Table
+  (
+   name => 'Grp',
+   pk   => [ 'ID' ],
+   ai   => 1,
+   cols => [
+	    { name => 'ID',
+              type => lib::sql::type::Int->new( not_null => 1 ) },
+	    { name => 'Name',
+              type => lib::sql::type::Char->new( size => 255 ) },
+	    { name => 'Description',
+              type => lib::sql::type::Char->new( size => 255 ) },
+	   ]
+  );
+
+## EDITOR GRP TABLE - holds assignments of editors (users) to
+## groups.  When a user is assigned to a group she can access
+## documents assigned to that group.
+new lib::sql::Table
+  (
+   name => 'Editor_Grp',
+   pk   => [ 'Editor_ID', 'Grp_ID' ],
+   cols => [
+	    { name => 'Editor_ID',
+              type => lib::sql::type::Int->new( not_null => 1 ) },
+	    { name => 'Grp_ID',
+              type => lib::sql::type::Int->new( not_null => 1 ) },
+	   ],
+   fk => {
+	  Grp  => { Grp_ID  => 'ID' },
+	  Editor => { Editor_ID => 'ID' },
+	 },
+   index => { ReverseGrpEditorIndex => [ 'Grp_ID', 'Editor_ID' ] },
+  );
+
+## DOCUMENT GRP TABLE - holds assignments of documents to groups.
+## When a document is assigned to one or more groups only members of
+## that group can access the document.
+new lib::sql::Table
+  (
+   name => 'Document_Grp',
+   pk   => [ 'Document_ID', 'Grp_ID' ],
+   cols => [
+	    { name => 'Document_ID',
+              type => lib::sql::type::Int->new( not_null => 1 ) },
+	    { name => 'Grp_ID',
+              type => lib::sql::type::Int->new( not_null => 1 ) },
+	   ],
+   fk => {
+	  Grp    => { Grp_ID    => 'ID' },
+	  Document => { Document_ID => 'ID' },
+	 },
+   index => { ReverseGrpDocumentIndex => [ 'Grp_ID', 'Document_ID' ] },
+  );
+
 1;
 
 __END__
Index: UserDelete.pm
===================================================================
RCS file: /var/spool/cvs/mkd/flo/plugin/Admin/UserDelete.pm,v
retrieving revision 1.1.2.4
retrieving revision 1.1.2.5
diff -Lflo/plugin/Admin/UserDelete.pm -Lflo/plugin/Admin/UserDelete.pm -u -r1.1.2.4 -r1.1.2.5
--- flo/plugin/Admin/UserDelete.pm
+++ flo/plugin/Admin/UserDelete.pm
@@ -47,6 +47,11 @@
 {
     my $self = shift;
     my $user_edit = $self->user_edit();
+
+    # clear grp assignments
+    my $editor_group_t = flo::Standard::table ('Editor_Grp');
+    $editor_group_t->delete(Editor_ID => $user_edit->id);
+
     $user_edit->delete();
 
     use flo::plugin::Admin::UserList;
Index: UserModify.pm
===================================================================
RCS file: /var/spool/cvs/mkd/flo/plugin/Admin/UserModify.pm,v
retrieving revision 1.1.2.5
retrieving revision 1.1.2.6
diff -Lflo/plugin/Admin/UserModify.pm -Lflo/plugin/Admin/UserModify.pm -u -r1.1.2.5 -r1.1.2.6
--- flo/plugin/Admin/UserModify.pm
+++ flo/plugin/Admin/UserModify.pm
@@ -70,6 +70,7 @@
 
     $user_edit->save();
     $self->insert_base_document ($user_edit->id());
+    $self->insert_groups ($user_edit->id());
     $self->{ok} = 1;
     return $self->http_get();
 }
@@ -88,6 +89,19 @@
     $base_document_t->insert (Editor_ID => $id, Document_ID => $_) for (@base_documents);
 }
 
+# create entries in Editor_Grp for group choices
+sub insert_groups
+{
+    my ($self, $id) = @_;
+    my $cgi         = flo::Standard::cgi();
+    my @groups      = $cgi->param('groups');
+    
+    my $editor_group_t = flo::Standard::table ('Editor_Grp');
+    $editor_group_t->delete(Editor_ID => $id);
+    $editor_group_t->insert(Editor_ID => $id, Grp_ID => $_) 
+      for @groups;
+}
+
 
 sub documents
 {
@@ -102,7 +116,20 @@
     return wantarray ? @res : \@res;
 }
 
+# return list of available groups for template
+sub groups {
+    my $self = shift;
+    my $document_t = flo::Standard::table ('Grp');
+    my @res = $document_t->select (
+	cols => ['ID', 'Name' ],
+	sort => [ 'Name' ],
+	desc => 0,
+       )->fetch_all();
 
+    return wantarray ? @res : \@res;
+}
+
+# returns true if the document is selected, false if not
 sub is_selected
 {
     my $self = shift;
@@ -114,5 +141,21 @@
     return;
 }
 
+# returns true if the group is selected, false if not
+sub is_group_selected {
+    my ($self, $id) = @_;
+
+    # look for a row for this editor and group in the Editor_Grp table
+    my $grp_t = flo::Standard::table ('Editor_Grp');
+    my @res = $grp_t->select (
+	cols  => ['Grp_ID'],
+        where => lib::sql::Condition->new(Grp_ID    => $id,
+                                          Editor_ID => $self->user_edit->id)
+                                  )->fetch_all();
+
+    return 1 if @res;
+    return;
+}
+
 
 1;
Index: UserInsert.pm
===================================================================
RCS file: /var/spool/cvs/mkd/flo/plugin/Admin/UserInsert.pm,v
retrieving revision 1.1.2.3
retrieving revision 1.1.2.4
diff -Lflo/plugin/Admin/UserInsert.pm -Lflo/plugin/Admin/UserInsert.pm -u -r1.1.2.3 -r1.1.2.4
--- flo/plugin/Admin/UserInsert.pm
+++ flo/plugin/Admin/UserInsert.pm
@@ -45,6 +45,7 @@
     
     $self->has_errors() and return $self->http_get();
     $self->insert_base_document ($user->id());
+    $self->insert_groups ($user->id());
     
     $self->{ok} = 1;
     $cgi->delete ($cgi->param());
@@ -64,6 +65,18 @@
     $base_document_t->insert (Editor_ID => $id, Document_ID => $_) for (@base_documents);
 }
 
+# create entries in Editor_Grp for group choices
+sub insert_groups
+{
+    my ($self, $id) = @_;
+    my $cgi         = flo::Standard::cgi();
+    my @groups      = $cgi->param('groups');
+    
+    my $editor_group_t = flo::Standard::table ('Editor_Grp');
+    $editor_group_t->delete(Editor_ID => $id);
+    $editor_group_t->insert(Editor_ID => $id, Grp_ID => $_) 
+      for @groups;
+}
 
 sub documents
 {
@@ -78,6 +91,18 @@
     return wantarray ? @res : \@res;
 }
 
+# return list of available groups for template
+sub groups {
+    my $self = shift;
+    my $document_t = flo::Standard::table ('Grp');
+    my @res = $document_t->select (
+	cols => ['ID', 'Name' ],
+	sort => [ 'Name' ],
+	desc => 0,
+       )->fetch_all();
+
+    return wantarray ? @res : \@res;
+}
 
 sub is_selected
 {
Index: en.html
===================================================================
RCS file: /var/spool/cvs/mkd/templates/admin/user_insert/Attic/en.html,v
retrieving revision 1.1.2.16
retrieving revision 1.1.2.17
diff -Ltemplates/admin/user_insert/en.html -Ltemplates/admin/user_insert/en.html -u -r1.1.2.16 -r1.1.2.17
--- templates/admin/user_insert/en.html
+++ templates/admin/user_insert/en.html
@@ -292,6 +292,43 @@
             <em
               class="help"
             >
+              Users may belong to one or more groups.
+              You can use groups to restrict access to areas
+              of your site to groups of users.  This restriction affects
+              both editing and viewing.  To select multiple groups or
+              select no groups you might need to use the 
+              <kbd>Ctrl</kbd> key.
+            </em>
+            <label
+              for="groups"
+            >Group(s)</label>
+            <br />
+            <select 
+              multiple="multiple" 
+              size="5"
+              id="groups"
+              name="groups"
+            >
+              <option 
+                title="Bar"
+                petal:repeat="group self/groups"
+                petal:attributes="value    group/ID;
+                                  title    group/ID;"
+                petal:content="group/Name"
+              >Happy People</option>
+            </select>
+          </p>
+
+          <p
+            lang="en"
+            xml:lang="en"
+            dir="ltr"
+            align="left"
+            petal:attributes="align align"
+          >
+            <em
+              class="help"
+            >
               Accounts can be enabled, which means that they can be used, or disabled which means that
               they can't be used.
             </em>
Index: en.html
===================================================================
RCS file: /var/spool/cvs/mkd/templates/admin/user_modify/Attic/en.html,v
retrieving revision 1.1.2.16
retrieving revision 1.1.2.17
diff -Ltemplates/admin/user_modify/en.html -Ltemplates/admin/user_modify/en.html -u -r1.1.2.16 -r1.1.2.17
--- templates/admin/user_modify/en.html
+++ templates/admin/user_modify/en.html
@@ -288,6 +288,44 @@
               >/foo/bar/</option>
             </select>
           </p>
+
+          <p
+            lang="en"
+            xml:lang="en"
+            dir="ltr"
+            align="left"
+            petal:attributes="align align"
+          >
+            <em
+              class="help"
+            >
+              Users may belong to one or more groups.
+              You can use groups to restrict access to areas
+              of your site to groups of users.  This restriction affects
+              both editing and viewing.  To select multiple groups or
+              select no groups you might need to use the 
+              <kbd>Ctrl</kbd> key.
+            </em>
+            <label
+              for="groups"
+            >Group(s)</label>
+            <br />
+            <select 
+              multiple="multiple" 
+              size="5"
+              id="groups"
+              name="groups"
+            >
+              <option 
+                title="Bar"
+                petal:repeat="group self/groups"
+                petal:attributes="selected self/is_group_selected $group/ID;
+                                  value    group/ID;
+                                  title    group/ID;"
+                petal:content="group/Name"
+              >Happy People</option>
+            </select>
+          </p>
              
           <p
             lang="en"


More information about the MKDoc-commit mailing list