{"thread":{"id":"9450","subject":"[PATCH 1/3] Teach update-paranoid how to store ACLs organized by groups","startedAt":"2007-08-09T06:38:09Z","lastAt":"2007-08-09T06:38:09Z","messageCount":1,"participants":["Shawn O. Pearce"],"isPatch":true,"patchVersion":1,"patchTotal":3},"messages":[{"id":"50286","messageId":"20070809063809.GA26843@spearce.org","threadId":"9450","inReplyTo":null,"subject":"[PATCH 1/3] Teach update-paranoid how to store ACLs organized by groups","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2007-08-09T06:38:09Z","receivedAt":"2007-08-09T06:38:09Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"In some applications of this paranoid update hook the set of ACL\nrules that need to be applied to a user can be large, and the\nnumber of users that those rules must also be applied to can be\nmore than a handful of individuals.  Rather than repeating the same\nrules multiple times (once for each user) we now allow users to be\nmembers of groups, where the group supplies the list of ACL rules.\nFor various reasons we don't depend on the underlying OS groups\nand instead perform our own group handling.\n\nUsers can be made a member of one or more groups by setting the\nuser.memberOf property within the \"users/$who.acl\" file:\n\n  [user]\n    memberOf = developer\n\tmemberOf = administrator\n\nThis will cause the hook to also parse the \"groups/$groupname.acl\"\nfile for each value of user.memberOf, and merge any allow rules\nthat match the current repository with the user's own private rules\n(if they had any).\n\nSince some rules are basically the same but may have a component\ndiffer based on the individual user, any user.* key may be inserted\ninto a rule using the \"${user.foo}\" syntax.  The allow rule does\nnot match if the user does not define one (and exactly one) value\nfor the key \"foo\".\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n contrib/hooks/update-paranoid |   60 ++++++++++++++++++++++++++++++-----------\n 1 files changed, 44 insertions(+), 16 deletions(-)\n\ndiff --git a/contrib/hooks/update-paranoid b/contrib/hooks/update-paranoid\nindex 5ee1835..fb2aca3 100644\n--- a/contrib/hooks/update-paranoid\n+++ b/contrib/hooks/update-paranoid\n@@ -118,22 +118,29 @@ sub info ($) {\n \tprint STDERR \"-Info-    $_[0]\\n\" if $debug;\n }\n \n-sub parse_config ($$) {\n-\tmy ($data, $fn) = @_;\n-\tinfo \"Loading $fn\";\n-\topen(I,'-|','git',\"--git-dir=$acl_git\",'cat-file','blob',$fn);\n+sub git_value (@) {\n+\topen(T,'-|','git',@_); local $_ = <T>; chop; close T; $_;\n+}\n+\n+sub parse_config ($$$$) {\n+\tmy $data = shift;\n+\tlocal $ENV{GIT_DIR} = shift;\n+\tmy $br = shift;\n+\tmy $fn = shift;\n+\tinfo \"Loading $br:$fn\";\n+\topen(I,'-|','git','cat-file','blob',\"$br:$fn\");\n \tmy $section = '';\n \twhile (<I>) {\n \t\tchomp;\n \t\tif (/^\\s*$/ || /^\\s*#/) {\n \t\t} elsif (/^\\[([a-z]+)\\]$/i) {\n-\t\t\t$section = $1;\n+\t\t\t$section = lc $1;\n \t\t} elsif (/^\\[([a-z]+)\\s+\"(.*)\"\\]$/i) {\n-\t\t\t$section = \"$1.$2\";\n+\t\t\t$section = join('.',lc $1,$2);\n \t\t} elsif (/^\\s*([a-z][a-z0-9]+)\\s*=\\s*(.*?)\\s*$/i) {\n-\t\t\tpush @{$data->{\"$section.$1\"}}, $2;\n+\t\t\tpush @{$data->{join('.',$section,lc $1)}}, $2;\n \t\t} else {\n-\t\t\tdeny \"bad config file line $. in $fn\";\n+\t\t\tdeny \"bad config file line $. in $br:$fn\";\n \t\t}\n \t}\n \tclose I;\n@@ -202,11 +209,6 @@ sub check_committers (@) {\n \t}\n }\n \n-sub git_value (@) {\n-\topen(T,'-|','git',@_); local $_ = <T>; chop; close T;\n-\t$_;\n-}\n-\n deny \"No GIT_DIR inherited from caller\" unless $git_dir;\n deny \"Need a ref name\" unless $ref;\n deny \"Refusing funny ref $ref\" unless $ref =~ s,^refs/,,;\n@@ -231,13 +233,39 @@ $op = 'U' if ($op eq 'R'\n \t&& $ref =~ m,^heads/,\n \t&& $old eq git_value('merge-base',$old,$new));\n \n-# Load the user's ACL file.\n+# Load the user's ACL file. Expand groups (user.memberof) one level.\n {\n \tmy %data = ('user.committer' => []);\n-\tparse_config(\\%data, \"$acl_branch:users/$this_user.acl\");\n+\tparse_config(\\%data,$acl_git,$acl_branch,\"external/$repository_name.acl\");\n+\n+\t%data = (\n+\t\t'user.committer' => $data{'user.committer'},\n+\t\t'user.memberof' => [],\n+\t);\n+\tparse_config(\\%data,$acl_git,$acl_branch,\"users/$this_user.acl\");\n+\n \t%user_committer = map {$_ => $_} @{$data{'user.committer'}};\n-\tmy $rules = $data{\"repository.$repository_name.allow\"} || [];\n+\tmy $rule_key = \"repository.$repository_name.allow\";\n+\tmy $rules = $data{$rule_key} || [];\n+\n+\tforeach my $group (@{$data{'user.memberof'}}) {\n+\t\tmy %g;\n+\t\tparse_config(\\%g,$acl_git,$acl_branch,\"groups/$group.acl\");\n+\t\tmy $group_rules = $g{$rule_key};\n+\t\tpush @$rules, @$group_rules if $group_rules;\n+\t}\n+\n+RULE:\n \tforeach (@$rules) {\n+\t\twhile (/\\${user\\.([a-z][a-zA-Z0-9]+)}/) {\n+\t\t\tmy $k = lc $1;\n+\t\t\tmy $v = $data{\"user.$k\"};\n+\t\t\tnext RULE unless defined $v;\n+\t\t\tnext RULE if @$v != 1;\n+\t\t\tnext RULE unless defined $v->[0];\n+\t\t\ts/\\${user\\.$k}/$v->[0]/g;\n+\t\t}\n+\n \t\tif (/^([CDRU ]+)\\s+for\\s+([^\\s]+)$/) {\n \t\t\tmy $ops = $1;\n \t\t\tmy $ref = $2;\n-- \n1.5.3.rc4.29.g74276\n"}]}