open_directory_utils 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,127 @@
1
+ require 'net/ssh'
2
+ require "open_directory_utils/dscl"
3
+ require "open_directory_utils/pwpolicy"
4
+ require "open_directory_utils/commands_user"
5
+ require "open_directory_utils/commands_group"
6
+
7
+ module OpenDirectoryUtils
8
+ class Connection
9
+
10
+ attr_reader :srv_info, :dir_info
11
+
12
+ include OpenDirectoryUtils::Dscl
13
+ include OpenDirectoryUtils::Pwpolicy
14
+ include OpenDirectoryUtils::CommandsUser
15
+ include OpenDirectoryUtils::CommandsGroup
16
+
17
+ # configure connection with ENV_VARS (or parameters)
18
+ # @params [Hash] - reqiured info includes: srv_hostname:, srv_username: (password: if not using ssh-keys)
19
+ # @note - mostly likely needed for better security is -- dir_username:, dir_password:
20
+ def initialize(params={})
21
+ config = defaults.merge(params)
22
+
23
+ @srv_info = { hostname: config[:srv_hostname],
24
+ username: config[:srv_username],
25
+ ssh_options: config[:ssh_options]}
26
+ @dir_info = { username: config[:dir_username],
27
+ password: config[:dir_password],
28
+ data_path: config[:dir_datapath],
29
+ dscl: config[:dscl_path],
30
+ pwpol: config[:pwpol_path],
31
+ }
32
+ raise ArgumentError, 'server hostname missing' if srv_info[:hostname].nil? or
33
+ srv_info[:hostname].empty?
34
+ raise ArgumentError, 'server username missing' if srv_info[:username].nil? or
35
+ srv_info[:username].empty?
36
+ end
37
+
38
+ # after configuring a connection with .new - send commands via ssh to open directory
39
+ # @command [Symbol] - required -- to choose the action wanted
40
+ # @params [Hash] - required -- necessary information to accomplish action
41
+ # @output [String] - optional -- 'xml' or 'plist' will return responses using xml format
42
+ def run(command:, params:, output: nil)
43
+ answer = {}
44
+ params[:format] = output
45
+ # just in case clear record_name and calculate later
46
+ params[:record_name] = nil
47
+ ssh_cmds = send(command, params, dir_info)
48
+ results = send_cmds_to_od_server(ssh_cmds)
49
+ # pp ssh_cmds
50
+ # pp results
51
+ format_results(results, command, params, ssh_cmds)
52
+ rescue ArgumentError, NoMethodError => error
53
+ {error: {response: error.message, command: command,
54
+ attributes: params, dscl_cmds: ssh_cmds}}
55
+ end
56
+
57
+ private
58
+
59
+ def send_cmds_to_od_server(cmds)
60
+ cmd_array = Array( cmds )
61
+ output = []
62
+ Net::SSH.start( srv_info[:hostname], srv_info[:username],
63
+ srv_info[:ssh_options] ) do |ssh|
64
+ cmd_array.each do |one_cmd|
65
+ output << (ssh.exec!(one_cmd)).strip
66
+ end
67
+ end
68
+ return output
69
+ end
70
+
71
+ def format_results(results, command, params, ssh_cmds)
72
+ errors = true if results.to_s.include? 'Error'
73
+ errors = false unless results.to_s.include? 'Error'
74
+
75
+ if command.eql?(:user_exists?) or command.eql?(:group_exists?)
76
+ errors = false # in this case not actually an error
77
+ unless results.to_s.include?('eDSRecordNotFound')
78
+ results = [true]
79
+ else
80
+ results = [false]
81
+ end
82
+ end
83
+
84
+ if command.eql?(:user_in_group?) or command.eql?(:group_has_user?)
85
+ username = nil
86
+ username = username || params[:user_name]
87
+ username = username || params[:username]
88
+ username = username || params[:uid]
89
+ username = username.to_s.strip
90
+
91
+ raise ArgumentError, "username invalid or missing" if username.eql? '' or username.include? ' '
92
+ raise ArgumentError, "groupname invalid or missing" if results.to_s.include?('eDSRecordNotFound')
93
+
94
+ if results.to_s.include?( username )
95
+ results = [true]
96
+ else
97
+ results = [false]
98
+ end
99
+ end
100
+
101
+ ans = case errors
102
+ when false
103
+ {success:{response: results, command: command, attributes: params}}
104
+ else
105
+ {error: {response: results, command: command,
106
+ attributes: params, dscl_cmds: ssh_cmds}}
107
+ end
108
+ return ans
109
+ end
110
+
111
+ def defaults
112
+ {
113
+ srv_hostname: ENV['OD_HOSTNAME'],
114
+ srv_username: ENV['OD_USERNAME'],
115
+ ssh_options: (eval(ENV['OD_SSH_OPTIONS'].to_s) || {}),
116
+
117
+ dir_username: ENV['DIR_ADMIN_USER'],
118
+ dir_password: ENV['DIR_ADMIN_PASS'],
119
+ dir_datapath: (ENV['DIR_DATAPATH'] || '/LDAPv3/127.0.0.1/'),
120
+
121
+ dscl_path: ENV['DSCL_PATH'] || '/usr/bin/dscl',
122
+ pwpol_path: ENV['PWPOL_PATH'] || '/usr/bin/pwpolicy'
123
+ }
124
+ end
125
+
126
+ end
127
+ end
@@ -0,0 +1,53 @@
1
+ require "open_directory_utils/clean_check"
2
+
3
+ module OpenDirectoryUtils
4
+
5
+ # https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man1/dscl.1.html
6
+ # https://superuser.com/questions/592921/mac-osx-users-vs-dscl-command-to-list-user/621055?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa
7
+ module Dscl
8
+
9
+ include OpenDirectoryUtils::CleanCheck
10
+
11
+ # builds the dscl command (with complete flexibility)
12
+ # attribs [Hash] - required - :record_name (the resource to affect), :action (create, append, delete, passwd, etc), attribute: (resource attribute to change), value: (value to add to attribute)
13
+ # dir_info [Hash] - usually configured in the connection initializer and then passed to dscl to build command correctly
14
+ def dscl(attribs, dir_info)
15
+ check_critical_attribute( attribs, :record_name )
16
+ check_critical_attribute( attribs, :action )
17
+ check_critical_attribute( attribs, :scope )
18
+ tidy_attribs = tidy_attribs(attribs)
19
+ build_dscl_command( tidy_attribs, dir_info )
20
+ end
21
+
22
+ # TODO: switch to template pattern
23
+ def build_dscl_command(attribs, dir_info)
24
+ # allow :recordname to be passed-in if using dscl directly
25
+ attribs[:record_name] = attribs[:record_name] || attribs[:recordname]
26
+ # /usr/bin/dscl -u diradmin -P "BigSecret" /LDAPv3/127.0.0.1/ -append /Users/$UID_USERNAME apple-keyword "$VALUE"
27
+ # "/usr/bin/dscl -plist -u #{od_username} -P #{od_password} #{od_dsclpath} -#{command} #{resource} #{params}"
28
+ ans = "#{dir_info[:dscl]}"
29
+ unless attribs[:format].nil?
30
+ ans += ' -plist' if attribs[:format].eql? 'plist' or
31
+ attribs[:format].eql? 'xml'
32
+ end
33
+ ans += " -u #{dir_info[:username]}" unless dir_info[:username].nil? or
34
+ dir_info[:username].empty? or
35
+ attribs[:action].eql? 'auth'
36
+ ans += %Q[ -P "#{dir_info[:password]}"] unless dir_info[:password].nil? or
37
+ dir_info[:password].empty? or
38
+ attribs[:action].eql? 'auth'
39
+ ans += " #{dir_info[:data_path]}"
40
+
41
+ ans += %Q[ -#{attribs[:action]}]
42
+ ans += %Q[ #{attribs[:record_name]}] if attribs[:action].eql? 'auth'
43
+ ans += %Q[ /#{attribs[:scope]}/#{attribs[:record_name]}] unless
44
+ attribs[:action].eql? 'auth'
45
+ ans += %Q[ #{attribs[:attribute]}] unless attribs[:attribute].nil? or
46
+ attribs[:attribute].empty?
47
+ ans += %Q[ "#{attribs[:value]}"] unless attribs[:value].nil? or
48
+ attribs[:value].empty?
49
+ return ans
50
+ end
51
+
52
+ end
53
+ end
@@ -0,0 +1,48 @@
1
+ require "open_directory_utils/clean_check"
2
+
3
+ module OpenDirectoryUtils
4
+
5
+ # https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man1/dscl.1.html
6
+ # https://superuser.com/questions/592921/mac-osx-users-vs-dscl-command-to-list-user/621055?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa
7
+ module Pwpolicy
8
+
9
+ include OpenDirectoryUtils::CleanCheck
10
+
11
+ def build_pwpolicy_command(params, dir_info)
12
+ # /usr/bin/pwpolicy -a diradmin -p "BigSecret" -u username -setpolicy "isDisabled=0"
13
+ ans = "#{dir_info[:pwpol]}"
14
+ ans += " -a #{dir_info[:diradmin]}" unless dir_info[:diradmin].nil? or
15
+ dir_info[:diradmin].empty?
16
+ ans += %Q[ -p "#{dir_info[:password]}"] unless dir_info[:password].nil? or
17
+ dir_info[:password].empty?
18
+ ans += %Q[ -u #{params[:record_name]}]
19
+ ans += %Q[ -#{params[:attribute]}]
20
+ ans += %Q[ "#{params[:value]}"] unless params[:value].nil? or
21
+ params[:value].empty?
22
+ return ans
23
+ end
24
+
25
+ def pwpolicy(params, dir_info)
26
+ check_critical_attribute( params, :record_name )
27
+ cmd_params = tidy_attribs(params)
28
+
29
+ build_pwpolicy_command( cmd_params, dir_info )
30
+ end
31
+
32
+ ## PRE-BUILT commands
33
+ #####################
34
+ # /usr/bin/pwpolicy -a diradmin -p A-B1g-S3cret -u $shortname_USERNAME -setpolicy "isDisabled=0"
35
+ def user_enable_login(params, dir_info)
36
+ command = {attribute: 'enableuser'}
37
+ params = command.merge(params)
38
+ pwpolicy(params, dir_info)
39
+ end
40
+ # /usr/bin/pwpolicy -a diradmin -p A-B1g-S3cret -u $shortname_USERNAME -setpolicy "isDisabled=1"
41
+ def user_disable_login(params, dir_info)
42
+ command = {attribute: 'disableuser'}
43
+ params = command.merge(params)
44
+ pwpolicy(params, dir_info)
45
+ end
46
+
47
+ end
48
+ end
@@ -0,0 +1,188 @@
1
+ module OpenDirectoryUtils
2
+ # command pattern
3
+ # https://makandracards.com/alexander-m/43748-command-pattern
4
+ # https://stackoverflow.com/questions/43535421/command-pattern-in-ruby?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa
5
+ #
6
+ # DSCL
7
+ # https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man1/dscl.1.html
8
+ # https://superuser.com/questions/592921/mac-osx-users-vs-dscl-command-to-list-user/621055?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa
9
+ class Commands
10
+ class Error < StandardError; end
11
+
12
+ def initialize(params)
13
+ end
14
+
15
+ def execute
16
+ raise NotYetImplemented
17
+ end
18
+ end
19
+
20
+ # # get all usernames -- dscl . -list /Users
21
+ # # get all user details -- dscl . -readall /Users
22
+ # def user_exists?
23
+ # end
24
+ class UserGetInfo
25
+ # get user record -- dscl . -read /Users/<username>
26
+ # get user value -- dscl . -read /Users/<username> <key>
27
+ # search od user -- dscl . -search /Users RealName "Andrew Garrett"
28
+ # return as xml -- dscl -plist . -search /Users RealName "Andrew Garrett"
29
+ def user_get_info
30
+ end
31
+ end
32
+
33
+ # https://images.apple.com/server/docs/Command_Line.pdf
34
+ # https://serverfault.com/questions/20702/how-do-i-create-user-accounts-from-the-terminal-in-mac-os-x-10-5?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa
35
+ # https://superuser.com/questions/1154564/how-to-create-a-user-from-the-macos-command-line
36
+ # There are a few steps to create a user account from the command line. The good news is that you're using the right tool, dscl. What you're missing are the separate components that comprise a user account. You have to create these manually.
37
+ # sudo dscl . -create /Users/someuser
38
+ # sudo dscl . -create /Users/someuser UserShell /bin/bash
39
+ # sudo dscl . -create /Users/someuser RealName "Lucius Q. User"
40
+ # sudo dscl . -create /Users/someuser UniqueID "1010" #use something not already in use
41
+ # sudo dscl . -create /Users/someuser PrimaryGroupID 80
42
+ # sudo dscl . -create /Users/someuser NFSHomeDirectory /Users/soemuser
43
+ #
44
+ # You can then use passwd to change the user's password, or use:
45
+ # sudo dscl . -passwd /Users/someuser password
46
+
47
+ # You'll also have to create the user's home directory and change ownership so the user can access it. And be sure that the UniqueID is, in fact, unique.
48
+ #
49
+ # This line will add the user to the administrator's group:
50
+ # sudo dscl . -append /Groups/admin GroupMembership someuser
51
+ def user_create
52
+ end
53
+
54
+ # add 1st user -- dscl . create /Groups/ladmins GroupMembership localadmin
55
+ # add more users -- dscl . append /Groups/ladmins GroupMembership 2ndlocaladmin
56
+ def user_add_to_group
57
+ end
58
+
59
+ # /usr/bin/dscl -u diradmin -P A-B1g-S3cret /LDAPv3/127.0.0.1/ -delete /Groups/$VALUE GroupMembership $UID_USERNAME
60
+ def user_remove_from_group
61
+ end
62
+
63
+ # dscl . -delete /Users/yourUserName
64
+ # https://tutorialforlinux.com/2011/09/15/delete-users-and-groups-from-terminal/
65
+ def user_delete
66
+ end
67
+
68
+ # /usr/bin/dscl -plist -u diradmin -P #{adminpw} /LDAPv3/127.0.0.1/ -passwd /Users/#{uid} #{passwd}
69
+ def user_set_password
70
+ end
71
+
72
+ # /usr/bin/dscl /LDAPv3/127.0.0.1 auth #{uid} #{passwd}
73
+ def user_test_password
74
+ end
75
+
76
+ # /usr/bin/pwpolicy -a diradmin -p A-B1g-S3cret -u $UID_USERNAME -setpolicy "isDisabled=0"
77
+ def user_enable_login
78
+ end
79
+
80
+ # /usr/bin/pwpolicy -a diradmin -p A-B1g-S3cret -u $UID_USERNAME -setpolicy "isDisabled=1"
81
+ def user_disable_login
82
+ end
83
+
84
+ # /usr/bin/dscl -u diradmin -P A-B1g-S3cret /LDAPv3/127.0.0.1/ -create /Users/$UID_USERNAME gidnumber "$VALUE"
85
+ def user_set_groupnumber
86
+ end
87
+
88
+ # /usr/bin/dscl -u diradmin -P A-B1g-S3cret /LDAPv3/127.0.0.1/ -create /Users/$UID_USERNAME givenName "$VALUE"
89
+ def user_set_first_name
90
+ end
91
+
92
+ # /usr/bin/dscl -u diradmin -P A-B1g-S3cret /LDAPv3/127.0.0.1/ -create /Users/$UID_USERNAME sn "$VALUE"
93
+ def user_set_last_name
94
+ end
95
+
96
+ # /usr/bin/dscl -u diradmin -P A-B1g-S3cret /LDAPv3/127.0.0.1/ -create /Users/$UID_USERNAME apple-namesuffix "$VALUE"
97
+ def user_set_name_suffix
98
+ end
99
+
100
+ # /usr/bin/dscl -u diradmin -P A-B1g-S3cret /LDAPv3/127.0.0.1/ -create /Users/$UID_USERNAME mail "$VALUE"
101
+ # /usr/bin/dscl -u diradmin -P A-B1g-S3cret /LDAPv3/127.0.0.1/ -create /Users/$UID_USERNAME email "$VALUE"
102
+ # /usr/bin/dscl -u diradmin -P A-B1g-S3cret /LDAPv3/127.0.0.1/ -create /Users/$UID_USERNAME apple-user-mailattribute "$VALUE"
103
+ def user_set_email
104
+ end
105
+
106
+ # create first keyword
107
+ # /usr/bin/dscl -u diradmin -P A-B1g-S3cret /LDAPv3/127.0.0.1/ -create /Users/$UID_USERNAME apple-keyword "$VALUE"
108
+ # add a keyword
109
+ # /usr/bin/dscl -u diradmin -P A-B1g-S3cret /LDAPv3/127.0.0.1/ -append /Users/$UID_USERNAME apple-keyword "$VALUE"
110
+ def user_set_keywords
111
+ end
112
+
113
+ # /usr/bin/dscl -u diradmin -P A-B1g-S3cret /LDAPv3/127.0.0.1/ -append /Users/$UID_USERNAME apple-keyword "$VALUE"
114
+ def user_add_keywords
115
+ end
116
+
117
+ # /usr/bin/dscl -u diradmin -P A-B1g-S3cret /LDAPv3/127.0.0.1/ -create /Users/$UID_USERNAME mobile "$VALUE"
118
+ def user_set_mobile_phone
119
+ end
120
+
121
+ # /usr/bin/dscl -u diradmin -P A-B1g-S3cret /LDAPv3/127.0.0.1/ -create /Users/$UID_USERNAME telephoneNumber "$VALUE"
122
+ def user_set_work_phone
123
+ end
124
+
125
+ # /usr/bin/dscl -u diradmin -P A-B1g-S3cret /LDAPv3/127.0.0.1/ -create /Users/$UID_USERNAME homePhone "$VALUE"
126
+ def user_set_home_phone
127
+ end
128
+
129
+ # /usr/bin/dscl -u diradmin -P A-B1g-S3cret /LDAPv3/127.0.0.1/ -create /Users/$UID_USERNAME title "$VALUE"
130
+ def user_set_title
131
+ end
132
+
133
+ # /usr/bin/dscl -u diradmin -P A-B1g-S3cret /LDAPv3/127.0.0.1/ -create /Users/$UID_USERNAME homedirectory "$VALUE"
134
+ def user_set_home_directoy
135
+ end
136
+
137
+ # /usr/bin/dscl -u diradmin -P A-B1g-S3cret /LDAPv3/127.0.0.1/ -create /Users/$UID_USERNAME loginShell "$VALUE"
138
+ def user_set_shell
139
+ end
140
+
141
+ # /usr/bin/dscl -u diradmin -P A-B1g-S3cret /LDAPv3/127.0.0.1/ -create /Users/$UID_USERNAME apple-company "$VALUE"
142
+ def user_set_company
143
+ end
144
+ alias_method :las_program_info, :user_set_company
145
+
146
+ # /usr/bin/dscl -u diradmin -P A-B1g-S3cret /LDAPv3/127.0.0.1/ -create /Users/$UID_USERNAME street "$VALUE"
147
+ def user_set_street
148
+ end
149
+ alias_method :las_, :user_set_street
150
+
151
+ # /usr/bin/dscl -u diradmin -P A-B1g-S3cret /LDAPv3/127.0.0.1/ -create /Users/$UID l "$VALUE"
152
+ def user_set_city
153
+ end
154
+ alias_method :las_, :user_set_city
155
+
156
+ # /usr/bin/dscl -u diradmin -P A-B1g-S3cret /LDAPv3/127.0.0.1/ -create /Users/$UID_USERNAME st "$VALUE"
157
+ def user_set_state
158
+ end
159
+ alias_method :las_cultural_trip, :user_set_state
160
+
161
+ # /usr/bin/dscl -u diradmin -P A-B1g-S3cret /LDAPv3/127.0.0.1/ -create /Users/$UID_USERNAME postalCode "$VALUE"
162
+ def user_set_postcode
163
+ end
164
+ alias_method :las_faculty_family, :user_set_postcode
165
+
166
+ # /usr/bin/dscl -u diradmin -P A-B1g-S3cret /LDAPv3/127.0.0.1/ -create /Users/$UID_USERNAME apple-webloguri "$VALUE"
167
+ def user_set_blog
168
+ end
169
+ alias_method :las_, :user_set_blog
170
+
171
+ # /usr/bin/dscl -u diradmin -P A-B1g-S3cret /LDAPv3/127.0.0.1/ -create /Users/$UID_USERNAME apple-organizationinfo "$VALUE"
172
+ def user_organizational_info
173
+ end
174
+ alias_method :las_link_student_to_parent, :user_organizational_info
175
+
176
+ # /usr/bin/dscl -u diradmin -P A-B1g-S3cret /LDAPv3/127.0.0.1/ -create /Users/$UID_USERNAME apple-relationships "$VALUE"
177
+ def user_relationships
178
+ end
179
+ alias_method :las_link_parent_to_student, :user_relationships
180
+
181
+ # /usr/bin/dscl -u diradmin -P A-B1g-S3cret /LDAPv3/127.0.0.1/ -create /Users/$UID_USERNAME labeledURI "$VALUE"
182
+ def user_set_homepage
183
+ end
184
+ alias_method :las_enrollment_date, :user_set_homepage
185
+ alias_method :las_start_date, :user_set_homepage
186
+
187
+ end
188
+ end
@@ -0,0 +1,5 @@
1
+ module OpenDirectoryUtils
2
+ module Version
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
@@ -0,0 +1,38 @@
1
+
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "open_directory_utils/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "open_directory_utils"
8
+ spec.version = OpenDirectoryUtils::Version::VERSION
9
+ spec.authors = ["Bill Tihen", "Lee Weisbecker"]
10
+ spec.email = ["btihen@gmail.com", "leeweisbecker@gmail.com"]
11
+
12
+ spec.summary = %q{A ruby wrapper to access MacOpenDirectory management commands remotely}
13
+ spec.description = %q{Create and update users and groups on a MacOpenDirectory Server}
14
+ spec.homepage = "https://github.com/btihen/open_directory_utils"
15
+ spec.license = "MIT"
16
+
17
+ # # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
18
+ # # to allow pushing to a single host or delete this section to allow pushing to any host.
19
+ # if spec.respond_to?(:metadata)
20
+ # spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
21
+ # else
22
+ # raise "RubyGems 2.0 or newer is required to protect against " \
23
+ # "public gem pushes."
24
+ # end
25
+
26
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
27
+ f.match(%r{^(test|spec|features)/})
28
+ end
29
+ spec.bindir = "exe"
30
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
31
+ spec.require_paths = ["lib"]
32
+
33
+ spec.add_dependency "net-ssh", "~> 4.2"
34
+
35
+ spec.add_development_dependency "bundler", "~> 1.16"
36
+ spec.add_development_dependency "rake", "~> 12.3"
37
+ spec.add_development_dependency "rspec", "~> 3.7"
38
+ end