summaryrefslogtreecommitdiffstats
path: root/plugins/puppet/provider/mysql_grant/mysql.rb
blob: ef0205376530a65e96d0e5b5c0264878c79ffd06 (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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# A grant is either global or per-db. This can be distinguished by the syntax
# of the name:
# 	user@host => global
# 	user@host/db => per-db

require 'puppet/provider/package'

MYSQL_USER_PRIVS = [ :select_priv, :insert_priv, :update_priv, :delete_priv,
	:create_priv, :drop_priv, :reload_priv, :shutdown_priv, :process_priv,
	:file_priv, :grant_priv, :references_priv, :index_priv, :alter_priv,
	:show_db_priv, :super_priv, :create_tmp_table_priv, :lock_tables_priv,
	:execute_priv, :repl_slave_priv, :repl_client_priv, :create_view_priv,
	:show_view_priv, :create_routine_priv, :alter_routine_priv,
	:create_user_priv
]

MYSQL_DB_PRIVS = [ :select_priv, :insert_priv, :update_priv, :delete_priv,
	:create_priv, :drop_priv, :grant_priv, :references_priv, :index_priv,
	:alter_priv, :create_tmp_table_priv, :lock_tables_priv, :create_view_priv,
	:show_view_priv, :create_routine_priv, :alter_routine_priv, :execute_priv
]

Puppet::Type.type(:mysql_grant).provide(:mysql) do

	desc "Uses mysql as database."

	commands :mysql => '/usr/bin/mysql'
	commands :mysqladmin => '/usr/bin/mysqladmin'

	def mysql_flush 
		mysqladmin "flush-privileges"
	end

	# this parses the
	def split_name(string)
		matches = /^([^@]*)@([^\/]*)(\/(.*))?$/.match(string).captures.compact
		case matches.length 
			when 2
				{
					:type => :user,
					:user => matches[0],
					:host => matches[1]
				}
			when 4
				{
					:type => :db,
					:user => matches[0],
					:host => matches[1],
					:db => matches[3]
				}
		end
	end

	def create_row
		unless @resource.should(:privileges).empty?
			name = split_name(@resource[:name])
			case name[:type]
			when :user
				mysql "mysql", "-e", "INSERT INTO user (host, user) VALUES ('%s', '%s')" % [
					name[:host], name[:user],
				]
			when :db
				mysql "mysql", "-e", "INSERT INTO db (host, user, db) VALUES ('%s', '%s', '%s')" % [
					name[:host], name[:user], name[:db],
				]
			end
			mysql_flush
		end
	end

	def destroy
		mysql "mysql", "-e", "REVOKE ALL ON '%s'.* FROM '%s@%s'" % [ @resource[:privileges], @resource[:db], @resource[:name], @resource[:host] ]
	end
	
	def row_exists?
		name = split_name(@resource[:name])
		fields = [:user, :host]
		if name[:type] == :db
			fields << :db
		end
		not mysql( "mysql", "-NBe", 'SELECT "1" FROM %s WHERE %s' % [ name[:type], fields.map do |f| "%s = '%s'" % [f, name[f]] end.join(' AND ')]).empty?
	end

	def all_privs_set?
		all_privs = case split_name(@resource[:name])[:type]
			when :user
				MYSQL_USER_PRIVS
			when :db
				MYSQL_DB_PRIVS
		end
		all_privs = all_privs.collect do |p| p.to_s end.sort.join("|")
		privs = privileges.collect do |p| p.to_s end.sort.join("|")

		all_privs == privs
	end

	def privileges 
		name = split_name(@resource[:name])
		privs = ""

		case name[:type]
		when :user
			privs = mysql "mysql", "-Be", 'select * from user where user="%s" and host="%s"' % [ name[:user], name[:host] ]
		when :db
			privs = mysql "mysql", "-Be", 'select * from db where user="%s" and host="%s" and db="%s"' % [ name[:user], name[:host], name[:db] ]
		end

		if privs.match(/^$/) 
			privs = [] # no result, no privs
		else
			# returns a line with field names and a line with values, each tab-separated
			privs = privs.split(/\n/).map! do |l| l.chomp.split(/\t/) end
			# transpose the lines, so we have key/value pairs
			privs = privs[0].zip(privs[1])
			privs = privs.select do |p| p[0].match(/_priv$/) and p[1] == 'Y' end
		end

		privs.collect do |p| symbolize(p[0].downcase) end
	end

	def privileges=(privs) 
		unless row_exists?
			create_row
		end

		# puts "Setting privs: ", privs.join(", ")
		name = split_name(@resource[:name])
		stmt = ''
		where = ''
		all_privs = []
		case name[:type]
		when :user
			stmt = 'update user set '
			where = ' where user="%s" and host="%s"' % [ name[:user], name[:host] ]
			all_privs = MYSQL_USER_PRIVS
		when :db
			stmt = 'update db set '
			where = ' where user="%s" and host="%s"' % [ name[:user], name[:host] ]
			all_privs = MYSQL_DB_PRIVS
		end

		if privs[0] == :all 
			privs = all_privs
		end
	
		# puts "stmt:", stmt
		set = all_privs.collect do |p| "%s = '%s'" % [p, privs.include?(p) ? 'Y' : 'N'] end.join(', ')
		# puts "set:", set
		stmt = stmt << set << where

		mysql "mysql", "-Be", stmt
		mysql_flush
	end
end