pgchief 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 72ba9fb5a28f3128a49fa7944b30154be2de8b785a24295bae9c3b4544c29c3d
4
- data.tar.gz: b049ed99e5365e1fd78d727947950aa99e3e5426d9aa76bb4fd77ffdea091d73
3
+ metadata.gz: dac74bd646caf6b54ac884174ea91654af3733a4a638b45cf7e690c501c0534a
4
+ data.tar.gz: b0be173c4090cda9a176f25434318f805f6892324e8b7a148db6ee93a09e24c5
5
5
  SHA512:
6
- metadata.gz: b8cfe8f92033c986892e551b8cdc6ff1f7847ed91010ec38ffdd73d2283000b02ae70b9669a79eb4509d5c69c2449fd8f8891d2a5d7a75ccf2372ab141f83087
7
- data.tar.gz: d8ee3dadb057da4338ab8467040f286c4de2f4383a62a162204de8a686da42734aab4ccfe7e768dc53ef94962b432c3e4be8269129f2ce10bde727bd31129318
6
+ metadata.gz: 57135dde1c56aee8a082a36f03b440470a74549bd21ae970167ca5e4c02da91ed254ecf2a670818a712cdd6ad615372cd9a75229ff3f20c61f8ade718f0bf0a0
7
+ data.tar.gz: 2a70cc3718d84daba51d552c5d7a3ae144567bdb660db57fc4f890ff13ff3b154996f0ae5cae15212e3a433d01e8f2188cb1e62064cd364c51e7c9c0705d4671
data/CHANGELOG.md CHANGED
@@ -1,6 +1,26 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project will try its best to adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
1
8
  ## [Unreleased]
2
9
 
3
- ## [0.1.0] - 2024-07-20
10
+ ### Additions
11
+
12
+ - Add `j` and `k` keys as substitutes for `↑` and `↓`.
13
+ - Allow exiting the program with the `esc` key.
14
+ - Add ability to grant access privileges for newly created users.
15
+ - Or grant privileges for existing users to database(s).
16
+
17
+ ### Fixes
18
+
19
+ - GitHub now running CI successfully.
20
+ - Newly created databases are no longer open for connection by default. `CONNECT` is revoked by default for them.
21
+ - When dropping users, loop through all the databases they have access to and revoke access before dropping them.
22
+
23
+ ## [0.1.0] - 2024-08-30
4
24
 
5
25
  - Initial release
6
26
  - Create database ✅
@@ -8,3 +28,6 @@
8
28
  - Drop database ✅
9
29
  - Drop user ✅
10
30
  - List databases ✅
31
+
32
+ [Unreleased]: https://github.com/jayroh/pgchief/compare/v0.1.0...HEAD
33
+ [0.1.0]: https://github.com/jayroh/pgchief/releases/tag/v0.1.0
data/README.md CHANGED
@@ -32,6 +32,11 @@ export DATABASE_URL=postgresql://postgres:password@postgres.local:5432
32
32
  pgchief
33
33
  ```
34
34
 
35
+ Note:
36
+
37
+ 1. Prompts accept both `↑` and `↓` arrows, as well as `j` and `k`.
38
+ 2. Pressing the `esc` key at any point amidst a prompt will exit out of the program.
39
+
35
40
  ## Development of the gem
36
41
 
37
42
  1. Clone this repo.
@@ -106,7 +111,8 @@ backup_dir = "~/.pg_backups"
106
111
  - [x] Drop database
107
112
  - [x] Drop user
108
113
  - [x] List databases
109
- - [ ] Give user permissions to use database
114
+ - [x] Give user permissions to use database
115
+ - [ ] Initialize toml file
110
116
  - [ ] Back up database
111
117
  - [ ] Restore database
112
- - [ ] Initialize toml file
118
+ - [ ] Display connection information
@@ -11,6 +11,7 @@ module Pgchief
11
11
  raise Pgchief::Errors::DatabaseExistsError if db_exists?
12
12
 
13
13
  conn.exec("CREATE DATABASE #{database}")
14
+ conn.exec("REVOKE CONNECT ON DATABASE #{database} FROM PUBLIC")
14
15
 
15
16
  "Database '#{database}' created successfully!"
16
17
  rescue PG::Error => e
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pgchief
4
+ module Command
5
+ # Class to grant database privileges
6
+ class DatabasePrivilegesGrant < Base
7
+ def call
8
+ username = params.first
9
+ databases = params.last
10
+
11
+ databases.each do |database|
12
+ conn.exec("GRANT CONNECT ON DATABASE #{database} TO #{username};")
13
+ end
14
+
15
+ "Privileges granted to #{username} on #{databases.join(", ")}"
16
+ rescue PG::Error => e
17
+ "Error: #{e.message}"
18
+ ensure
19
+ conn.close
20
+ end
21
+ end
22
+ end
23
+ end
@@ -10,7 +10,8 @@ module Pgchief
10
10
  @username = params.first
11
11
  raise Pgchief::Errors::UserExistsError unless user_exists?
12
12
 
13
- conn.exec("DROP USER #{username}")
13
+ revoke_all_privileges
14
+ drop_user
14
15
 
15
16
  "User '#{username}' dropped successfully!"
16
17
  rescue PG::Error => e
@@ -19,10 +20,35 @@ module Pgchief
19
20
  conn.close
20
21
  end
21
22
 
23
+ private
24
+
22
25
  def user_exists?
23
26
  query = "SELECT 1 FROM pg_user WHERE usename = '#{username}'"
24
27
  conn.exec(query).any?
25
28
  end
29
+
30
+ def revoke_all_privileges
31
+ databases_with_access.each do |database|
32
+ conn.exec("REVOKE ALL PRIVILEGES ON DATABASE #{database} FROM #{username};")
33
+ end
34
+ end
35
+
36
+ def drop_user
37
+ conn.exec("DROP USER #{username}")
38
+ end
39
+
40
+ def databases_with_access
41
+ @databases_with_access ||= begin
42
+ results = conn.exec <<~SQL
43
+ SELECT datname
44
+ FROM pg_database
45
+ WHERE has_database_privilege('#{username}', datname, 'CONNECT')
46
+ AND datname NOT IN ('postgres', 'template1', 'template0')
47
+ SQL
48
+
49
+ results.map { |row| row["datname"] }
50
+ end
51
+ end
26
52
  end
27
53
  end
28
54
  end
@@ -4,16 +4,45 @@ module Pgchief
4
4
  module Prompt
5
5
  # Base class for prompt classes
6
6
  class Base
7
- def self.class
8
- raise "Method not defined"
7
+ def self.call(*params)
8
+ new(*params).call
9
9
  end
10
10
 
11
- def self.klassify(scope, words)
11
+ attr_reader :params
12
+
13
+ def initialize(*params)
14
+ @params = params
15
+ end
16
+
17
+ def call
18
+ raise NotImplementedError
19
+ end
20
+
21
+ def klassify(scope, words)
12
22
  Object.const_get([
13
23
  "Pgchief", "::", scope.capitalize, "::",
14
24
  words.split.map(&:capitalize)
15
25
  ].flatten.join)
16
26
  end
27
+
28
+ def yes_or_no(question, yes: nil, no: nil) # rubocop:disable Naming/MethodParameterName
29
+ response = prompt.yes?(question)
30
+ response ? yes&.call : no&.call
31
+ end
32
+
33
+ def prompt
34
+ @prompt ||= TTY::Prompt.new.tap do |p|
35
+ p.on(:keypress) do |event|
36
+ p.trigger(:keydown) if event.value == "j"
37
+ p.trigger(:keyup) if event.value == "k"
38
+ end
39
+
40
+ p.on(:keyescape) do
41
+ p.say "\n\nExiting...bye-bye 👋\n\n"
42
+ exit
43
+ end
44
+ end
45
+ end
17
46
  end
18
47
  end
19
48
  end
@@ -4,8 +4,7 @@ module Pgchief
4
4
  module Prompt
5
5
  # Class to ask for database name, in order to create it
6
6
  class CreateDatabase < Base
7
- def self.call
8
- prompt = TTY::Prompt.new
7
+ def call
9
8
  database = prompt.ask("Database name:")
10
9
  result = Pgchief::Command::DatabaseCreate.call(database)
11
10
 
@@ -4,13 +4,17 @@ module Pgchief
4
4
  module Prompt
5
5
  # Class to prompt for user creation details
6
6
  class CreateUser < Base
7
- def self.call
8
- prompt = TTY::Prompt.new
7
+ def call
9
8
  username = prompt.ask("Username:")
10
9
  password = prompt.mask("Password:")
11
10
  result = Pgchief::Command::UserCreate.call(username, password)
12
11
 
13
12
  prompt.say result
13
+
14
+ yes_or_no(
15
+ "Give \"#{username}\" access to database(s)?",
16
+ yes: -> { Pgchief::Prompt::GrantDatabasePrivileges.call(username) }
17
+ )
14
18
  end
15
19
  end
16
20
  end
@@ -4,7 +4,7 @@ module Pgchief
4
4
  module Prompt
5
5
  # Class to manage database operations
6
6
  class DatabaseManagement < Base
7
- def self.call
7
+ def call
8
8
  prompt = TTY::Prompt.new
9
9
  result = prompt.select("Database management", ["Create database", "Drop database", "Database List"])
10
10
  scope = result == "Database List" ? "command" : "prompt"
@@ -4,8 +4,7 @@ module Pgchief
4
4
  module Prompt
5
5
  # Class to prompt for which database to drop
6
6
  class DropDatabase < Base
7
- def self.call
8
- prompt = TTY::Prompt.new
7
+ def call
9
8
  database = prompt.select("Which database needs to be dropped?", Pgchief::Database.all)
10
9
  result = Pgchief::Command::DatabaseDrop.call(database)
11
10
 
@@ -4,8 +4,7 @@ module Pgchief
4
4
  module Prompt
5
5
  # Class to prompt for which user to drop
6
6
  class DropUser < Base
7
- def self.call
8
- prompt = TTY::Prompt.new
7
+ def call
9
8
  user = prompt.select("Which user needs to be deleted?", Pgchief::User.all)
10
9
  result = Pgchief::Command::UserDrop.call(user)
11
10
 
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pgchief
4
+ module Prompt
5
+ # Class to ask for database names, in order to create it
6
+ class GrantDatabasePrivileges < Base
7
+ def call
8
+ username = params.first || select_user
9
+ databases = prompt.multi_select("Give \"#{username}\" access to database(s):", Pgchief::Database.all)
10
+ result = Pgchief::Command::DatabasePrivilegesGrant.call(username, databases)
11
+
12
+ prompt.say result
13
+ end
14
+
15
+ def select_user
16
+ prompt.select("Select user to update:", Pgchief::User.all)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -4,8 +4,7 @@ module Pgchief
4
4
  module Prompt
5
5
  # Kicks off the CLI with an initial prompt
6
6
  class Start < Base
7
- def self.call
8
- prompt = TTY::Prompt.new
7
+ def call
9
8
  result = prompt.select(
10
9
  "Welcome! How can I help?",
11
10
  [
@@ -4,9 +4,13 @@ module Pgchief
4
4
  module Prompt
5
5
  # Class to manage users
6
6
  class UserManagement < Base
7
- def self.call
8
- prompt = TTY::Prompt.new
9
- result = prompt.select("User management", ["Create user", "Drop user", "User list"])
7
+ def call
8
+ result = prompt.select("User management", [
9
+ "Create user",
10
+ "Drop user",
11
+ "User list",
12
+ "Grant database privileges"
13
+ ])
10
14
 
11
15
  scope = result == "User list" ? "command" : "prompt"
12
16
  klassify(scope, result).call
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Pgchief
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.0"
5
5
  end
data/lib/pgchief.rb CHANGED
@@ -14,11 +14,13 @@ require "pgchief/prompt/database_management"
14
14
  require "pgchief/prompt/drop_database"
15
15
  require "pgchief/prompt/drop_user"
16
16
  require "pgchief/prompt/user_management"
17
+ require "pgchief/prompt/grant_database_privileges"
17
18
 
18
19
  require "pgchief/command/base"
19
20
  require "pgchief/command/database_create"
20
21
  require "pgchief/command/database_drop"
21
22
  require "pgchief/command/database_list"
23
+ require "pgchief/command/database_privileges_grant"
22
24
  require "pgchief/command/user_create"
23
25
  require "pgchief/command/user_drop"
24
26
  require "pgchief/command/user_list"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pgchief
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joel Oliveira
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-08-30 00:00:00.000000000 Z
11
+ date: 2024-08-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pg
@@ -90,6 +90,7 @@ files:
90
90
  - lib/pgchief/command/database_create.rb
91
91
  - lib/pgchief/command/database_drop.rb
92
92
  - lib/pgchief/command/database_list.rb
93
+ - lib/pgchief/command/database_privileges_grant.rb
93
94
  - lib/pgchief/command/user_create.rb
94
95
  - lib/pgchief/command/user_drop.rb
95
96
  - lib/pgchief/command/user_list.rb
@@ -101,7 +102,7 @@ files:
101
102
  - lib/pgchief/prompt/database_management.rb
102
103
  - lib/pgchief/prompt/drop_database.rb
103
104
  - lib/pgchief/prompt/drop_user.rb
104
- - lib/pgchief/prompt/grant_database_priveleges.rb
105
+ - lib/pgchief/prompt/grant_database_privileges.rb
105
106
  - lib/pgchief/prompt/start.rb
106
107
  - lib/pgchief/prompt/user_management.rb
107
108
  - lib/pgchief/user.rb
@@ -1,15 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Pgchief
4
- module Prompt
5
- # Class to ask for database names, in order to create it
6
- class GrantDatabasePrivileges
7
- def self.call(username)
8
- databases = Pgchief::Database.all
9
- prompt = TTY::Prompt.new
10
- databases = prompt.select("Select database:", databases, multiselect: true)
11
- Pgchief::Command::GrantDatabasePrivileges.call(username, databases)
12
- end
13
- end
14
- end
15
- end