pgchief 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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