class Gratan::Driver

Constants

ER_NO_SUCH_TABLE

Public Class Methods

new(client, options = {}) click to toggle source
# File lib/gratan/driver.rb, line 6
def initialize(client, options = {})
  @client = client
  @options = options
end

Public Instance Methods

create_user(user, host, options = {}) click to toggle source
# File lib/gratan/driver.rb, line 64
def create_user(user, host, options = {})
  objects = options[:objects]
  grant_options = options[:options]
  granted = false

  objects.each do |object_or_regexp, object_options|
    expand_object(object_or_regexp).each do |object|
      grant(user, host, object, grant_options.merge(object_options))
      granted = true
    end
  end

  unless granted
    log(:warn, "there was no privileges to grant to #{quote_user(user, host)}", :color => :yellow)
  end
end
disable_log_bin_local() click to toggle source
# File lib/gratan/driver.rb, line 206
def disable_log_bin_local
  unless @options[:skip_disable_log_bin]
    query('SET SQL_LOG_BIN = 0')
  end
end
drop_user(user, host) click to toggle source
# File lib/gratan/driver.rb, line 81
def drop_user(user, host)
  sql = "DROP USER #{quote_user(user, host)}"
  delete(sql)
end
each_user() { |row, row| ... } click to toggle source
# File lib/gratan/driver.rb, line 11
def each_user
  query('SELECT user, host FROM mysql.user').each do |row|
    yield(row['user'], row['host'])
  end
end
expand_object(object_or_regexp) click to toggle source
# File lib/gratan/driver.rb, line 48
def expand_object(object_or_regexp)
  if object_or_regexp.kind_of?(Regexp)
    objects = show_all_tables.select {|i| i =~ object_or_regexp }
  else
    objects = [object_or_regexp]
  end

  objects.select do |object|
    object !~ @options[:ignore_object]
  end
end
flush_privileges() click to toggle source
# File lib/gratan/driver.rb, line 60
def flush_privileges
  update("FLUSH PRIVILEGES")
end
grant(user, host, object, options) click to toggle source
# File lib/gratan/driver.rb, line 86
def grant(user, host, object, options)
  privs = options.fetch(:privs)
  identified = options[:identified]
  required = options[:required]
  with_option = options[:with]

  sql = 'GRANT %s ON %s TO %s' % [
    privs.join(', '),
    quote_object(object),
    quote_user(user, host),
  ]

  sql << " IDENTIFIED BY #{quote_identifier(identified)}" if identified
  sql << " REQUIRE #{required}" if required
  sql << " WITH #{with_option}" if with_option

  begin
    update(sql)
  rescue Mysql2::Error => e
    if @options[:ignore_not_exist] and e.error_number == ER_NO_SUCH_TABLE
      log(:warn, e.message, :color => :yellow)
    else
      raise e
    end
  end
end
identify(user, host, identifier) click to toggle source
# File lib/gratan/driver.rb, line 113
def identify(user, host, identifier)
  sql = 'GRANT USAGE ON *.* TO %s IDENTIFIED BY %s' % [
    quote_user(user, host),
    quote_identifier(identifier),
  ]

  update(sql)

  if (identifier || '').empty?
    set_password(user, host, identifier)
  end
end
override_sql_mode() click to toggle source
# File lib/gratan/driver.rb, line 212
def override_sql_mode
  if @options[:override_sql_mode]
    query('SET SQL_MODE = ""')
  end
end
revoke(user, host, object, options = {}) click to toggle source
# File lib/gratan/driver.rb, line 152
def revoke(user, host, object, options = {})
  privs = options.fetch(:privs)
  with_option = options[:with]

  if with_option =~ /\bGRANT\s+OPTION\b/i
    revoke0(user, host, object, ['GRANT OPTION'])

    if privs.length == 1 and privs[0] =~ /\AUSAGE\z/i
      return
    end
  end

  revoke0(user, host, object, privs)
end
revoke0(user, host, object, privs) click to toggle source
# File lib/gratan/driver.rb, line 167
def revoke0(user, host, object, privs)
  sql = 'REVOKE %s ON %s FROM %s' % [
    privs.join(', '),
    quote_object(object),
    quote_user(user, host),
  ]

  delete(sql)
end
set_password(user, host, password, options = {}) click to toggle source
# File lib/gratan/driver.rb, line 126
def set_password(user, host, password, options = {})
  password ||= ''

  unless options[:hash]
    password = "PASSWORD('#{escape(password)}')"
  end

  sql = 'SET PASSWORD FOR %s = %s' % [
    quote_user(user, host),
    password,
  ]

  update(sql)
end
set_require(user, host, required) click to toggle source
# File lib/gratan/driver.rb, line 141
def set_require(user, host, required)
  required ||= 'NONE'

  sql = 'GRANT USAGE ON *.* TO %s REQUIRE %s' % [
    quote_user(user, host),
    required
  ]

  update(sql)
end
set_wait_timeout() click to toggle source
# File lib/gratan/driver.rb, line 218
def set_wait_timeout
  if @options[:wait_timeout]
    query("SET @@wait_timeout = #{@options[:wait_timeout]}")
  end
end
show_all_tables() click to toggle source
# File lib/gratan/driver.rb, line 40
def show_all_tables
  @all_tables ||= show_databases.map {|database|
    show_tables(database).map do |table|
      "#{database}.#{table}"
    end
  }.flatten
end
show_create_user(user, host) click to toggle source
# File lib/gratan/driver.rb, line 23
def show_create_user(user, host)
  query("SHOW CREATE USER #{quote_user(user, host)}").first.values.first
end
show_databases() click to toggle source
# File lib/gratan/driver.rb, line 27
def show_databases
  query("SHOW DATABASES").map {|i| i.values.first }
end
show_grants(user, host) { |values.first| ... } click to toggle source
# File lib/gratan/driver.rb, line 17
def show_grants(user, host)
  query("SHOW GRANTS FOR #{quote_user(user, host)}").each do |row|
    yield(row.values.first)
  end
end
show_tables(database) click to toggle source
# File lib/gratan/driver.rb, line 31
def show_tables(database)
  query("SHOW TABLES FROM `#{database}`").map {|i| i.values.first }
rescue Mysql2::Error => e
  raise e if e.error_number != 1102

  log(:debug, e.message)
  []
end
update_with_option(user, host, object, with_option) click to toggle source
# File lib/gratan/driver.rb, line 177
def update_with_option(user, host, object, with_option)
  options = []

  if with_option =~ /\bGRANT\s+OPTION\b/i
    options << 'GRANT OPTION'
  else
    revoke(user, host, object, :privs => ['GRANT OPTION'])
  end

  %w(
    MAX_QUERIES_PER_HOUR
    MAX_UPDATES_PER_HOUR
    MAX_CONNECTIONS_PER_HOUR
    MAX_USER_CONNECTIONS
  ).each do |name|
    count = 0

    if with_option =~ /\b#{name}\s+(\d+)\b/i
      count = $1
    end

    options << [name, count].join(' ')
  end

  unless options.empty?
    grant(user, host, object, :privs => ['USAGE'], :with => options.join(' '))
  end
end

Private Instance Methods

delete(sql) click to toggle source
# File lib/gratan/driver.rb, line 236
def delete(sql)
  log(:info, sql, :color => :red)
  @client.query(sql) unless @options[:dry_run]
end
escape(str) click to toggle source
# File lib/gratan/driver.rb, line 241
def escape(str)
  @client.escape(str)
end
query(sql) click to toggle source
# File lib/gratan/driver.rb, line 226
def query(sql)
  log(:debug, sql, :dry_run => false)
  @client.query(sql)
end
quote_identifier(identifier) click to toggle source
# File lib/gratan/driver.rb, line 260
def quote_identifier(identifier)
  identifier ||= ''

  unless identifier =~ /\APASSWORD\s+'.+'\z/
    identifier = "'#{escape(identifier)}'"
  end

  identifier
end
quote_object(object) click to toggle source
# File lib/gratan/driver.rb, line 249
def quote_object(object)
  if object =~ /\A(FUNCTION|PROCEDURE)\s+/i
    object_type, object = object.split(/\s+/, 2)
    object_type << ' '
  else
    object_type = ''
  end

  object_type + object.split('.', 2).map {|i| i == '*' ? i : "`#{i}`" }.join('.')
end
quote_user(user, host) click to toggle source
# File lib/gratan/driver.rb, line 245
def quote_user(user, host)
  "'%s'@'%s'" % [escape(user), escape(host)]
end
update(sql) click to toggle source
# File lib/gratan/driver.rb, line 231
def update(sql)
  log(:info, sql, :color => :green)
  @client.query(sql) unless @options[:dry_run]
end