From 5d4f4a920f549a18a0f7e83e9de16d31cf6c948f Mon Sep 17 00:00:00 2001 From: Antoine Augusti Date: Mon, 21 Nov 2016 10:01:56 +0100 Subject: [PATCH 1/7] Possibility to overwrite the default password length --- lib/posgra/cli/helper.rb | 1 + lib/posgra/identifier/auto.rb | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/posgra/cli/helper.rb b/lib/posgra/cli/helper.rb index 64c197a..69a637b 100644 --- a/lib/posgra/cli/helper.rb +++ b/lib/posgra/cli/helper.rb @@ -6,6 +6,7 @@ module Posgra::CLI::Helper :exclude_role, :include_object, :exclude_object, + :password_length ] def check_fileanem(file) diff --git a/lib/posgra/identifier/auto.rb b/lib/posgra/identifier/auto.rb index c3d7daf..f473c66 100644 --- a/lib/posgra/identifier/auto.rb +++ b/lib/posgra/identifier/auto.rb @@ -5,14 +5,14 @@ def initialize(output, options = {}) end def identify(user) - password = mkpasswd + password = mkpasswd(@options[:password_length] || 8) puts_password(user, password) password end private - def mkpasswd(len = 8) + def mkpasswd(len) sources = [ (1..9).to_a, ('A'..'Z').to_a, From 213ce34f928e73a8746e85386495de651b32112e Mon Sep 17 00:00:00 2001 From: Antoine Augusti Date: Mon, 21 Nov 2016 10:29:12 +0100 Subject: [PATCH 2/7] Reuse old password if password saved in file --- lib/posgra/identifier/auto.rb | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/lib/posgra/identifier/auto.rb b/lib/posgra/identifier/auto.rb index f473c66..7d61666 100644 --- a/lib/posgra/identifier/auto.rb +++ b/lib/posgra/identifier/auto.rb @@ -1,17 +1,29 @@ +require 'csv' + class Posgra::Identifier::Auto def initialize(output, options = {}) @output = output @options = options + @accounts = {} + read_accounts end def identify(user) - password = mkpasswd(@options[:password_length] || 8) + password = @accounts.fetch(user, mkpasswd(@options[:password_length] || 8)) puts_password(user, password) password end private + def read_accounts + return unless File.file?(@output) + + CSV.foreach(@output, {encoding: "UTF-8", headers: false}) do |row| + @accounts[row[0]] = row[1] + end + end + def mkpasswd(len) sources = [ (1..9).to_a, @@ -30,6 +42,7 @@ def mkpasswd(len) end def puts_password(user, password) + @accounts[user] = password open_output do |f| f.puts("#{user},#{password}") end From 1a3ce68df1ed8d797e175f39b0cc83f0a2d92e34 Mon Sep 17 00:00:00 2001 From: Antoine Augusti Date: Mon, 21 Nov 2016 14:34:17 +0100 Subject: [PATCH 3/7] Monkey patch for groups --- lib/posgra/driver.rb | 42 ++++++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/lib/posgra/driver.rb b/lib/posgra/driver.rb index 6a46391..e85f5ee 100644 --- a/lib/posgra/driver.rb +++ b/lib/posgra/driver.rb @@ -41,7 +41,7 @@ def create_user(user) updated = false password = @identifier.identify(user) - sql = "CREATE USER #{@client.escape_identifier(user)} PASSWORD #{@client.escape_literal(password)}" + sql = "CREATE USER #{escape(user)} PASSWORD #{@client.escape_literal(password)}" log(:info, sql, :color => :cyan) unless @options[:dry_run] @@ -55,7 +55,7 @@ def create_user(user) def drop_user(user) updated = false - sql = "DROP USER #{@client.escape_identifier(user)}" + sql = "DROP USER #{escape(user)}" log(:info, sql, :color => :red) unless @options[:dry_run] @@ -69,7 +69,7 @@ def drop_user(user) def create_group(group) updated = false - sql = "CREATE GROUP #{@client.escape_identifier(group)}" + sql = "CREATE GROUP #{escape(group)}" log(:info, sql, :color => :cyan) unless @options[:dry_run] @@ -83,7 +83,7 @@ def create_group(group) def add_user_to_group(user, group) updated = false - sql = "ALTER GROUP #{@client.escape_identifier(group)} ADD USER #{@client.escape_identifier(user)}" + sql = "ALTER GROUP #{escape(group)} ADD USER #{escape(user)}" log(:info, sql, :color => :green) unless @options[:dry_run] @@ -97,7 +97,7 @@ def add_user_to_group(user, group) def drop_user_from_group(user, group) updated = false - sql = "ALTER GROUP #{@client.escape_identifier(group)} DROP USER #{@client.escape_identifier(user)}" + sql = "ALTER GROUP #{escape(group)} DROP USER #{escape(user)}" log(:info, sql, :color => :cyan) unless @options[:dry_run] @@ -111,7 +111,7 @@ def drop_user_from_group(user, group) def drop_group(group) updated = false - sql = "DROP GROUP #{@client.escape_identifier(group)}" + sql = "DROP GROUP #{escape(group)}" log(:info, sql, :color => :red) unless @options[:dry_run] @@ -135,7 +135,7 @@ def revoke_all_on_schema(role, schema) def revoke_all_on_object(role, schema, object) updated = false - sql = "REVOKE ALL ON #{@client.escape_identifier(schema)}.#{@client.escape_identifier(object)} FROM #{@client.escape_identifier(role)}" + sql = "REVOKE ALL ON #{escape(schema)}.#{escape(object)} FROM #{escape(role)}" log(:info, sql, :color => :green) unless @options[:dry_run] @@ -147,7 +147,7 @@ def revoke_all_on_object(role, schema, object) end def revoke_all_on_database(role, database) - sql = "REVOKE ALL ON DATABASE #{@client.escape_identifier(database)} FROM #{@client.escape_identifier(role)}" + sql = "REVOKE ALL ON DATABASE #{escape(database)} FROM #{escape(role)}" log(:info, sql, :color => :green) unless @options[:dry_run] @@ -161,7 +161,7 @@ def revoke_all_on_database(role, database) def grant(role, priv, options, schema, object) updated = false - sql = "GRANT #{priv} ON #{@client.escape_identifier(schema)}.#{@client.escape_identifier(object)} TO #{@client.escape_identifier(role)}" + sql = "GRANT #{priv} ON #{escape(schema)}.#{escape(object)} TO #{escape(role)}" if options['is_grantable'] sql << ' WITH GRANT OPTION' @@ -192,7 +192,7 @@ def update_grant_options(role, priv, options, schema, object) def grant_grant_option(role, priv, schema, object) updated = false - sql = "GRANT #{priv} ON #{@client.escape_identifier(schema)}.#{@client.escape_identifier(object)} TO #{@client.escape_identifier(role)} WITH GRANT OPTION" + sql = "GRANT #{priv} ON #{escape(schema)}.#{escape(object)} TO #{escape(role)} WITH GRANT OPTION" log(:info, sql, :color => :green) unless @options[:dry_run] @@ -206,7 +206,7 @@ def grant_grant_option(role, priv, schema, object) def roveke_grant_option(role, priv, schema, object) updated = false - sql = "REVOKE GRANT OPTION FOR #{priv} ON #{@client.escape_identifier(schema)}.#{@client.escape_identifier(object)} FROM #{@client.escape_identifier(role)}" + sql = "REVOKE GRANT OPTION FOR #{priv} ON #{escape(schema)}.#{escape(object)} FROM #{escape(role)}" log(:info, sql, :color => :green) unless @options[:dry_run] @@ -220,7 +220,7 @@ def roveke_grant_option(role, priv, schema, object) def revoke(role, priv, schema, object) updated = false - sql = "REVOKE #{priv} ON #{@client.escape_identifier(schema)}.#{@client.escape_identifier(object)} FROM #{@client.escape_identifier(role)}" + sql = "REVOKE #{priv} ON #{escape(schema)}.#{escape(object)} FROM #{escape(role)}" log(:info, sql, :color => :green) unless @options[:dry_run] @@ -234,7 +234,7 @@ def revoke(role, priv, schema, object) def database_grant(role, priv, options, database) updated = false - sql = "GRANT #{priv} ON DATABASE #{@client.escape_identifier(database)} TO #{@client.escape_identifier(role)}" + sql = "GRANT #{priv} ON DATABASE #{escape(database)} TO #{escape(role)}" if options['is_grantable'] sql << ' WITH GRANT OPTION' @@ -265,7 +265,7 @@ def update_database_grant_options(role, priv, options, database) def grant_database_grant_option(role, priv, database) updated = false - sql = "GRANT #{priv} ON DATABASE #{@client.escape_identifier(database)} TO #{@client.escape_identifier(role)} WITH GRANT OPTION" + sql = "GRANT #{priv} ON DATABASE #{escape(database)} TO #{escape(role)} WITH GRANT OPTION" log(:info, sql, :color => :green) unless @options[:dry_run] @@ -279,7 +279,7 @@ def grant_database_grant_option(role, priv, database) def roveke_database_grant_option(role, priv, database) updated = false - sql = "REVOKE GRANT OPTION FOR #{priv} ON DATABASE #{@client.escape_identifier(database)} FROM #{@client.escape_identifier(role)}" + sql = "REVOKE GRANT OPTION FOR #{priv} ON DATABASE #{escape(database)} FROM #{escape(role)}" log(:info, sql, :color => :green) unless @options[:dry_run] @@ -293,7 +293,7 @@ def roveke_database_grant_option(role, priv, database) def database_revoke(role, priv, database) updated = false - sql = "REVOKE #{priv} ON DATABASE #{@client.escape_identifier(database)} FROM #{@client.escape_identifier(role)}" + sql = "REVOKE #{priv} ON DATABASE #{escape(database)} FROM #{escape(role)}" log(:info, sql, :color => :green) unless @options[:dry_run] @@ -440,6 +440,16 @@ def describe_databases private + def escape(src) + @groups ||= describe_groups.keys + if @groups.include?(src) + group_name = @client.escape_identifier(src.gsub('group:', '')) + "GROUP #{group_name}" + else + @client.escape_identifier(src) + end + end + def parse_aclitems(aclitems, owner, relkind) aclitems_fmt = DEFAULT_ACL_BY_KIND.fetch(relkind, DEFAULT_ACL) aclitems ||= aclitems_fmt % [owner, owner] From b3b826e2e6b4a732b0900b838f6c86b9cc68c2ce Mon Sep 17 00:00:00 2001 From: Antoine Augusti Date: Tue, 22 Nov 2016 09:28:32 +0100 Subject: [PATCH 4/7] Whitelist password-length for role command --- lib/posgra/cli/helper.rb | 1 - lib/posgra/cli/role.rb | 1 + lib/posgra/identifier/auto.rb | 3 ++- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/posgra/cli/helper.rb b/lib/posgra/cli/helper.rb index 69a637b..64c197a 100644 --- a/lib/posgra/cli/helper.rb +++ b/lib/posgra/cli/helper.rb @@ -6,7 +6,6 @@ module Posgra::CLI::Helper :exclude_role, :include_object, :exclude_object, - :password_length ] def check_fileanem(file) diff --git a/lib/posgra/cli/role.rb b/lib/posgra/cli/role.rb index c2c37a9..7b748e2 100644 --- a/lib/posgra/cli/role.rb +++ b/lib/posgra/cli/role.rb @@ -4,6 +4,7 @@ class Posgra::CLI::Role < Thor class_option :'include-role' class_option :'exclude-role' + class_option :'password-length' desc 'apply FILE', 'Apply roles' option :'dry-run', :type => :boolean, :default => false diff --git a/lib/posgra/identifier/auto.rb b/lib/posgra/identifier/auto.rb index 7d61666..21e6626 100644 --- a/lib/posgra/identifier/auto.rb +++ b/lib/posgra/identifier/auto.rb @@ -9,7 +9,8 @@ def initialize(output, options = {}) end def identify(user) - password = @accounts.fetch(user, mkpasswd(@options[:password_length] || 8)) + password_length = [@options[:password_length].to_i, 8].max + password = @accounts.fetch(user, mkpasswd(password_length)) puts_password(user, password) password end From bb0c79d35080490edd30aaeac78d7864d1091bdc Mon Sep 17 00:00:00 2001 From: Antoine Augusti Date: Thu, 1 Dec 2016 14:48:46 +0100 Subject: [PATCH 5/7] Cast port to int --- lib/posgra/cli/app.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/posgra/cli/app.rb b/lib/posgra/cli/app.rb index 34be271..2164476 100644 --- a/lib/posgra/cli/app.rb +++ b/lib/posgra/cli/app.rb @@ -1,6 +1,6 @@ class Posgra::CLI::App < Thor class_option :host, :default => ENV['POSGRA_DB_HOST'] || 'localhost', :aliases => '-h' - class_option :port, :type => :numeric, :default => ENV['POSGRA_DB_PORT'] || 5432, :aliases => '-p' + class_option :port, :type => :numeric, :default => ENV['POSGRA_DB_PORT'].to_i || 5432, :aliases => '-p' class_option :dbname, :default => ENV['POSGRA_DB_DATABASE'] || 'postgres', :aliases => '-d' class_option :user, :default => ENV['POSGRA_DB_USER'], :aliases => '-U' class_option :password, :default => ENV['POSGRA_DB_PASSWORD'], :aliases => '-P' From 53a98f43d9aefa06fa94e03e57aa298cc446745c Mon Sep 17 00:00:00 2001 From: Antoine Augusti Date: Mon, 26 Dec 2016 17:21:20 +0100 Subject: [PATCH 6/7] Fix memoization for group commands --- lib/posgra/driver.rb | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/posgra/driver.rb b/lib/posgra/driver.rb index e85f5ee..933fdd5 100644 --- a/lib/posgra/driver.rb +++ b/lib/posgra/driver.rb @@ -69,12 +69,13 @@ def drop_user(user) def create_group(group) updated = false - sql = "CREATE GROUP #{escape(group)}" + sql = "CREATE GROUP #{escape(group, true)}" log(:info, sql, :color => :cyan) unless @options[:dry_run] exec(sql) updated = true + @groups = describe_groups.keys end updated @@ -83,7 +84,7 @@ def create_group(group) def add_user_to_group(user, group) updated = false - sql = "ALTER GROUP #{escape(group)} ADD USER #{escape(user)}" + sql = "ALTER GROUP #{escape(group, true)} ADD USER #{escape(user)}" log(:info, sql, :color => :green) unless @options[:dry_run] @@ -97,7 +98,7 @@ def add_user_to_group(user, group) def drop_user_from_group(user, group) updated = false - sql = "ALTER GROUP #{escape(group)} DROP USER #{escape(user)}" + sql = "ALTER GROUP #{escape(group, true)} DROP USER #{escape(user)}" log(:info, sql, :color => :cyan) unless @options[:dry_run] @@ -111,12 +112,13 @@ def drop_user_from_group(user, group) def drop_group(group) updated = false - sql = "DROP GROUP #{escape(group)}" + sql = "DROP GROUP #{escape(group, true)}" log(:info, sql, :color => :red) unless @options[:dry_run] exec(sql) updated = true + @groups = describe_groups.keys end updated @@ -440,9 +442,9 @@ def describe_databases private - def escape(src) + def escape(src, skip_group_command=false) @groups ||= describe_groups.keys - if @groups.include?(src) + if @groups.include?(src) && !skip_group_command group_name = @client.escape_identifier(src.gsub('group:', '')) "GROUP #{group_name}" else From ae86eb0016a7eef985a1a4dd624067a99f591e6a Mon Sep 17 00:00:00 2001 From: Antoine Augusti Date: Mon, 21 Nov 2016 10:05:14 +0100 Subject: [PATCH 7/7] Fix typos --- lib/posgra/cli/database.rb | 4 ++-- lib/posgra/cli/grant.rb | 4 ++-- lib/posgra/cli/helper.rb | 2 +- lib/posgra/cli/role.rb | 4 ++-- lib/posgra/driver.rb | 8 ++++---- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/posgra/cli/database.rb b/lib/posgra/cli/database.rb index c6dcd78..6935495 100644 --- a/lib/posgra/cli/database.rb +++ b/lib/posgra/cli/database.rb @@ -12,7 +12,7 @@ class Posgra::CLI::Database < Thor desc 'apply FILE', 'Apply database grants' option :'dry-run', :type => :boolean, :default => false def apply(file) - check_fileanem(file) + check_filename(file) updated = client.apply_databases(file) unless updated @@ -23,7 +23,7 @@ def apply(file) desc 'export [FILE]', 'Export database grants' option :split, :type => :boolean, :default => false def export(file = nil) - check_fileanem(file) + check_filename(file) dsl = client.export_databases if options[:split] diff --git a/lib/posgra/cli/grant.rb b/lib/posgra/cli/grant.rb index c7ab94e..56f699b 100644 --- a/lib/posgra/cli/grant.rb +++ b/lib/posgra/cli/grant.rb @@ -14,7 +14,7 @@ class Posgra::CLI::Grant < Thor desc 'apply FILE', 'Apply grants' option :'dry-run', :type => :boolean, :default => false def apply(file) - check_fileanem(file) + check_filename(file) updated = client.apply_grants(file) unless updated @@ -25,7 +25,7 @@ def apply(file) desc 'export [FILE]', 'Export grants' option :split, :type => :boolean, :default => false def export(file = nil) - check_fileanem(file) + check_filename(file) dsl = client.export_grants if options[:split] diff --git a/lib/posgra/cli/helper.rb b/lib/posgra/cli/helper.rb index 64c197a..9cdefd4 100644 --- a/lib/posgra/cli/helper.rb +++ b/lib/posgra/cli/helper.rb @@ -8,7 +8,7 @@ module Posgra::CLI::Helper :exclude_object, ] - def check_fileanem(file) + def check_filename(file) if file =~ /\A-.+/ raise "Invalid failname: #{file}" end diff --git a/lib/posgra/cli/role.rb b/lib/posgra/cli/role.rb index 7b748e2..ea4999c 100644 --- a/lib/posgra/cli/role.rb +++ b/lib/posgra/cli/role.rb @@ -9,7 +9,7 @@ class Posgra::CLI::Role < Thor desc 'apply FILE', 'Apply roles' option :'dry-run', :type => :boolean, :default => false def apply(file) - check_fileanem(file) + check_filename(file) updated = client.apply_roles(file) unless updated @@ -19,7 +19,7 @@ def apply(file) desc 'export [FILE]', 'Export roles' def export(file = nil) - check_fileanem(file) + check_filename(file) dsl = client.export_roles if file.nil? or file == '-' diff --git a/lib/posgra/driver.rb b/lib/posgra/driver.rb index 933fdd5..51bce2c 100644 --- a/lib/posgra/driver.rb +++ b/lib/posgra/driver.rb @@ -185,7 +185,7 @@ def update_grant_options(role, priv, options, schema, object) if options.fetch('is_grantable') updated = grant_grant_option(role, priv, schema, object) else - updated = roveke_grant_option(role, priv, schema, object) + updated = revoke_grant_option(role, priv, schema, object) end updated @@ -205,7 +205,7 @@ def grant_grant_option(role, priv, schema, object) updated end - def roveke_grant_option(role, priv, schema, object) + def revoke_grant_option(role, priv, schema, object) updated = false sql = "REVOKE GRANT OPTION FOR #{priv} ON #{escape(schema)}.#{escape(object)} FROM #{escape(role)}" @@ -258,7 +258,7 @@ def update_database_grant_options(role, priv, options, database) if options.fetch('is_grantable') updated = grant_database_grant_option(role, priv, database) else - updated = roveke_database_grant_option(role, priv, database) + updated = revoke_database_grant_option(role, priv, database) end updated @@ -278,7 +278,7 @@ def grant_database_grant_option(role, priv, database) updated end - def roveke_database_grant_option(role, priv, database) + def revoke_database_grant_option(role, priv, database) updated = false sql = "REVOKE GRANT OPTION FOR #{priv} ON DATABASE #{escape(database)} FROM #{escape(role)}"