summaryrefslogtreecommitdiff
path: root/lib/chef/scan_access_control.rb
blob: d81166c28a1607eb41872245e557ea2e45fa4818 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
#--
# Author:: Daniel DeLeo (<dan@chef.io>)
# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

class Chef
  # == ScanAccessControl
  # Reads Access Control Settings on a file and writes them out to a resource
  # (should be the current_resource), attempting to match the style used by the
  # new resource, that is, if users are specified with usernames in
  # new_resource, then the uids from stat will be looked up and usernames will
  # be added to current_resource.
  #
  # === Why?
  # FileAccessControl objects may operate on a temporary file, in which case we
  # won't know if the access control settings changed (ex: rendering a template
  # with both a change in content and ownership). For auditing purposes, we
  # need to record the current state of a file system entity.
  #--
  # Not yet sure if this is the optimal way to solve the problem. But it's
  # progress towards the end goal.
  #
  # TODO: figure out if all this works with macOS' negative uids
  # TODO: windows
  class ScanAccessControl

    attr_reader :new_resource
    attr_reader :current_resource

    def initialize(new_resource, current_resource)
      @new_resource, @current_resource = new_resource, current_resource
    end

    # Modifies @current_resource, setting the current access control state.
    def set_all!
      if ::File.exist?(new_resource.path)
        set_owner
        set_group
        set_mode
      else
        # leave the values as nil.
      end
    end

    # Set the owner attribute of +current_resource+ to whatever the current
    # state is. Attempts to match the format given in new_resource: if the
    # new_resource specifies the owner as a string, the username for the uid
    # will be looked up and owner will be set to the username, and vice versa.
    def set_owner
      @current_resource.owner(current_owner)
    end

    def current_owner
      case new_resource.owner
      when String, nil
        lookup_uid
      when Integer
        stat.uid
      else
        Chef::Log.error("The `owner` parameter of the #{@new_resource} resource is set to an invalid value (#{new_resource.owner.inspect})")
        raise ArgumentError, "cannot resolve #{new_resource.owner.inspect} to uid, owner must be a string or integer"
      end
    end

    def lookup_uid
      unless (pwent = Etc.getpwuid(stat.uid)).nil?
        pwent.name
      else
        stat.uid
      end
    rescue ArgumentError
      stat.uid
    end

    # Set the group attribute of +current_resource+ to whatever the current state is.
    def set_group
      @current_resource.group(current_group)
    end

    def current_group
      case new_resource.group
      when String, nil
        lookup_gid
      when Integer
        stat.gid
      else
        Chef::Log.error("The `group` parameter of the #{@new_resource} resource is set to an invalid value (#{new_resource.owner.inspect})")
        raise ArgumentError, "cannot resolve #{new_resource.group.inspect} to gid, group must be a string or integer"
      end
    end

    def lookup_gid
      unless (pwent = Etc.getgrgid(stat.gid)).nil?
        pwent.name
      else
        stat.gid
      end
    rescue ArgumentError
      stat.gid
    end

    def set_mode
      @current_resource.mode(current_mode)
    end

    def current_mode
      case new_resource.mode
      when String, Integer, nil
        "0#{(stat.mode & 07777).to_s(8)}"
      else
        Chef::Log.error("The `mode` parameter of the #{@new_resource} resource is set to an invalid value (#{new_resource.mode.inspect})")
        raise ArgumentError, "Invalid value #{new_resource.mode.inspect} for `mode` on resource #{@new_resource}"
      end
    end

    def stat
      @stat ||= if @new_resource.instance_of?(Chef::Resource::Link)
                  ::File.lstat(@new_resource.path)
                else
                  realpath = ::File.realpath(@new_resource.path)
                  ::File.stat(realpath)
                end
    end
  end
end