dbi-dbrc 1.1.7-x86-mingw32

Sign up to get free protection for your applications and to get access to all the features.
data/dbi-dbrc.gemspec ADDED
@@ -0,0 +1,37 @@
1
+ require 'rubygems'
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = 'dbi-dbrc'
5
+ spec.version = '1.1.7'
6
+ spec.author = 'Daniel Berger'
7
+ spec.email = 'djberg96@gmail.com'
8
+ spec.license = 'Artistic 2.0'
9
+ spec.summary = 'A simple way to avoid hard-coding passwords with DBI'
10
+ spec.homepage = 'http://www.rubyforge.org/projects/shards'
11
+ spec.files = Dir['**/*'].reject{ |f| f.include?('git') }
12
+ spec.test_files = Dir['test/test*.rb']
13
+
14
+ spec.extra_rdoc_files = ['README', 'CHANGES', 'MANIFEST']
15
+ spec.rubyforge_project = 'shards'
16
+
17
+ spec.add_dependency('sys-admin', '>= 1.5.2')
18
+ spec.add_development_dependency('test-unit')
19
+
20
+ if Config::CONFIG['host_os'] =~ /mswin|msdos|win32|mingw|cygwin/i
21
+ spec.add_dependency('win32-file', '>= 0.6.6')
22
+ spec.add_dependency('win32-dir', '>= 0.3.7')
23
+ spec.add_dependency('win32-process', '>= 0.6.2')
24
+ spec.platform = Gem::Platform::CURRENT
25
+ end
26
+
27
+ spec.description = <<-EOF
28
+ The dbi-dbrc library provides an interface for storing database
29
+ connection information, including passwords, in a locally secure
30
+ file only accessible by you, or root. This allows you to avoid
31
+ hard coding login and password information in your programs
32
+ that require such information.
33
+
34
+ This library can also be used to store login and password information
35
+ for logins on remote hosts, not just databases.
36
+ EOF
37
+ end
@@ -0,0 +1,21 @@
1
+ ###########################################################################
2
+ # test.rb
3
+ #
4
+ # This script is provided for those without TestUnit installed and/or for
5
+ # general futzing.
6
+ ###########################################################################
7
+ if File.basename(Dir.pwd) == "plain"
8
+ Dir.chdir "../.."
9
+ $LOAD_PATH.unshift Dir.pwd + "/lib"
10
+ Dir.chdir "examples/plain"
11
+ end
12
+
13
+ require "pp"
14
+ require "dbi/dbrc"
15
+ include DBI
16
+
17
+ puts "VERSION: " + DBRC::VERSION
18
+
19
+ db = DBRC.new("foo","user1",Dir.pwd)
20
+
21
+ pp db
@@ -0,0 +1,28 @@
1
+ #######################################################################
2
+ # test_xml.rb
3
+ #
4
+ # Simple test script that uses the DBRC::XML subclass.
5
+ #######################################################################
6
+ if File.basename(Dir.pwd) == "xml"
7
+ Dir.chdir "../.."
8
+ $LOAD_PATH.unshift Dir.pwd + "/lib"
9
+ Dir.chdir "examples/xml"
10
+ end
11
+
12
+ require "dbi/dbrc"
13
+ require "pp"
14
+ include DBI
15
+
16
+ puts "VERSION: " + DBRC::XML::VERSION
17
+
18
+ # Use the .dbrc file in this directory
19
+ db1 = DBRC::XML.new("foo",nil,Dir.pwd) # Get first entry found for 'foo'
20
+ db2 = DBRC::XML.new("foo","user1",Dir.pwd) # Specify user
21
+
22
+ puts "First entry found for 'foo': "
23
+ pp db1
24
+ puts "=" * 20
25
+
26
+ puts "Entry for 'foo' with user 'bar': "
27
+ pp db2
28
+ puts "=" * 20
@@ -0,0 +1,28 @@
1
+ #######################################################################
2
+ # test_xml.rb
3
+ #
4
+ # Simple test script that uses the DBRC::XML subclass.
5
+ #######################################################################
6
+ if File.basename(Dir.pwd) == "yml"
7
+ Dir.chdir "../.."
8
+ $LOAD_PATH.unshift Dir.pwd + "/lib"
9
+ Dir.chdir "examples/yml"
10
+ end
11
+
12
+ require "dbi/dbrc"
13
+ require "pp"
14
+ include DBI
15
+
16
+ puts "VERSION: " + DBRC::YML::VERSION
17
+
18
+ # Use the .dbrc file in this directory
19
+ db1 = DBRC::YML.new("foo",nil,Dir.pwd) # Get first entry found for 'foo'
20
+ db2 = DBRC::YML.new("foo","user1",Dir.pwd) # Specify user
21
+
22
+ puts "First entry found for 'foo': "
23
+ pp db1
24
+ puts "=" * 20
25
+
26
+ puts "Entry for 'foo' with user 'bar': "
27
+ pp db2
28
+ puts "=" * 20
data/lib/dbi/dbrc.rb ADDED
@@ -0,0 +1,305 @@
1
+ require 'rbconfig'
2
+ require 'rubygems'
3
+
4
+ if Config::CONFIG['host_os'] =~ /mswin|msdos|win32|mingw|cygwin/i
5
+ require 'win32/dir'
6
+ require 'win32/file'
7
+ require 'win32/process'
8
+ end
9
+
10
+ require 'sys/admin'
11
+
12
+ # The DBI module serves as a namespace only.
13
+ module DBI
14
+
15
+ # The DBRC class encapsulates a database resource config file.
16
+ class DBRC
17
+
18
+ # This error is raised if anything fails trying to read the config file.
19
+ class Error < StandardError; end
20
+
21
+ # The version of the dbi-dbrc library
22
+ VERSION = '1.1.7'
23
+
24
+ @@windows = Config::CONFIG['host_os'] =~ /mswin|msdos|win32|mingw|cygwin/i
25
+
26
+ # The database or host to be connected to.
27
+ attr_accessor :database
28
+
29
+ # The user name used for the database or host connection.
30
+ attr_accessor :user
31
+
32
+ # The password associated with the database or host.
33
+ attr_accessor :password
34
+
35
+ # The driver associated with the database. This is used to internally to
36
+ # construct the DSN.
37
+ attr_accessor :driver
38
+
39
+ # Data source name, e.g. "dbi:OCI8:your_database".
40
+ attr_accessor :dsn
41
+
42
+ # The maximum number of reconnects a program should make before giving up.
43
+ attr_accessor :maximum_reconnects
44
+
45
+ # The timeout, in seconds, for each connection attempt.
46
+ attr_accessor :timeout
47
+
48
+ # The interval, in seconds, between each connection attempt.
49
+ attr_accessor :interval
50
+
51
+ # The directory where the .dbrc file is stored.
52
+ attr_accessor :dbrc_dir
53
+
54
+ # The full path to the .dbrc file.
55
+ attr_accessor :dbrc_file
56
+
57
+ # Returns a new DBI::DBRC object. The contents of the object depend on
58
+ # the arguments passed to the constructor. If only a database name is
59
+ # passed, then the first entry found in the .dbrc file that matches that
60
+ # database is parsed. If a user name is also included, then the first
61
+ # entry that matches both the database and user name is parsed.
62
+ #
63
+ # If a directory is passed as the third argument, then DBRC will look
64
+ # in that directory, instead of the default directory, for the .dbrc
65
+ # file.
66
+ #
67
+ # If an entry cannot be found for the database, or database plus user
68
+ # combination, then a Error is raised. If the .dbrc file cannot be
69
+ # found, or is setup improperly with regards to permissions or properties
70
+ # then a DBI::DBRC::Error is raised.
71
+ #
72
+ # See the README for the rules regarding .dbrc files and permissions.
73
+ #
74
+ # Note that this library can also be used as a general password
75
+ # storage mechanism. In that case simply treat the 'database' as the
76
+ # host name, and ignore the DBI::DBRC#dsn and DBI::DBRC#driver methods.
77
+ #
78
+ # Examples:
79
+ #
80
+ # # Find the first match for 'some_database'
81
+ # DBI::DBRC.new('some_database')
82
+ #
83
+ # # Find the first match for 'foo_user@some_database'
84
+ # DBI::DBRC.new('some_database', 'foo_user')
85
+ #
86
+ # # Find the first match for 'foo_user@some_database' under /usr/local
87
+ # DBI::DBRC.new('some_database', 'foo_usr', '/usr/local')
88
+ #
89
+ def initialize(database, user=nil, dbrc_dir=nil)
90
+ if dbrc_dir.nil?
91
+ uid = Process.uid
92
+ home = ENV['HOME'] || ENV['USERPROFILE']
93
+
94
+ if home.nil?
95
+ if @@windows
96
+ home ||= Sys::Admin.get_user(uid, :localaccount => true).dir
97
+ else
98
+ home ||= Sys::Admin.get_user(uid).dir
99
+ end
100
+ end
101
+
102
+ # Default to the app data directory on Windows if no home dir found
103
+ if @@windows && home.nil?
104
+ @dbrc_file = File.join(File.basename(Dir::APPDATA), '.dbrc')
105
+ else
106
+ uid = Process.uid
107
+ @dbrc_file = File.join(Sys::Admin.get_user(uid).dir, '.dbrc')
108
+ end
109
+ else
110
+ raise Error, 'bad directory' unless File.directory?(dbrc_dir)
111
+ @dbrc_file = File.join(dbrc_dir, '.dbrc')
112
+ end
113
+
114
+ @dbrc_dir = dbrc_dir
115
+ @database = database
116
+ @user = user
117
+ encrypted = false # Win32 only
118
+
119
+ @driver = nil
120
+ @interval = nil
121
+ @timeout = nil
122
+ @maximum_reconnects = nil
123
+
124
+ check_file()
125
+
126
+ # Decrypt and re-encrypt the file if we're on MS Windows and the
127
+ # file is encrypted.
128
+ begin
129
+ if @@windows && File.encrypted?(@dbrc_file)
130
+ file_was_encrypted = true
131
+ File.decrypt(@dbrc_file)
132
+ end
133
+
134
+ parse_dbrc_config_file()
135
+ validate_data()
136
+ convert_numeric_strings()
137
+ create_dsn_string()
138
+ ensure
139
+ if @@windows && file_was_encrypted
140
+ File.encrypt(@dbrc_file)
141
+ end
142
+ end
143
+ end
144
+
145
+ # Inspection of the DBI::DBRC object. This is identical to the standard
146
+ # Ruby Object#inspect, except that the password field is filtered.
147
+ #
148
+ def inspect
149
+ str = instance_variables.map{ |iv|
150
+ if iv == '@password'
151
+ "#{iv}=[FILTERED]"
152
+ else
153
+ "#{iv}=#{instance_variable_get(iv).inspect}"
154
+ end
155
+ }.join(', ')
156
+
157
+ "#<#{self.class}:0x#{(self.object_id*2).to_s(16)} " << str << ">"
158
+ end
159
+
160
+ private
161
+
162
+ # Ensure that the user/password has been set
163
+ def validate_data
164
+ unless @user
165
+ raise Error, "no user found associated with #{@database}"
166
+ end
167
+
168
+ unless @password
169
+ raise Error, "password not defined for #{@user}@#{@database}"
170
+ end
171
+ end
172
+
173
+ # Converts strings that should be numbers into actual numbers
174
+ def convert_numeric_strings
175
+ @interval = @interval.to_i if @interval
176
+ @timeout = @timeout.to_i if @timeout
177
+ @maximum_reconnects = @maximum_reconnects.to_i if @maximum_reconnects
178
+ end
179
+
180
+ # Create the dsn string if the driver is defined
181
+ def create_dsn_string
182
+ @dsn = "dbi:#{@driver}:#{@database}" if @driver
183
+ end
184
+
185
+ # Check ownership and permissions
186
+ def check_file(file=@dbrc_file)
187
+ File.open(file){ |f|
188
+ # Permissions must be set to 600 or better on Unix systems.
189
+ # Must be hidden on Win32 systems.
190
+ if @@windows
191
+ unless File.hidden?(file)
192
+ raise Error, "The .dbrc file must be hidden"
193
+ end
194
+ else
195
+ unless (f.stat.mode & 077) == 0
196
+ raise Error, "Bad .dbrc file permissions"
197
+ end
198
+ end
199
+
200
+ # Only the owner may use it
201
+ unless f.stat.owned?
202
+ raise Error, "Not owner of .dbrc file"
203
+ end
204
+ }
205
+ end
206
+
207
+ # Parse the text out of the .dbrc file. This is the only method you
208
+ # need to redefine if writing your own config handler.
209
+ def parse_dbrc_config_file(file=@dbrc_file)
210
+ IO.foreach(file){ |line|
211
+ next if line =~ /^#/ # Ignore comments
212
+ db, user, pwd, driver, timeout, max, interval = line.split
213
+
214
+ next unless @database == db
215
+
216
+ if @user
217
+ next unless @user == user
218
+ end
219
+
220
+ @user = user
221
+ @password = pwd
222
+ @driver = driver
223
+ @timeout = timeout
224
+ @maximum_reconnects = max
225
+ @interval = interval
226
+ return
227
+ }
228
+
229
+ # If we reach here it means the database and/or user wasn't found
230
+ if @user
231
+ err = "no record found for #{@user}@#{@database}"
232
+ else
233
+ err = "no record found for #{@database}"
234
+ end
235
+
236
+ raise Error, err
237
+ end
238
+
239
+ alias_method(:db, :database)
240
+ alias_method(:db=, :database=)
241
+ alias_method(:passwd, :password)
242
+ alias_method(:passwd=, :password=)
243
+ alias_method(:max_reconn, :maximum_reconnects)
244
+ alias_method(:max_reconn=, :maximum_reconnects=)
245
+ alias_method(:time_out, :timeout)
246
+ alias_method(:time_out=, :timeout=)
247
+ alias_method(:host, :database)
248
+ end
249
+
250
+ # A subclass of DBRC designed to handle .dbrc files in XML format. The
251
+ # public methods of this class are identical to DBRC.
252
+ class XML < DBRC
253
+ require "rexml/document"
254
+ include REXML
255
+
256
+ private
257
+
258
+ def parse_dbrc_config_file(file=@dbrc_file)
259
+ doc = Document.new(File.new(file))
260
+ fields = %w/user password driver interval timeout maximum_reconnects/
261
+ doc.elements.each("/dbrc/database"){ |element|
262
+ next unless element.attributes["name"] == database
263
+ if @user
264
+ next unless element.elements["user"].text == @user
265
+ end
266
+ fields.each{ |field|
267
+ val = element.elements[field]
268
+ unless val.nil?
269
+ send("#{field}=",val.text)
270
+ end
271
+ }
272
+ return
273
+ }
274
+ # If we reach here it means the database and/or user wasn't found
275
+ raise Error, "No record found for #{@user}@#{@database}"
276
+ end
277
+ end
278
+
279
+ # A subclass of DBRC designed to handle .dbrc files in YAML format. The
280
+ # public methods of this class are identical to DBRC.
281
+ class YML < DBRC
282
+ require "yaml"
283
+
284
+ private
285
+
286
+ def parse_dbrc_config_file(file=@dbrc_file)
287
+ config = YAML.load(File.open(file))
288
+ config.each{ |hash|
289
+ hash.each{ |db,info|
290
+ next unless db == @database
291
+ next unless @user == info["user"] if @user
292
+ @user = info["user"]
293
+ @password = info["password"]
294
+ @driver = info["driver"]
295
+ @interval = info["interval"]
296
+ @timeout = info["timeout"]
297
+ @maximum_reconnects = info["max_reconn"]
298
+ return
299
+ }
300
+ }
301
+ # If we reach this point, it means the database wasn't found
302
+ raise Error, "No entry found for #{@user}@#{@database}"
303
+ end
304
+ end
305
+ end
@@ -0,0 +1,201 @@
1
+ #########################################################################
2
+ # test_dbi_dbrc.rb
3
+ #
4
+ # Test suite for the base class of DBI::DBRC. This test case should be
5
+ # run via the 'rake test' task.
6
+ #########################################################################
7
+ require 'rubygems'
8
+ gem 'test-unit'
9
+
10
+ require 'dbi/dbrc'
11
+ require 'test/unit'
12
+ include DBI
13
+
14
+ class TC_DBI_DBRC < Test::Unit::TestCase
15
+ def self.startup
16
+ @@windows = File::ALT_SEPARATOR
17
+ end
18
+
19
+ def setup
20
+ @dir = File.join(Dir.pwd, 'examples/plain')
21
+ @file = File.join(@dir, '.dbrc')
22
+ @db1 = 'foo'
23
+ @db2 = 'bar'
24
+ @user1 = 'user1'
25
+ @user2 = 'user2'
26
+ @db_bad = 'blah' # Doesn't exist
27
+ @user_bad = 'user8' # Doesn't exist
28
+
29
+ if @@windows && File.respond_to?(:set_attr)
30
+ File.set_attr(@file, File::HIDDEN)
31
+ else
32
+ File.chmod(0600, @file)
33
+ end
34
+
35
+ @dbrc = DBRC.new(@db1, nil, @dir)
36
+ end
37
+
38
+ def test_version
39
+ assert_equal('1.1.7', DBRC::VERSION)
40
+ end
41
+
42
+ def test_bad_dbrc_properties
43
+ if @@windows
44
+ File.unset_attr(@file, File::HIDDEN)
45
+ assert_raises(DBRC::Error){ DBRC.new(@db1, @user1, @dir) }
46
+ else
47
+ File.chmod(0555,@file)
48
+ assert_raises(DBRC::Error){ DBRC.new(@db1, @user1, @dir) }
49
+ end
50
+ end
51
+
52
+ def test_constructor
53
+ assert_raises(ArgumentError){ DBRC.new }
54
+ assert_nothing_raised{ DBRC.new(@db1, @user1, @dir) }
55
+ assert_nothing_raised{ DBRC.new(@db1, nil, @dir) }
56
+ end
57
+
58
+ def test_bad_database
59
+ assert_raise(DBRC::Error){ DBRC.new(@db_bad, nil, @dir) }
60
+ end
61
+
62
+ def test_bad_user
63
+ assert_raise(DBRC::Error){ DBRC.new(@db1, @user_bad, @dir) }
64
+ end
65
+
66
+ def test_bad_dir
67
+ assert_raise(DBI::DBRC::Error){ DBI::DBRC.new(@db1, @user1, '/bogusXX') }
68
+ end
69
+
70
+ def test_database
71
+ assert_respond_to(@dbrc, :database)
72
+ assert_respond_to(@dbrc, :database=)
73
+ assert_respond_to(@dbrc, :db)
74
+ assert_respond_to(@dbrc, :db=)
75
+ assert_kind_of(String, @dbrc.db)
76
+ end
77
+
78
+ def test_host_alias
79
+ assert_respond_to(@dbrc, :host)
80
+ assert_equal(true, @dbrc.method(:host) == @dbrc.method(:database))
81
+ end
82
+
83
+ def test_dbrc_dir
84
+ assert_respond_to(@dbrc, :dbrc_dir)
85
+ assert_equal(@dir, @dbrc.dbrc_dir)
86
+ end
87
+
88
+ def test_dbrc_file
89
+ assert_respond_to(@dbrc, :dbrc_file)
90
+ assert_equal('.dbrc', File.basename(@dbrc.dbrc_file))
91
+ end
92
+
93
+ def test_dsn
94
+ assert_respond_to(@dbrc, :dsn)
95
+ assert_respond_to(@dbrc, :dsn=)
96
+ end
97
+
98
+ def test_user
99
+ assert_respond_to(@dbrc, :user)
100
+ assert_respond_to(@dbrc, :user=)
101
+ assert_kind_of(String, @dbrc.user)
102
+ end
103
+
104
+ def test_password
105
+ assert_respond_to(@dbrc, :password)
106
+ assert_respond_to(@dbrc, :password=)
107
+ assert_respond_to(@dbrc, :passwd)
108
+ assert_respond_to(@dbrc, :passwd=)
109
+ assert_kind_of(String, @dbrc.password)
110
+ end
111
+
112
+ def test_driver
113
+ assert_respond_to(@dbrc, :driver)
114
+ assert_respond_to(@dbrc, :driver=)
115
+ assert_kind_of(String, @dbrc.driver)
116
+ end
117
+
118
+ def test_interval
119
+ assert_respond_to(@dbrc, :interval)
120
+ assert_respond_to(@dbrc, :interval=)
121
+ assert_kind_of(Fixnum, @dbrc.interval)
122
+ end
123
+
124
+ def test_timeout
125
+ assert_respond_to(@dbrc, :timeout)
126
+ assert_respond_to(@dbrc, :timeout=)
127
+ assert_respond_to(@dbrc, :time_out)
128
+ assert_respond_to(@dbrc, :time_out=)
129
+ assert_kind_of(Fixnum, @dbrc.timeout)
130
+ end
131
+
132
+ def test_max_reconn
133
+ assert_respond_to(@dbrc, :max_reconn)
134
+ assert_respond_to(@dbrc, :max_reconn=)
135
+ assert_respond_to(@dbrc, :maximum_reconnects)
136
+ assert_respond_to(@dbrc, :maximum_reconnects=)
137
+ assert_kind_of(Fixnum, @dbrc.maximum_reconnects)
138
+ end
139
+
140
+ def test_sample_values
141
+ assert_equal("foo", @dbrc.database)
142
+ assert_equal("user1", @dbrc.user)
143
+ assert_equal("pwd1", @dbrc.passwd)
144
+ assert_equal("Oracle", @dbrc.driver)
145
+ assert_equal(60, @dbrc.interval)
146
+ assert_equal(40, @dbrc.timeout)
147
+ assert_equal(3, @dbrc.max_reconn)
148
+ assert_equal("dbi:Oracle:foo", @dbrc.dsn)
149
+ end
150
+
151
+ # Same database, different user
152
+ def test_duplicate_database
153
+ db = DBRC.new("foo", "user2", @dir)
154
+ assert_equal("user2", db.user)
155
+ assert_equal("pwd2", db.passwd)
156
+ assert_equal("OCI8", db.driver)
157
+ assert_equal(60, db.interval)
158
+ assert_equal(60, db.timeout)
159
+ assert_equal(4, db.max_reconn)
160
+ assert_equal("dbi:OCI8:foo", db.dsn)
161
+ end
162
+
163
+ # Different database, different user
164
+ def test_different_database
165
+ db = DBRC.new("bar", "user1", @dir)
166
+ assert_equal("user1", db.user)
167
+ assert_equal("pwd3", db.passwd)
168
+ assert_equal("Oracle", db.driver)
169
+ assert_equal(30, db.interval)
170
+ assert_equal(30, db.timeout)
171
+ assert_equal(2, db.max_reconn)
172
+ assert_equal("dbi:Oracle:bar", db.dsn)
173
+ end
174
+
175
+ # A database with only a couple fields defined
176
+ def test_nil_values
177
+ db = DBRC.new("baz", "user3", @dir)
178
+ assert_equal("user3", db.user)
179
+ assert_equal("pwd4", db.passwd)
180
+ assert_nil(db.driver)
181
+ assert_nil(db.interval)
182
+ assert_nil(db.timeout)
183
+ assert_nil(db.max_reconn)
184
+ assert_nil(db.dsn)
185
+ end
186
+
187
+ def teardown
188
+ @dir = nil
189
+ @db1 = nil
190
+ @db2 = nil
191
+ @user1 = nil
192
+ @user2 = nil
193
+ @db_bad = nil
194
+ @user_bad = nil
195
+ @dbrc = nil
196
+ end
197
+
198
+ def self.shutdown
199
+ @@windows = nil
200
+ end
201
+ end