#!/usr/bin/perl


## W3C chaclCGI - change access control list for a resource

#Copyright Massachusetts Institute of technology, 2000.
#Written by Eric Prud'hommeaux

#things that need to be done:
#1. purty it up (add icons)
#2. consider "like $resource%"

#####
# What It Does:
# presents form to edit ACLs for a resource. The form is preloaded with the 
# current ACLs for that resource. chaclCGI hands its output back to itself.

#####
# set up module environment

#BEGIN {unshift@INC,('../../..');}

package chacl;
use CGI;
use strict;
use W3C::Util::W3CDebugCGI;
use W3C::Util::Exception qw(&throw &catch &DieHandler);
use W3C::Rnodes::CGIChacl qw($SOURCE_chacl);
use W3C::Rnodes::CvsLogAgent;
use W3C::Rnodes::CvsPermAgent;
use W3C::Rnodes::RdfAclAgent;
use W3C::Rnodes::W3CAclAgent;
use W3C::Rnodes::ApacheConfAgent;
use W3C::Rdf::Atoms qw($RDF_SCHEMA_URI);
use W3C::Rdf::RdfApp;
use W3C::Rnodes::ACL qw($ACCESS_FOR_FILESYSTEM_RW $ACCESS_FOR_FILESYSTEM_READ $ACCESS_RACL $ACCESS_CHACL $MACRO_RULENAME $MACRO_RULEICON 
			$MACRO_RULES $MACRO_RULE_TYPE $MACRO_RULE_ID $MACRO_RULE_ACCESS
			%TEXT_TYPE $ACL_SCHEMA_URI &accessBitFieldList);
# W3C-specific ACL layous are kept in a separate module
use W3C::Rnodes::W3CWebAcls qw(@MACRO_OPTIONS @MACRO_LAYOUT
			       &getStandardW3CMacro &standardDescToW3CMacro
			       &standardW3CMacroToDesc &standardW3CMacroToIcon
			       &standardMatch);

use vars qw($REVISION $VERSION @ISA);
$VERSION=0.10;
$REVISION = '$Id: chacl,v 1.70 2007/03/26 22:28:32 jean-gui Exp $ ';
@ISA = qw(W3C::Rnodes::CGIChacl);

# Queries to get a user's prefs from the persistent DB
use vars qw($PREFS_QUERY_FULL_LAYOUT $PREFS_QUERY_MACRO_SET
	    $PREFS_QUERY_LAYOUT $PREFS_QUERY_OPTIONS $PREFS_QUERY_MACRO);

use vars qw($CHACL_SCHEMA_URI);
$CHACL_SCHEMA_URI = 'http://www.w3.org/2001/02/chacl/ns';
# This query would be used to pre-fill a local DB to make the queries more efficient
$PREFS_QUERY_FULL_LAYOUT = <<EOF
(ask '((${RDF_SCHEMA_URI}type ?layout ${CHACL_SCHEMA_URI}Layout)
       (${CHACL_SCHEMA_URI}inRows ?layout ?rowSeq)
       (${RDF_SCHEMA_URI}li ?rowSeq ?row)
       (${CHACL_SCHEMA_URI}inColumns ?row ?colSeq)
       (${RDF_SCHEMA_URI}li ?colSeq ?macro)
       (${CHACL_SCHEMA_URI}Title ?macro ?title)
       (${ACL_SCHEMA_URI}acl ?macro ?acl)
       (${ACL_SCHEMA_URI}img ?macro ?img)
       (${ACL_SCHEMA_URI}resourceAccessRule ?macro ?rule)
       (${ACL_SCHEMA_URI}accessor ?rule ?accessor)
       (${ACL_SCHEMA_URI}access ?rule ?access))
 :collect '(?row ?macro ?title ?acl ?img ?accessor ?access))
EOF
    ;

$PREFS_QUERY_MACRO_SET = <<EOF
(ask '((${RDF_SCHEMA_URI}type ?macroSet ${CHACL_SCHEMA_URI}MacroSet)
       (${CHACL_SCHEMA_URI}inOrder ?macroSet ?macroSeq)
       (${RDF_SCHEMA_URI}li ?macroSeq ?macro)
       (${CHACL_SCHEMA_URI}Title ?macro ?title)
       (${ACL_SCHEMA_URI}acl ?macro ?acl)
       (${ACL_SCHEMA_URI}img ?macro ?img)
       (${ACL_SCHEMA_URI}resourceAccessRule ?macro ?rule)
       (${ACL_SCHEMA_URI}accessor ?rule ?accessor)
       (${ACL_SCHEMA_URI}access ?rule ?access))
 :collect '(?macro ?title ?acl ?img ?rule ?accessor ?access))
EOF
    ;

$PREFS_QUERY_LAYOUT = <<EOF
(ask '((${RDF_SCHEMA_URI}type ?layout ${CHACL_SCHEMA_URI}Layout)
       (${CHACL_SCHEMA_URI}inRows ?layout ?rowSeq)
       (${RDF_SCHEMA_URI}li ?rowSeq ?row)
       (${CHACL_SCHEMA_URI}inColumns ?row ?colSeq)
       (${RDF_SCHEMA_URI}li ?colSeq ?macro))
 :collect '(?row ?macro))
EOF
    ;

$PREFS_QUERY_OPTIONS = <<EOF
(ask '((${RDF_SCHEMA_URI}type ?layout ${CHACL_SCHEMA_URI}Options)
       (${CHACL_SCHEMA_URI}inOrder ?layout ?seq)
       (${RDF_SCHEMA_URI}li ?seq ?option))
 :collect '(?option))
EOF
    ;

$PREFS_QUERY_MACRO = <<EOF
(ask '((${CHACL_SCHEMA_URI}Title ?macro ?title)
       (${ACL_SCHEMA_URI}acl ?macro ?acl)
       (${ACL_SCHEMA_URI}img ?macro ?img)
       (${ACL_SCHEMA_URI}resourceAccessRule ?macro ?rule)
       (${ACL_SCHEMA_URI}accessor ?rule ?accessor)
       (${ACL_SCHEMA_URI}access ?rule ?access))
 :collect '(?title ?acl ?img ?accessor ?access))
EOF
    ;

use vars qw($SOURCE_aclDB $SOURCE_AFS $SOURCE_CVS $SOURCE_CVSPerm $SOURCE_apache);
$SOURCE_aclDB = 0x10;
$SOURCE_AFS = 0x20;
$SOURCE_CVS = 0x40;
$SOURCE_CVSPerm = 0x80;
$SOURCE_apache = 0x100;

&main::main('chacl');

sub loadAgents {
    my ($self) = @_;
    $self->{-htmlHeaderAddendum} = <<EOF
    <meta name="ROBOTS" content="NOINDEX, NOFOLLOW"/>
    <style type="text/css">
	TD.fixme,DIV.fixme,SPAN.fixme { 
		background:#FF4040;
	}
    </style>
EOF
    ;
    $self->{SOURCE_TO_STRING} = {$SOURCE_aclDB=>'<a href="#source_aclDB">ACLs DB</a>', 
				 $SOURCE_AFS=>'<a href="#source_AFS">Mirror (Unix permissions)</a>', 
				 $SOURCE_CVS=>'<a href="#source_CVS">CVS Log</a>', 
				 $SOURCE_CVSPerm=>'<a href="#source_CVS">CVS (Unix permissions)</a>', 
				 $SOURCE_chacl=>'<a href="#source_chacl">Previous Form</a>',
				 $SOURCE_apache=>'<a href="#source_apache">Apache configuration</a> [<strong>cannot be changed with this tool</strong>]'
				 };

    $self->{SOURCE_TO_CLASS} = {$SOURCE_aclDB=>'', 
				$SOURCE_AFS=>'class="fixme"', 
				$SOURCE_CVS=>'class="fixme"', 
				$SOURCE_CVSPerm=>'class="fixme"', 
				$SOURCE_apache=>'',
				$SOURCE_chacl=>''};

    $self->{ACL_REPOSITORY} = new W3C::Rnodes::W3CAclAgent(-properties => $self->{PROPERTIES}, -sourceID => $SOURCE_aclDB);
    my $apacheAgent = new W3C::Rnodes::ApacheConfAgent(-properties => $self->{PROPERTIES}, -sourceID => $SOURCE_apache,
							   -getFilepathMaps => sub {
						     $self->_getAFSFilepathMaps(@_)},
						       -getRules => sub { $self->_getApacheConfigRules(@_)});
    my $cvsLogAgent = new W3C::Rnodes::CvsLogAgent(-properties => $self->{PROPERTIES}, -sourceID => $SOURCE_CVS, 
						   -getFilepathMaps => sub {
						       $self->_getCVSFilepathMaps(@_)}, 
						   -getFilesystemIdsAndTypes => sub {
						       $self->_getFilesystemIdsAndTypes(@_)});
    my $cvsPermAgent = new W3C::Rnodes::CvsPermAgent(-properties => $self->{PROPERTIES}, -sourceID => $SOURCE_CVSPerm, 
						 -getFilepathMaps => sub {
						     $self->_getCVSFilepathMaps(@_)}, 
						 -getFilesystemIdsAndTypes => sub {
						     $self->_getFilesystemIdsAndTypes(@_)});
    my $afsAgent = new W3C::Rnodes::UnixPermAgent(-properties => $self->{PROPERTIES}, -sourceID => $SOURCE_AFS, 
						   -getFilepathMaps => sub {
						       $self->_getAFSFilepathMaps(@_)}, 
						   -getFilesystemIdsAndTypes => sub {
						       $self->_getFilesystemIdsAndTypes(@_)});
    $self->{ACL_IMPORTERS} = [$apacheAgent, $self->{ACL_REPOSITORY}, $cvsPermAgent, $cvsLogAgent, $afsAgent];

    $self->{OVERRIDES} = 0;

    # Set OVERRIDES with the additional permissions specially granted the authenticated user. 
    if (my $overrides = $self->{PROPERTIES}->getI('ids.override')) {
	my ($id, @accesses) = split(',', $overrides);
	my $access = &accessBitFieldList(@accesses);
	my @hierarchy; # = $self->{ACL_REPOSITORY}->groupHierarchy($id);
	eval {
	    # executeSingleQuery will throw an exception if there is not exactly one solution.
	    $self->{ACL_REPOSITORY}{DB}->executeSingleQuery("SELECT 1 FROM idInclusions,ids AS sub,ids AS super WHERE idInclusions.id=sub.id AND idInclusions.groupId=super.id AND sub.value=\"$ENV{REMOTE_USER}\" and super.value=\"$id\"");
	    $self->{OVERRIDES} = $access;
	}
    }
}

#-------------- Extraneous stuff 'cause we talk to the database ----------------
sub _getCVSFilepathMaps {
    my ($self) = @_;
    return [['http://www.w3.org/.*', 
	     'http://www.w3.org/CGI/chacl', 
	     '/w3ccvs/WWW', 
	     's|\Ahttp://www.w3.org/|/w3ccvs/WWW/|', 
	     's|\A/w3ccvs/WWW/|http://www.w3.org/|'], 
	    ['http://quake.w3.org/.*', 
	     'http://quake.w3.org/CGI/chacl', 
	     '/w3ccvs/WWW', 
	     's|\Ahttp://quake.w3.org/|/w3ccvs/WWW/|', 
	     's|\A/w3ccvs/WWW/|http://quake.w3.org/|'], 
	    ['http://localhost/.*', 
	     'http://localhost/CGI/chacl', 
	     '/w3ccvs/WWW', 
	     's|\Ahttp://localhost/|/w3ccvs/WWW/|', 
	     's|\A/w3ccvs/WWW/|http://localhost/|']]; # @@@ need to get this from *somewhere*...
}

sub _getApacheConfigRules {
    my ($self,$path) = @_;
    # ACLd zones
    if ($path =~ m:^/(1998|1999|2000|2001|2002|20\d\d|P3P|Project|RDF/Metalog|Systems/db|Systems/Sophia|Guide|WAI/Steer|Consortium/Hosts/Keio|Consortium/Legal|Submission|Security|News)/:) {
	return [];
    }

    # Member areas
    if ($path =~ m:(^/TandS/Member/|/Group/|/ERB/|^/MarkUp/CoordGroup/|^/Search/Mail/Member/): || $path =~ m:^/(Member|member)/: || $path =~ m:^/Consortium/Process/Plan/: || $path =~ m:^/ECommerce/Micropayments/Paris/:) {
	return [['G','w3cteamgroup',$ACCESS_FOR_FILESYSTEM_RW],
		['G','cabal',$ACCESS_FOR_FILESYSTEM_READ]];
    }

    # Team areas
    if ($path =~ m:^/team/|^/Team/|/Plan/|^/Project/|^/Systems/|^/Web/|^/Security/Config/|^/Repository/: || $path =~ m:^/(ArchiveBrowser|htbin|ismap|server-status|server-info)/: || $path =~ m:^/History/(1994/WWW/Organization/Consortium/BAA9406|1995/WWW/Administration|19921103-hypertext/hypertext/WWW/Administration|old)/:) {
	return [['G','w3cteamgroup',$ACCESS_FOR_FILESYSTEM_RW]];
    }
    if ($path =~ m:^/Consortium/Offices/internal/:) {
	return [['G','w3cteamgroup',$ACCESS_FOR_FILESYSTEM_RW],
		['G','w3c_offices',$ACCESS_FOR_FILESYSTEM_READ]];
    }
    if ($path =~ m:^/W3C-LA/.*Partner/:) {
	return [['G','w3cteamgroup',$ACCESS_FOR_FILESYSTEM_RW],
		['G','w3clagroup',$ACCESS_FOR_FILESYSTEM_READ]];
    }
    if ($path =~ m:^/W3C-LA/Core/:) {
	return [['G','w3cteamgroup',$ACCESS_FOR_FILESYSTEM_RW],
		['G','w3clacoregroup',$ACCESS_FOR_FILESYSTEM_READ]];
    }
    # Everybody has access by default;
    return [['G','w3cteamgroup',$ACCESS_FOR_FILESYSTEM_RW],
	    ['A','all',$ACCESS_FOR_FILESYSTEM_READ]];
}

sub _getAFSFilepathMaps {
    my ($self) = @_;
    my @maps;
    $self->{ACL_REPOSITORY}{DB}->executeArrayQuery(\@maps, 'SELECT uri,chacl,filepath,map,fileToURI FROM uriMaps');
    # reverse sort the map regexps so the first match is the most-specific
    return [sort {$b->[0] cmp $a->[0]} @maps];
}

sub _getFilesystemIdsAndTypes {
    my ($self, $filesystemId, $filesystemType) = @_;
    my (@webIds, %webTypes);
    my $typeStr = $self->{ACL_REPOSITORY}{DB}->escape($filesystemType);
    $self->{ACL_REPOSITORY}{DB}->executeQuery(\@webIds, \%webTypes, "
SELECT value,ids.type
  FROM fileSystemIds,ids
 WHERE fileSystemIds.id=$filesystemId
   AND fileSystemIds.type=$typeStr
   AND ids.id=fileSystemIds.webId");
    return (\@webIds, \%webTypes);
}

sub invokeImporter {
    my ($self, $importer, $aclDB, $resourceList, $wildcard, $recursive) = @_;
    my $ret;
    eval {
	$ret = $importer->getAclsFor($aclDB, $resourceList, $wildcard, $recursive);
    }; if ($@) {if (my $ex = &catch('W3C::Rnodes::UnixPermAgent::NoMapForResourceException')) {
	&throw($self->noMapsForURI(ref $importer, $ex->getHost, $ex->getUri));
    } else {&throw()}}
    return $ret;
}

sub readPrefs {
    my ($self, $prefs) = @_;
    my $rdfApp = new W3C::Rdf::RdfApp([], -atomDictionary => $self->{-atomDictionary});
    # $rdfApp->{ARGS}{-files} = [$prefs];
    $rdfApp->prepareParser();
    $rdfApp->parseOne(&W3C::Rdf::RdfApp::getInputSource($prefs));
    my $queryHandler = $rdfApp->{RDF_DB}->getAlgaeInterface;
    my $query = $PREFS_QUERY_MACRO_SET;
    my $sysID = undef; # $rdfApp->{RDF_DB}->getUri($self->getSelfPath() ? 'http://'.$ENV{'SERVER_NAME'}.$self->getSelfPath() : 'http://www.w3.org/');
    my ($rows, $selects, $messages) = $queryHandler->algae($query, $sysID, {-uniqueResults => 1});
    my ($curMacro, $curRule);
    $self->{MACRO_OPTIONS} = [];
    foreach my $row (@$rows) {
	my ($macro, $title, $acl, $img, $rule, $accessor, $access) = 
	    map {$_->isa('W3C::Rdf::String') ? $_->getString : $_->getUri} @$row;
	# print sprintf ("%s %s %s %s %s %s\n", $option, $title, $acl, $img, $accessor, $access);
	if ($macro ne $curMacro) {
	    $curMacro = $macro;
	    push (@{$self->{MACRO_OPTIONS}}, []);
	    $curRule = undef;
	}
	if ($rule ne $curRule) {
	    $curRule = $rule;
	    push (@{$self->{MACRO_OPTIONS}[-1][$MACRO_RULES]}, []);
	}
	my ($type, $id);
	if ($accessor eq "${RDF_SCHEMA_URI}NULL") {
	    ($type, $id) = ('U', undef);
	} else {
	    ($type, $id) = &parseDBRuleId($accessor);
	    $type = $TEXT_TYPE{$type};
	} &throw();
	$access = &accessBitFieldList($access);
	$self->{MACRO_OPTIONS}[-1][$MACRO_RULENAME] = $title;
	$self->{MACRO_OPTIONS}[-1][$MACRO_RULE_ID] = $acl;
	$self->{MACRO_OPTIONS}[-1][$MACRO_RULEICON] = $img;
	$self->{MACRO_OPTIONS}[-1][$MACRO_RULES][-1][$MACRO_RULE_TYPE] = $type;
	$self->{MACRO_OPTIONS}[-1][$MACRO_RULES][-1][$MACRO_RULE_ID] = $id;
	$self->{MACRO_OPTIONS}[-1][$MACRO_RULES][-1][$MACRO_RULE_ACCESS] = $access;
    }
# file://quake.w3.org/usr/local/httpd/WWW/Systems/Code/Conf/chaclPrefs.rdf#row1
# http://www.w3.org/Icons/toc3off.gif
# http://www.w3.org/Systems/db/webId
# chacl,racl,head,get,put,delete,connect,options,trace
# racl,head,get,options,trace

#row1 #user   "user"   "4" img:toc5off.gif rdf:NULL               "ALL"
#row1 #team   "team"   "5" img:toc3off.gif ids:group=w3cteamgroup "ALL"
#row1 #member "member" "6" img:toc2off.gif ids:group=cabal        "READ"
#row1 #member "member" "6" img:toc2off.gif ids:group=w3cteamgroup "ALL"
#row1 #world  "world"  "7" img:toc1off.gif ids:all=all            "READ"
#row1 #world  "world"  "7" img:toc1off.gif ids:group=w3cteamgroup "ALL"
#row2 #team   "team"   "5" img:toc3off.gif ids:group=w3cteamgroup "ALL"
#row2 #world  "world"  "7" img:toc3off.gif ids:all=all            "READ"
#row2 #world  "world"  "7" img:toc3off.gif ids:group=w3cteamgroup "ALL"
}

sub getEffectiveAccess {
    my ($self, $aclDB, $resource) = @_;
    my $ret = $aclDB->getResourceAttribute($resource, 'access');
    return $ret | $self->{OVERRIDES};
}

sub discoverClientsInclusions {
    my ($self) = @_;
    $self->SUPER::discoverClientsInclusions();
    $self->{MAX_ACCESS} |= $self->{OVERRIDES};
    $self->{MIN_ACCESS} |= $self->{OVERRIDES};
}

sub getRuleInfo {
    my ($self, $aclCheckDB, $user, $byType, $resources) = @_;
    my ($acl, $diffs, $userMatches) = &standardMatch($aclCheckDB, $user, $byType, $resources);
    my $ruleName = $MACRO_OPTIONS[$acl][$MACRO_RULENAME];
    return ($acl, $diffs, $userMatches, $ruleName);
}

sub getSourceDescriptions {
    my ($self) = @_;
    return <<EOF
<p>See also how  you can define a <a href='http://www.w3.org/2002/02/auto-acl'>default ACL for a subtree</a> of the Web space.</p>
<!-- some vocabulary terms -->
<h2><a name="vocabulary">Sources of ACLs for resources:</a></h2>

<dl>
   <dt><a name="source_aclDB">ACL DB</a></dt><dd>This ACL came from the ACL Database - This file has an ACL.</dd>
   <dt><a name="source_AFS">AFS</a></dt><dd>The ACL came from the AFS filesystem permissions - 
       <span class="fixme">This file has no ACL yet.</span></dd>
   <dt><a name="source_CVS">CVS</a></dt><dd>The ACL came from CVS - 
       <span class="fixme">This file has no ACL yet.</span></dd>
   <dt><a name="source_apache">Apache configuration</a></dt><dd>The ACL is set in the configuration of the Web server. It cannot be changed with this tool.</dd>
   <dt><a name="source_chacl">last form</a></dt><dd>This ACL came from the Previous Form.</dd>
</dl>
<p>(<a name="nb" id="nb">*</a>) note that for any of these sources and for the time being, the PUT and DELETE methods results are not really based on the defined ACLs, but on an entirely different authorization scheme</p>
EOF
    ;
}

sub renderLocalMacros {
    my $ret;
    foreach my $row (@MACRO_LAYOUT) {
	$ret .= "  <tr>\n";
	foreach my $macro (@$row) {
	    $ret .= "    <td align=\"center\">\n";
	    $ret .= "      <input type=\"submit\" name=\"w3c_macro\" value=\"$macro->[$MACRO_RULENAME]\" /></td>\n";
	}
	$ret .= "  </tr>\n";
    }
    return (scalar @{$MACRO_LAYOUT[0]}, $ret);
}

sub getStandardMacro { # static
    my ($self, $aclDB, $user) = @_;
    return &getStandardW3CMacro($aclDB, $user);
}

sub standardDescToMacro { # static
    my ($self, $macroName) = @_;
    return &standardDescToW3CMacro($macroName);
}

sub standardMacroToDesc {
    my ($self, $macroId) = @_;
    return &standardW3CMacroToDesc($macroId);
}

sub standardMacroToIcon {
    my ($self, $macroId) = @_;
    return &standardW3CMacroToIcon($macroId);
}

__END__

=head1 NAME chacl

CGI::chacl - CGI interface for setting access to web resources

=head1 SYNOPSIS

  http://<server>/CGI/chacl

=head1 DESCRIPTION

The chacl script is called via the CGI interface from a webserver.

chacl enables the user to browse and set Access Control Lists (henceforth ACLs) via an HTTP CGI interface. With no parameters, chacl prompts the user for the resources for which to browse/change ACLs. With a set of resorces, chacl checks the list of ACL sources and displays the ACLs for those resources.

This module is used with the W3C::Rnodes CPAN module.

=head2 Authentication

Users actions are evaluated prior to execution and are only permitted if the requesting user has access to perform the requested function. For instance, before displaying ACLs for a resource, chacl makes sure that one of the following is true: everyone may see the ACL, the authenticated user may see the resource ACLs, or anyone connecting from the remote address may see the resource ACLs. Smilar check are performed when the user attempts to alter ACLs for a resource. The user may elect to change identity, even when it is not required for the desired action. The "Change Authentication" will cause chacl to require new authentication, which will cause the client to prompt the user for a new identity and password.

=head2 User Input

User input comes in four flavors: L<presentation|/"Presentation">, L<state selection|/"State selection">, L<state manipulation|/"State manipulation">, and L<commit|/"Commit">. state manipulation and commit require the user to select when modifying the ACLs for multiple resources at once. Javascript-enabled browsers will have extra buttons for manipulating the resourse selection.

=head2 User macros

The system is designed to allow the user to make streamlined ACL selections. These ACLs represent a common configuration that the user will use repleated. Asl the system administrator to create these macros.

=head2 RDF

The chacl system is based on RDF assertions of trust. Internal state is maintained by a block of RDF that is optionally hidden or editable depending on whether chacl is in "expert mode". This state is ephemoral and goes away unless the user commits the changes with the "Replace ACLs with RDF below" button.

=head2 Presentation

Several buttons control the chacl display. The options inlcude:

    help - display verbose help interspersed with the
           user prompts.
    list / diff - view ACLs as a comprehensive list or
                  as changes to the closest macro.
    tabular form - justify resources/ACLs by placing
                   output in a large table.
    expert mode - view the extended "expert" options.

=head2 State selection

In "expert" mode, the user may get lists of web ids for whom to add or remove ACLs.

=head2 State manipulation

Once the user has selected some web ids and ACLs in the "State selection" step, He/she must tell chacl to write these changes back to the RDF. This is done with the "Replace ACLs with RDF below" button.

=head2 Commit

Commit actions are ones that specifically write ACLs to the ACL repository. These include the User Macros as well as the Commit RDF Below button.

=head2 ACL Sources

chacl can get ACLs from a list of ACL agents, For instance, W3CAclAgent, UnixPermAgent, RdfAclAgent, and maybe, someday, the CVSAclAgent. These agents support the AclImportInterface, mainly, the getAclsFor method. One of these agents will also likely serve as the repository. The AclRepositoryInterface has methods to setAclsFor and deleteAclFor.

=head2 Architecture



=head1 AUTHOR

Eric Prud'hommeaux <eric@w3.org>

=head1 SEE ALSO

W3C::Rnodes::ACL(3) W3C::Rnodes::W3CAclAgent(3) W3C::Rnodes::UnixPermAgent(3) W3C::Rnodes::RdfAclAgent(3).

=cut
