{"action":"created","comment":{"url":"https://api.github.com/repos/chef/cheffish/pulls/comments/41276834","id":41276834,"diff_hunk":"@@ -64,6 +57,439 @@ def remove_rights(*values)\n           @remove_rights << args\n         end\n       end\n+\n+      action :create do\n+        if new_resource.remove_rights && new_resource.complete\n+          Chef::Log.warn(\"'remove_rights' is redundant when 'complete' is specified: all rights not specified in a 'rights' declaration will be removed.\")\n+        end\n+        # Verify that we're not destroying all hope of ACL recovery here\n+        if new_resource.complete && (!new_resource.rights || !new_resource.rights.any? { |r| r[:permissions].include?(:all) || r[:permissions].include?(:grant) })\n+          # NOTE: if superusers exist, this should turn into a warning.\n+          raise \"'complete' specified on chef_acl resource, but no GRANT permissions were granted.  I'm sorry Dave, I can't let you remove all access to an object with no hope of recovery.\"\n+        end\n+\n+        # Find all matching paths so we can update them (resolve * and **)\n+        paths = match_paths(new_resource.path)\n+        if paths.size == 0 && !new_resource.path.split('/').any? { |p| p == '*' }\n+          raise \"Path #{path} cannot have an ACL set on it!\"\n+        end\n+\n+        # Go through the matches and update the ACLs for them\n+        paths.each do |path|\n+          create_acl(path)\n+        end\n+      end\n+\n+      private\n+\n+      action_class.class_eval do\n+        # Update the ACL if necessary.\n+        def create_acl(path)\n+          changed = false\n+          # There may not be an ACL path for some valid paths (/ and /organizations,\n+          # for example).  We want to recurse into these, but we don't want to try to\n+          # update nonexistent ACLs for them.\n+          acl = acl_path(path)\n+          if acl\n+            # It's possible to make a custom container\n+            current_json = current_acl(acl)\n+            if current_json\n+\n+              # Compare the desired and current json for the ACL, and update if different.\n+              modify = {}\n+              desired_acl(acl).each do |permission, desired_json|\n+                differences = json_differences(sort_values(current_json[permission]), sort_values(desired_json))\n+\n+                if differences.size > 0\n+                  # Verify we aren't trying to destroy grant permissions\n+                  if permission == 'grant' && desired_json['actors'] == [] && desired_json['groups'] == []\n+                    # NOTE: if superusers exist, this should turn into a warning.\n+                    raise \"chef_acl attempted to remove all actors from GRANT!  I'm sorry Dave, I can't let you remove access to an object with no hope of recovery.\"\n+                  end\n+                  modify[differences] ||= {}\n+                  modify[differences][permission] = desired_json\n+                end\n+              end\n+\n+              if modify.size > 0\n+                changed = true\n+                description = [ \"update acl #{path} at #{rest_url(path)}\" ] + modify.map do |diffs, permissions|\n+                  diffs.map { |diff| \"  #{permissions.keys.join(', ')}:#{diff}\" }\n+                end.flatten(1)\n+                converge_by description do\n+                  modify.values.each do |permissions|\n+                    permissions.each do |permission, desired_json|\n+                      rest.put(rest_url(\"#{acl}/#{permission}\"), { permission => desired_json })\n+                    end\n+                  end\n+                end\n+              end\n+            end\n+          end\n+\n+          # If we have been asked to recurse, do so.\n+          # If recurse is on_change, then we will recurse if there is no ACL, or if\n+          # the ACL has changed.\n+          if new_resource.recursive == true || (new_resource.recursive == :on_change && (!acl || changed))\n+            children, error = list(path, '*')\n+            Chef::ChefFS::Parallelizer.parallel_do(children) do |child|\n+              next if child.split('/')[-1] == 'containers'\n+              create_acl(child)\n+            end\n+            # containers mess up our descent, so we do them last\n+            Chef::ChefFS::Parallelizer.parallel_do(children) do |child|\n+              next if child.split('/')[-1] != 'containers'\n+              create_acl(child)\n+            end\n+\n+          end\n+        end\n+\n+        # Get the current ACL for the given path\n+        def current_acl(acl_path)\n+          @current_acls ||= {}\n+          if !@current_acls.has_key?(acl_path)\n+            @current_acls[acl_path] = begin\n+              rest.get(rest_url(acl_path))\n+            rescue Net::HTTPServerException => e\n+              unless e.response.code == '404' && new_resource.path.split('/').any? { |p| p == '*' }\n+                raise\n+              end\n+            end\n+          end\n+          @current_acls[acl_path]\n+        end\n+\n+        # Get the desired acl for the given acl path\n+        def desired_acl(acl_path)\n+          result = new_resource.raw_json ? new_resource.raw_json.dup : {}\n+\n+          # Calculate the JSON based on rights\n+          add_rights(acl_path, result)\n+\n+          if new_resource.complete\n+            result = Chef::ChefFS::DataHandler::AclDataHandler.new.normalize(result, nil)\n+          else\n+            # If resource is incomplete, use current json to fill any holes\n+            current_acl(acl_path).each do |permission, perm_hash|\n+              if !result[permission]\n+                result[permission] = perm_hash.dup\n+              else\n+                result[permission] = result[permission].dup\n+                perm_hash.each do |type, actors|\n+                  if !result[permission][type]\n+                    result[permission][type] = actors\n+                  else\n+                    result[permission][type] = result[permission][type].dup\n+                    result[permission][type] |= actors\n+                  end\n+                end\n+              end\n+            end\n+\n+            remove_rights_from_json(result)\n+          end\n+          result\n+        end\n+\n+        def sort_values(json)\n+          json.each do |key, value|\n+            json[key] = value.sort if value.is_a?(Array)\n+          end\n+          json\n+        end\n+\n+        def add_rights(acl_path, json)\n+          if new_resource.rights\n+            new_resource.rights.each do |rights|\n+              if rights[:permissions].delete(:all)\n+                rights[:permissions] |= current_acl(acl_path).keys\n+              end\n+\n+              Array(rights[:permissions]).each do |permission|\n+                ace = json[permission.to_s] ||= {}\n+                # WTF, no distinction between users and clients?  The Chef API doesn't\n+                # let us distinguish, so we have no choice :/  This means that:\n+                # 1. If you specify :users => 'foo', and client 'foo' exists, it will\n+                #    pick that (whether user 'foo' exists or not)\n+                # 2. If you specify :clients => 'foo', and user 'foo' exists but\n+                #    client 'foo' does not, it will pick user 'foo' and put it in the\n+                #    ACL\n+                # 3. If an existing item has user 'foo' on it and you specify :clients\n+                #    => 'foo' instead, idempotence will not notice that anything needs\n+                #    to be updated and nothing will happen.\n+                if rights[:users]\n+                  ace['actors'] ||= []\n+                  ace['actors'] |= Array(rights[:users])\n+                end\n+                if rights[:clients]\n+                  ace['actors'] ||= []\n+                  ace['actors'] |= Array(rights[:clients])\n+                end\n+                if rights[:groups]\n+                  ace['groups'] ||= []\n+                  ace['groups'] |= Array(rights[:groups])\n+                end\n+              end\n+            end\n+          end\n+        end\n+\n+        def remove_rights_from_json(json)\n+          if new_resource.remove_rights\n+            new_resource.remove_rights.each do |rights|\n+              rights[:permissions].each do |permission|\n+                if permission == :all\n+                  json.each_key do |key|\n+                    ace = json[key] = json[key.dup]\n+                    ace['actors'] = ace['actors'] - Array(rights[:users])   if rights[:users]   && ace['actors']\n+                    ace['actors'] = ace['actors'] - Array(rights[:clients]) if rights[:clients] && ace['actors']\n+                    ace['groups'] = ace['groups'] - Array(rights[:groups])  if rights[:groups]  && ace['groups']\n+                  end\n+                else\n+                  ace = json[permission.to_s] = json[permission.to_s].dup\n+                  if ace\n+                    ace['actors'] = ace['actors'] - Array(rights[:users])   if rights[:users]   && ace['actors']\n+                    ace['actors'] = ace['actors'] - Array(rights[:clients]) if rights[:clients] && ace['actors']\n+                    ace['groups'] = ace['groups'] - Array(rights[:groups])  if rights[:groups]  && ace['groups']\n+                  end\n+                end\n+              end\n+            end\n+          end\n+        end\n+\n+        def load_current_resource","path":"lib/chef/resource/chef_acl.rb","position":252,"original_position":252,"commit_id":"6b1e076e178b595632861cd7c5d2cd75cbf5b4db","original_commit_id":"6b1e076e178b595632861cd7c5d2cd75cbf5b4db","user":{"login":"jkeiser","id":654882,"avatar_url":"https://avatars.githubusercontent.com/u/654882?v=3","gravatar_id":"","url":"https://api.github.com/users/jkeiser","html_url":"https://github.com/jkeiser","followers_url":"https://api.github.com/users/jkeiser/followers","following_url":"https://api.github.com/users/jkeiser/following{/other_user}","gists_url":"https://api.github.com/users/jkeiser/gists{/gist_id}","starred_url":"https://api.github.com/users/jkeiser/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jkeiser/subscriptions","organizations_url":"https://api.github.com/users/jkeiser/orgs","repos_url":"https://api.github.com/users/jkeiser/repos","events_url":"https://api.github.com/users/jkeiser/events{/privacy}","received_events_url":"https://api.github.com/users/jkeiser/received_events","type":"User","site_admin":false},"body":"Actually, I'd like to do load_current_value at the same time as we make some other improvements to use converge_if_changed and other similar things.","created_at":"2015-10-06T15:09:59Z","updated_at":"2015-10-06T15:09:59Z","html_url":"https://github.com/chef/cheffish/pull/82#discussion_r41276834","pull_request_url":"https://api.github.com/repos/chef/cheffish/pulls/82","_links":{"self":{"href":"https://api.github.com/repos/chef/cheffish/pulls/comments/41276834"},"html":{"href":"https://github.com/chef/cheffish/pull/82#discussion_r41276834"},"pull_request":{"href":"https://api.github.com/repos/chef/cheffish/pulls/82"}}},"pull_request":{"url":"https://api.github.com/repos/chef/cheffish/pulls/82","id":46667751,"html_url":"https://github.com/chef/cheffish/pull/82","diff_url":"https://github.com/chef/cheffish/pull/82.diff","patch_url":"https://github.com/chef/cheffish/pull/82.patch","issue_url":"https://api.github.com/repos/chef/cheffish/issues/82","number":82,"state":"open","locked":false,"title":"Convert to basic 12.5 resources","user":{"login":"jkeiser","id":654882,"avatar_url":"https://avatars.githubusercontent.com/u/654882?v=3","gravatar_id":"","url":"https://api.github.com/users/jkeiser","html_url":"https://github.com/jkeiser","followers_url":"https://api.github.com/users/jkeiser/followers","following_url":"https://api.github.com/users/jkeiser/following{/other_user}","gists_url":"https://api.github.com/users/jkeiser/gists{/gist_id}","starred_url":"https://api.github.com/users/jkeiser/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jkeiser/subscriptions","organizations_url":"https://api.github.com/users/jkeiser/orgs","repos_url":"https://api.github.com/users/jkeiser/repos","events_url":"https://api.github.com/users/jkeiser/events{/privacy}","received_events_url":"https://api.github.com/users/jkeiser/received_events","type":"User","site_admin":false},"body":"1. `attribute` -> `property`\r\n2. `use_automatic_resource_name`\r\n3. `action` on resource\r\n4. No more providers\r\n\r\nThere are other things still to do to make it work really *well* in the model, but this gets the big move out of the way first.","created_at":"2015-10-02T18:39:36Z","updated_at":"2015-10-06T15:09:59Z","closed_at":null,"merged_at":null,"merge_commit_sha":"6cf9acbd6c7c8c572343af7420226815165b1fff","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/chef/cheffish/pulls/82/commits","review_comments_url":"https://api.github.com/repos/chef/cheffish/pulls/82/comments","review_comment_url":"https://api.github.com/repos/chef/cheffish/pulls/comments{/number}","comments_url":"https://api.github.com/repos/chef/cheffish/issues/82/comments","statuses_url":"https://api.github.com/repos/chef/cheffish/statuses/6b1e076e178b595632861cd7c5d2cd75cbf5b4db","head":{"label":"chef:jk/compat_resource","ref":"jk/compat_resource","sha":"6b1e076e178b595632861cd7c5d2cd75cbf5b4db","user":{"login":"chef","id":29740,"avatar_url":"https://avatars.githubusercontent.com/u/29740?v=3","gravatar_id":"","url":"https://api.github.com/users/chef","html_url":"https://github.com/chef","followers_url":"https://api.github.com/users/chef/followers","following_url":"https://api.github.com/users/chef/following{/other_user}","gists_url":"https://api.github.com/users/chef/gists{/gist_id}","starred_url":"https://api.github.com/users/chef/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/chef/subscriptions","organizations_url":"https://api.github.com/users/chef/orgs","repos_url":"https://api.github.com/users/chef/repos","events_url":"https://api.github.com/users/chef/events{/privacy}","received_events_url":"https://api.github.com/users/chef/received_events","type":"Organization","site_admin":false},"repo":{"id":14132156,"name":"cheffish","full_name":"chef/cheffish","owner":{"login":"chef","id":29740,"avatar_url":"https://avatars.githubusercontent.com/u/29740?v=3","gravatar_id":"","url":"https://api.github.com/users/chef","html_url":"https://github.com/chef","followers_url":"https://api.github.com/users/chef/followers","following_url":"https://api.github.com/users/chef/following{/other_user}","gists_url":"https://api.github.com/users/chef/gists{/gist_id}","starred_url":"https://api.github.com/users/chef/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/chef/subscriptions","organizations_url":"https://api.github.com/users/chef/orgs","repos_url":"https://api.github.com/users/chef/repos","events_url":"https://api.github.com/users/chef/events{/privacy}","received_events_url":"https://api.github.com/users/chef/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/chef/cheffish","description":"Resources and tools for testing and interacting with Chef and Chef Server.","fork":false,"url":"https://api.github.com/repos/chef/cheffish","forks_url":"https://api.github.com/repos/chef/cheffish/forks","keys_url":"https://api.github.com/repos/chef/cheffish/keys{/key_id}","collaborators_url":"https://api.github.com/repos/chef/cheffish/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/chef/cheffish/teams","hooks_url":"https://api.github.com/repos/chef/cheffish/hooks","issue_events_url":"https://api.github.com/repos/chef/cheffish/issues/events{/number}","events_url":"https://api.github.com/repos/chef/cheffish/events","assignees_url":"https://api.github.com/repos/chef/cheffish/assignees{/user}","branches_url":"https://api.github.com/repos/chef/cheffish/branches{/branch}","tags_url":"https://api.github.com/repos/chef/cheffish/tags","blobs_url":"https://api.github.com/repos/chef/cheffish/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/chef/cheffish/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/chef/cheffish/git/refs{/sha}","trees_url":"https://api.github.com/repos/chef/cheffish/git/trees{/sha}","statuses_url":"https://api.github.com/repos/chef/cheffish/statuses/{sha}","languages_url":"https://api.github.com/repos/chef/cheffish/languages","stargazers_url":"https://api.github.com/repos/chef/cheffish/stargazers","contributors_url":"https://api.github.com/repos/chef/cheffish/contributors","subscribers_url":"https://api.github.com/repos/chef/cheffish/subscribers","subscription_url":"https://api.github.com/repos/chef/cheffish/subscription","commits_url":"https://api.github.com/repos/chef/cheffish/commits{/sha}","git_commits_url":"https://api.github.com/repos/chef/cheffish/git/commits{/sha}","comments_url":"https://api.github.com/repos/chef/cheffish/comments{/number}","issue_comment_url":"https://api.github.com/repos/chef/cheffish/issues/comments{/number}","contents_url":"https://api.github.com/repos/chef/cheffish/contents/{+path}","compare_url":"https://api.github.com/repos/chef/cheffish/compare/{base}...{head}","merges_url":"https://api.github.com/repos/chef/cheffish/merges","archive_url":"https://api.github.com/repos/chef/cheffish/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/chef/cheffish/downloads","issues_url":"https://api.github.com/repos/chef/cheffish/issues{/number}","pulls_url":"https://api.github.com/repos/chef/cheffish/pulls{/number}","milestones_url":"https://api.github.com/repos/chef/cheffish/milestones{/number}","notifications_url":"https://api.github.com/repos/chef/cheffish/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/chef/cheffish/labels{/name}","releases_url":"https://api.github.com/repos/chef/cheffish/releases{/id}","created_at":"2013-11-05T04:33:22Z","updated_at":"2015-09-15T11:11:38Z","pushed_at":"2015-10-02T18:42:05Z","git_url":"git://github.com/chef/cheffish.git","ssh_url":"git@github.com:chef/cheffish.git","clone_url":"https://github.com/chef/cheffish.git","svn_url":"https://github.com/chef/cheffish","homepage":"","size":1867,"stargazers_count":24,"watchers_count":24,"language":"Ruby","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":21,"mirror_url":null,"open_issues_count":16,"forks":21,"open_issues":16,"watchers":24,"default_branch":"master"}},"base":{"label":"chef:master","ref":"master","sha":"30bdca17c38ea39fa0342de210b0a53e21bcee5b","user":{"login":"chef","id":29740,"avatar_url":"https://avatars.githubusercontent.com/u/29740?v=3","gravatar_id":"","url":"https://api.github.com/users/chef","html_url":"https://github.com/chef","followers_url":"https://api.github.com/users/chef/followers","following_url":"https://api.github.com/users/chef/following{/other_user}","gists_url":"https://api.github.com/users/chef/gists{/gist_id}","starred_url":"https://api.github.com/users/chef/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/chef/subscriptions","organizations_url":"https://api.github.com/users/chef/orgs","repos_url":"https://api.github.com/users/chef/repos","events_url":"https://api.github.com/users/chef/events{/privacy}","received_events_url":"https://api.github.com/users/chef/received_events","type":"Organization","site_admin":false},"repo":{"id":14132156,"name":"cheffish","full_name":"chef/cheffish","owner":{"login":"chef","id":29740,"avatar_url":"https://avatars.githubusercontent.com/u/29740?v=3","gravatar_id":"","url":"https://api.github.com/users/chef","html_url":"https://github.com/chef","followers_url":"https://api.github.com/users/chef/followers","following_url":"https://api.github.com/users/chef/following{/other_user}","gists_url":"https://api.github.com/users/chef/gists{/gist_id}","starred_url":"https://api.github.com/users/chef/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/chef/subscriptions","organizations_url":"https://api.github.com/users/chef/orgs","repos_url":"https://api.github.com/users/chef/repos","events_url":"https://api.github.com/users/chef/events{/privacy}","received_events_url":"https://api.github.com/users/chef/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/chef/cheffish","description":"Resources and tools for testing and interacting with Chef and Chef Server.","fork":false,"url":"https://api.github.com/repos/chef/cheffish","forks_url":"https://api.github.com/repos/chef/cheffish/forks","keys_url":"https://api.github.com/repos/chef/cheffish/keys{/key_id}","collaborators_url":"https://api.github.com/repos/chef/cheffish/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/chef/cheffish/teams","hooks_url":"https://api.github.com/repos/chef/cheffish/hooks","issue_events_url":"https://api.github.com/repos/chef/cheffish/issues/events{/number}","events_url":"https://api.github.com/repos/chef/cheffish/events","assignees_url":"https://api.github.com/repos/chef/cheffish/assignees{/user}","branches_url":"https://api.github.com/repos/chef/cheffish/branches{/branch}","tags_url":"https://api.github.com/repos/chef/cheffish/tags","blobs_url":"https://api.github.com/repos/chef/cheffish/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/chef/cheffish/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/chef/cheffish/git/refs{/sha}","trees_url":"https://api.github.com/repos/chef/cheffish/git/trees{/sha}","statuses_url":"https://api.github.com/repos/chef/cheffish/statuses/{sha}","languages_url":"https://api.github.com/repos/chef/cheffish/languages","stargazers_url":"https://api.github.com/repos/chef/cheffish/stargazers","contributors_url":"https://api.github.com/repos/chef/cheffish/contributors","subscribers_url":"https://api.github.com/repos/chef/cheffish/subscribers","subscription_url":"https://api.github.com/repos/chef/cheffish/subscription","commits_url":"https://api.github.com/repos/chef/cheffish/commits{/sha}","git_commits_url":"https://api.github.com/repos/chef/cheffish/git/commits{/sha}","comments_url":"https://api.github.com/repos/chef/cheffish/comments{/number}","issue_comment_url":"https://api.github.com/repos/chef/cheffish/issues/comments{/number}","contents_url":"https://api.github.com/repos/chef/cheffish/contents/{+path}","compare_url":"https://api.github.com/repos/chef/cheffish/compare/{base}...{head}","merges_url":"https://api.github.com/repos/chef/cheffish/merges","archive_url":"https://api.github.com/repos/chef/cheffish/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/chef/cheffish/downloads","issues_url":"https://api.github.com/repos/chef/cheffish/issues{/number}","pulls_url":"https://api.github.com/repos/chef/cheffish/pulls{/number}","milestones_url":"https://api.github.com/repos/chef/cheffish/milestones{/number}","notifications_url":"https://api.github.com/repos/chef/cheffish/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/chef/cheffish/labels{/name}","releases_url":"https://api.github.com/repos/chef/cheffish/releases{/id}","created_at":"2013-11-05T04:33:22Z","updated_at":"2015-09-15T11:11:38Z","pushed_at":"2015-10-02T18:42:05Z","git_url":"git://github.com/chef/cheffish.git","ssh_url":"git@github.com:chef/cheffish.git","clone_url":"https://github.com/chef/cheffish.git","svn_url":"https://github.com/chef/cheffish","homepage":"","size":1867,"stargazers_count":24,"watchers_count":24,"language":"Ruby","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":21,"mirror_url":null,"open_issues_count":16,"forks":21,"open_issues":16,"watchers":24,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/chef/cheffish/pulls/82"},"html":{"href":"https://github.com/chef/cheffish/pull/82"},"issue":{"href":"https://api.github.com/repos/chef/cheffish/issues/82"},"comments":{"href":"https://api.github.com/repos/chef/cheffish/issues/82/comments"},"review_comments":{"href":"https://api.github.com/repos/chef/cheffish/pulls/82/comments"},"review_comment":{"href":"https://api.github.com/repos/chef/cheffish/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/chef/cheffish/pulls/82/commits"},"statuses":{"href":"https://api.github.com/repos/chef/cheffish/statuses/6b1e076e178b595632861cd7c5d2cd75cbf5b4db"}}}}