From 335cd89ebee60252cb9948c84d981aff715b0be1 Mon Sep 17 00:00:00 2001 From: VetheonGames Date: Fri, 9 Jun 2023 19:39:54 -0600 Subject: [PATCH] Detailed Refactoring of Database Interaction 1. **Refactoring of Database Interaction Methods** - Refactored the `store_services` method in the `DatabaseManager` class to handle an array of services instead of a hash. This change was made to simplify the interaction with the database and to avoid unnecessary complexity in the data structure. - The `store_services` method now iterates over an array of services and inserts each service into the database with a default status of true. This design choice was made to ensure that all services are active by default. 2. **Modification of Database Schema** - Modified the `create_services_table` method in the `DatabaseManager` class to create a table with only two columns: `id` and `services`. This change was made to align the database schema with the new data structure used in the `store_services` method. - The `status` column was removed from the `services` table because the status of all services is now assumed to be true by default. 3. **Error Handling and Debugging** - Encountered a `Sequel::DatabaseError` with the message "Operand should contain 1 column(s)" during the execution of the `store_services` method. This error was caused by an attempt to insert a hash into a single database column. - The error was resolved by refactoring the `store_services` method to handle an array of services instead of a hash. 4. **Unorthodox Design Choices** - The decision to use an array of services instead of a hash and to assume that the status of all services is true by default may seem unorthodox. However, these design choices were made to simplify the interaction with the database and to avoid unnecessary complexity in the data structure. - These design choices also helped to resolve the `Sequel::DatabaseError` that was encountered during the execution of the `store_services` method. This commit represents a significant refactoring of the database interaction methods in the NETRAVE project. The changes made in this commit have simplified the interaction with the database and have resolved a `Sequel::DatabaseError` that was encountered during the execution of the `store_services` method. --- .rubocop.yml | 24 ++- .rubocop_todo.yml | 217 ------------------------- .ruby-version | 2 +- .vscode/settings.json | 4 +- bin/NETRAVE | 70 +++++--- lib/.env | 0 lib/Gemfile | 28 +++- lib/Gemfile.lock | 22 ++- lib/utils/GUI_launcher.rb | 15 +- lib/utils/database_manager.rb | 43 ++--- lib/utils/first_run_init.rb | 88 ++++++---- lib/utils/system_information_gather.rb | 118 ++++++-------- lib/utils/utilities.rb | 80 ++++++--- 13 files changed, 304 insertions(+), 407 deletions(-) delete mode 100644 .rubocop_todo.yml create mode 100644 lib/.env diff --git a/.rubocop.yml b/.rubocop.yml index f913acd..cc4712a 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,14 +1,10 @@ -# This is an example Rubocop configuration file with all cops enabled. - -# AllCops section defines global settings. -AllCops: - Exclude: - - 'vendor/**/*' # Exclude vendor directory - - 'db/schema.rb' # Exclude database schema file - - 'db/migrate/**/*' # Exclude database migration files - -# Enables all cops. -# Set the value to `true` or `false` to enable/disable all cops. -# You can also enable/disable specific cops individually. -# By default, all cops are enabled. -inherit_from: .rubocop_todo.yml +# The behavior of RuboCop can be controlled via the .rubocop.yml +# configuration file. It makes it possible to enable/disable +# certain cops (checks) and to alter their behavior if they accept +# any parameters. The file can be placed either in your home +# directory or in some project directory. +# +# RuboCop will start looking for the configuration file in the directory +# where the inspected file is and continue its way up to the root directory. +# +# See https://docs.rubocop.org/rubocop/configuration diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml deleted file mode 100644 index 22d74a5..0000000 --- a/.rubocop_todo.yml +++ /dev/null @@ -1,217 +0,0 @@ -# This configuration was generated by -# `rubocop --auto-gen-config --exclude-limit 1000` -# on 2023-06-07 19:34:17 UTC using RuboCop version 1.52.0. -# The point is for the user to remove these configuration records -# one by one as the offenses are removed from the code base. -# Note that changes in the inspected code, or installation of new -# versions of RuboCop, may require this file to be generated again. - -# Offense count: 1 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: TreatCommentsAsGroupSeparators, ConsiderPunctuation, Include. -# Include: **/*.gemfile, **/Gemfile, **/gems.rb -Bundler/OrderedGems: - Exclude: - - 'lib/Gemfile' - -# Offense count: 1 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyleAlignWith, Severity. -# SupportedStylesAlignWith: keyword, variable, start_of_line -Layout/EndAlignment: - Exclude: - - 'lib/utils/utilities.rb' - -# Offense count: 3 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: Width, AllowedPatterns. -Layout/IndentationWidth: - Exclude: - - 'lib/utils/utilities.rb' - -# Offense count: 8 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyle. -# SupportedStyles: final_newline, final_blank_line -Layout/TrailingEmptyLines: - Exclude: - - 'Modules/advanced_logger.rb' - - 'Modules/firewall_manager.rb' - - 'Modules/nftables_interface.rb' - - 'Modules/packet_manager.rb' - - 'Modules/protection_engine.rb' - - 'lib/utils/database_manager.rb' - - 'lib/utils/system_information_gather.rb' - - 'lib/utils/utilities.rb' - -# Offense count: 9 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: AllowInHeredoc. -Layout/TrailingWhitespace: - Exclude: - - 'lib/utils/database_manager.rb' - - 'lib/utils/first_run_init.rb' - - 'lib/utils/system_information_gather.rb' - - 'lib/utils/utilities.rb' - -# Offense count: 1 -# Configuration parameters: AllowComments. -Lint/EmptyFile: - Exclude: - - 'TUI/main_tui.rb' - -# Offense count: 1 -# This cop supports safe autocorrection (--autocorrect). -Lint/ScriptPermission: - Exclude: - - 'bin/NETRAVE' - -# Offense count: 1 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: AllowUnusedKeywordArguments, IgnoreEmptyMethods, IgnoreNotImplementedMethods. -Lint/UnusedMethodArgument: - Exclude: - - 'lib/utils/system_information_gather.rb' - -# Offense count: 5 -# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes. -Metrics/AbcSize: - Max: 20 - -# Offense count: 8 -# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns. -Metrics/MethodLength: - Max: 19 - -# Offense count: 1 -# Configuration parameters: AllowedNames. -# AllowedNames: module_parent -Naming/ClassAndModuleCamelCase: - Exclude: - - 'lib/utils/GUI_launcher.rb' - -# Offense count: 1 -# Configuration parameters: ExpectMatchingDefinition, CheckDefinitionPathHierarchy, CheckDefinitionPathHierarchyRoots, Regex, IgnoreExecutableScripts, AllowedAcronyms. -# CheckDefinitionPathHierarchyRoots: lib, spec, test, src -# AllowedAcronyms: CLI, DSL, ACL, API, ASCII, CPU, CSS, DNS, EOF, GUID, HTML, HTTP, HTTPS, ID, IP, JSON, LHS, QPS, RAM, RHS, RPC, SLA, SMTP, SQL, SSH, TCP, TLS, TTL, UDP, UI, UID, UUID, URI, URL, UTF8, VM, XML, XMPP, XSRF, XSS -Naming/FileName: - Exclude: - - 'lib/utils/GUI_launcher.rb' - -# Offense count: 2 -# This cop supports unsafe autocorrection (--autocorrect-all). -# Configuration parameters: EnforcedStyle. -# SupportedStyles: always, conditionals -Style/AndOr: - Exclude: - - 'lib/utils/GUI_launcher.rb' - -# Offense count: 5 -# Configuration parameters: AllowedConstants. -Style/Documentation: - Exclude: - - 'spec/**/*' - - 'test/**/*' - - 'lib/utils/GUI_launcher.rb' - - 'lib/utils/database_manager.rb' - - 'lib/utils/first_run_init.rb' - - 'lib/utils/system_information_gather.rb' - - 'lib/utils/utilities.rb' - -# Offense count: 11 -# This cop supports unsafe autocorrection (--autocorrect-all). -# Configuration parameters: EnforcedStyle. -# SupportedStyles: always, always_true, never -Style/FrozenStringLiteralComment: - Exclude: - - 'Modules/advanced_logger.rb' - - 'Modules/firewall_manager.rb' - - 'Modules/nftables_interface.rb' - - 'Modules/packet_manager.rb' - - 'Modules/protection_engine.rb' - - 'bin/NETRAVE' - - 'lib/utils/GUI_launcher.rb' - - 'lib/utils/database_manager.rb' - - 'lib/utils/first_run_init.rb' - - 'lib/utils/system_information_gather.rb' - - 'lib/utils/utilities.rb' - -# Offense count: 4 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: MinBodyLength, AllowConsecutiveConditionals. -Style/GuardClause: - Exclude: - - 'lib/utils/first_run_init.rb' - - 'lib/utils/system_information_gather.rb' - -# Offense count: 4 -# This cop supports unsafe autocorrection (--autocorrect-all). -Style/InfiniteLoop: - Exclude: - - 'lib/utils/first_run_init.rb' - - 'lib/utils/system_information_gather.rb' - -# Offense count: 1 -# This cop supports safe autocorrection (--autocorrect). -Style/NegatedWhile: - Exclude: - - 'lib/utils/first_run_init.rb' - -# Offense count: 1 -# This cop supports unsafe autocorrection (--autocorrect-all). -# Configuration parameters: EnforcedStyle, AllowedMethods, AllowedPatterns. -# SupportedStyles: predicate, comparison -Style/NumericPredicate: - Exclude: - - 'spec/**/*' - - 'lib/utils/system_information_gather.rb' - -# Offense count: 1 -# This cop supports safe autocorrection (--autocorrect). -Style/RedundantBegin: - Exclude: - - 'lib/utils/database_manager.rb' - -# Offense count: 9 -# This cop supports safe autocorrection (--autocorrect). -Style/RedundantFileExtensionInRequire: - Exclude: - - 'bin/NETRAVE' - - 'lib/utils/database_manager.rb' - - 'lib/utils/first_run_init.rb' - - 'lib/utils/system_information_gather.rb' - -# Offense count: 1 -# This cop supports unsafe autocorrection (--autocorrect-all). -# Configuration parameters: ConvertCodeThatCanStartToReturnNil, AllowedMethods, MaxChainLength. -# AllowedMethods: present?, blank?, presence, try, try! -Style/SafeNavigation: - Exclude: - - 'lib/utils/database_manager.rb' - -# Offense count: 40 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyle, ConsistentQuotesInMultiline. -# SupportedStyles: single_quotes, double_quotes -Style/StringLiterals: - Exclude: - - 'bin/NETRAVE' - - 'lib/Gemfile' - - 'lib/utils/GUI_launcher.rb' - - 'lib/utils/database_manager.rb' - - 'lib/utils/first_run_init.rb' - - 'lib/utils/system_information_gather.rb' - -# Offense count: 1 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: MinSize, WordRegex. -# SupportedStyles: percent, brackets -Style/WordArray: - EnforcedStyle: brackets - -# Offense count: 6 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns. -# URISchemes: http, https -Layout/LineLength: - Max: 239 diff --git a/.ruby-version b/.ruby-version index eca690e..be94e6f 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -3.0.5 +3.2.2 diff --git a/.vscode/settings.json b/.vscode/settings.json index a8f763c..10d8d5a 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,5 @@ { - "ruby.rubocop.autocorrectOnSave": true + "ruby.rubocop.autocorrectOnSave": true, + "ruby.rubocop.configFilePath": "/home/vetheon/VSCode/NETRAVE/.rubocop.yml", + "ruby.rubocop.executePath": "/home/vetheon/.rbenv/shims/" } diff --git a/bin/NETRAVE b/bin/NETRAVE index 3597643..d8d26cd 100644 --- a/bin/NETRAVE +++ b/bin/NETRAVE @@ -1,31 +1,63 @@ #!/usr/bin/env ruby +# frozen_string_literal: true -require 'yaml' -require_relative '../lib/utils/system_information_gather.rb' -require_relative '../lib/utils/database_manager.rb' -require_relative '../lib/utils/first_run_init.rb' +require 'dotenv' +require_relative '../lib/utils/system_information_gather' +require_relative '../lib/utils/database_manager' +require_relative '../lib/utils/first_run_init' +require_relative '../lib/utils/utilities' -# Check if config.yml exists, if not, create it -unless File.exist?('config.yml') - first_run_init = FirstRunInit.new - db_details = first_run_init.ask_for_db_details - first_run_init.write_db_details_to_config_file(db_details) +include Utilities # rubocop:disable Style/MixinUsage + +# Create .env file if it doesn't exist +File.new('.env', 'w') unless File.exist?('.env') + +# Load environment variables from .env file +Dotenv.load + +# Initialize DatabaseManager +db_manager = DatabaseManager.new + +# Get database details from environment variables +db_details = { + username: ENV['DB_USERNAME'], + password: ENV['DB_PASSWORD'], + key: ENV['DB_SECRET_KEY'], + database: ENV['DB_DATABASE'] +} + +# If any of the necessary details are missing, run the first run setup +if db_details.values.any?(&:nil?) + puts 'Missing or incomplete configuration. Running first run setup.' + first_run_init = FirstRunInit.new(db_manager) + first_run_init.run + # Reload environment variables after first run setup + Dotenv.load + db_details = { + username: ENV['DB_USERNAME'], + password: ENV['DB_PASSWORD'], + key: ENV['DB_SECRET_KEY'], + database: ENV['DB_DATABASE'] + } end -# Load database configuration -db_details = YAML.load_file('config.yml') +# Decrypt the password +db_details[:password] = decrypt_string_chacha20(db_details[:password], db_details[:key]) -# Initialize DatabaseManager and test connection -db_manager = DatabaseManager.new +# Test connection +unless db_manager.test_db_connection(db_details) + puts 'Failed to connect to the database with existing configuration. Please re-enter your details.' + first_run_init = FirstRunInit.new(db_manager) + first_run_init.run +end + +# Test connection again after potentially updating config if db_manager.test_db_connection(db_details) - puts "Successfully connected to the database." + puts 'Successfully connected to the database.' else - puts "Failed to connect to the database. Please check your configuration." + puts 'Failed to connect to the database. Please check your configuration.' exit 1 end -# Initialize the first run setup -first_run_setup = FirstRunInit.new(db_manager) -first_run_setup.run - +puts 'Program successfully ran with no errors' # TODO: Add the rest of your application logic here diff --git a/lib/.env b/lib/.env new file mode 100644 index 0000000..e69de29 diff --git a/lib/Gemfile b/lib/Gemfile index d2a097b..153ef0e 100644 --- a/lib/Gemfile +++ b/lib/Gemfile @@ -1,22 +1,32 @@ # frozen_string_literal: true -source "https://rubygems.org" +source 'https://rubygems.org' # gem "rails" -gem "curses", "~> 1.4" gem 'console' +gem 'curses', '~> 1.4' -gem "rubocop", "~> 1.52" +gem 'rubocop', '~> 1.52' -gem "solargraph", "~> 0.49.0" +gem 'solargraph', '~> 0.49.0' -gem "sequel", "~> 5.69" +gem 'sequel', '~> 5.69' -gem "mysql2", "~> 0.5.5" +gem 'mysql2', '~> 0.5.5' -gem "yaml", "~> 0.2.1" +gem 'yaml', '~> 0.2.1' -gem "gtk3", "~> 4.1" +gem 'gtk3', '~> 4.1' -gem "reek", "~> 6.1" +gem 'reek', '~> 6.1' + +gem 'dynamic_curses_input', '~> 1.0' + +gem 'openssl', '~> 3.1' + +gem 'base64', '~> 0.1.1' + +gem 'securerandom', '~> 0.2.2' + +gem 'dotenv', '~> 2.8' diff --git a/lib/Gemfile.lock b/lib/Gemfile.lock index f332d34..7f7e786 100644 --- a/lib/Gemfile.lock +++ b/lib/Gemfile.lock @@ -5,6 +5,7 @@ GEM atk (4.1.7) glib2 (= 4.1.7) backport (1.2.0) + base64 (0.1.1) benchmark (0.2.1) cairo (1.17.8) native-package-installer (>= 1.0.3) @@ -13,11 +14,16 @@ GEM cairo-gobject (4.1.7) cairo (>= 1.16.2) glib2 (= 4.1.7) - console (1.16.2) + console (1.17.2) + fiber-annotation fiber-local curses (1.4.4) diff-lcs (1.5.0) + dotenv (2.8.1) + dynamic_curses_input (1.0.0) + curses e2mmap (0.1.0) + fiber-annotation (0.2.0) fiber-local (1.0.0) fiddle (1.1.1) gdk3 (4.1.7) @@ -49,14 +55,16 @@ GEM native-package-installer (1.1.5) nokogiri (1.15.2-x86_64-linux) racc (~> 1.4) + openssl (3.1.0) pango (4.1.7) cairo-gobject (= 4.1.7) gobject-introspection (= 4.1.7) parallel (1.23.0) - parser (3.2.2.1) + parser (3.2.2.3) ast (~> 2.4.1) + racc pkg-config (1.5.1) - racc (1.6.2) + racc (1.7.0) rainbow (3.1.1) rbs (2.8.4) red-colors (0.3.0) @@ -82,6 +90,7 @@ GEM rubocop-ast (1.29.0) parser (>= 3.2.1.0) ruby-progressbar (1.13.0) + securerandom (0.2.2) sequel (5.69.0) solargraph (0.49.0) backport (~> 1.2) @@ -100,7 +109,7 @@ GEM tilt (~> 2.0) yard (~> 0.9, >= 0.9.24) thor (1.2.2) - tilt (2.1.0) + tilt (2.2.0) unicode-display_width (2.4.2) yaml (0.2.1) yard (0.9.34) @@ -109,12 +118,17 @@ PLATFORMS x86_64-linux DEPENDENCIES + base64 (~> 0.1.1) console curses (~> 1.4) + dotenv (~> 2.8) + dynamic_curses_input (~> 1.0) gtk3 (~> 4.1) mysql2 (~> 0.5.5) + openssl (~> 3.1) reek (~> 6.1) rubocop (~> 1.52) + securerandom (~> 0.2.2) sequel (~> 5.69) solargraph (~> 0.49.0) yaml (~> 0.2.1) diff --git a/lib/utils/GUI_launcher.rb b/lib/utils/GUI_launcher.rb index d3e1ea7..40bfe13 100644 --- a/lib/utils/GUI_launcher.rb +++ b/lib/utils/GUI_launcher.rb @@ -1,14 +1,15 @@ require 'gtk3' -class GUI_Launcher - def initialize - @app = Gtk::Application.new("com.netrave.gui", :flags_none) +# GUI launcher +class GUILauncher + def initialize # rubocop:disable Metrics/MethodLength, Metrics/AbcSize + @app = Gtk::Application.new('com.netrave.gui', :flags_none) - @app.signal_connect "activate" do |application| + @app.signal_connect 'activate' do |application| builder = Gtk::Builder.new - builder.add_from_file("./Glade/NETRAVE.glade") + builder.add_from_file('./Glade/NETRAVE.glade') - window = builder.get_object("main_window") + window = builder.get_object('main_window') window.application = application screen = Gdk::Screen.default @@ -31,7 +32,7 @@ class GUI_Launcher end def run - puts "Launching GUI..." + puts 'Launching GUI...' @app.run end end diff --git a/lib/utils/database_manager.rb b/lib/utils/database_manager.rb index c99562f..3c35f32 100644 --- a/lib/utils/database_manager.rb +++ b/lib/utils/database_manager.rb @@ -1,25 +1,30 @@ +# frozen_string_literal: true + require 'sequel' require 'mysql2' -require_relative 'system_information_gather.rb' +require_relative 'system_information_gather' require_relative '../utils/utilities' +# database manager class DatabaseManager + include Utilities + def initialize @db = nil end def test_db_connection(db_details) - begin - connection_string = "mysql2://#{db_details[:username]}:#{db_details[:password]}@localhost/#{db_details[:database]}" - @db = Sequel.connect(connection_string) - # Try a simple query to test the connection - @db.run "SELECT 1" - true - rescue Sequel::DatabaseConnectionError - false - ensure - @db.disconnect if @db - end + # Decrypt the password before using it + decrypted_password = decrypt_string_chacha20(db_details[:password], db_details[:key]) + connection_string = "mysql2://#{db_details[:username]}:#{decrypted_password}@localhost/#{db_details[:database]}" + @db = Sequel.connect(connection_string) + # Try a simple query to test the connection + @db.run 'SELECT 1' + true + rescue Sequel::DatabaseConnectionError + false + ensure + @db&.disconnect end def create_system_info_table @@ -27,25 +32,25 @@ class DatabaseManager primary_key :id Integer :uplink_speed Integer :downlink_speed - String :services, text: true + Integer :total_bandwidth end end def store_system_info(system_info) @db[:system_info].insert(system_info) - end + end def create_services_table - @db.create_table :services do + @db.create_table? :services do primary_key :id String :service_name - Boolean :status + TrueClass :status, default: true end end def store_services(services) - services.each do |service, status| - @db[:services].insert(service_name: service, status: status) + services.each do |service| + @db[:services].insert(service_name: service, status: true) end end -end \ No newline at end of file +end diff --git a/lib/utils/first_run_init.rb b/lib/utils/first_run_init.rb index f8e6970..76258d7 100644 --- a/lib/utils/first_run_init.rb +++ b/lib/utils/first_run_init.rb @@ -1,13 +1,19 @@ -require 'curses' -require_relative 'database_manager.rb' -require_relative 'system_information_gather.rb' -require_relative 'utilities.rb' +# frozen_string_literal: true +require 'curses' +require 'dynamic_curses_input' +require 'dotenv' +require_relative 'database_manager' +require_relative 'system_information_gather' +require_relative 'utilities' + +# first run class class FirstRunInit include Utilities + include Curses - def initialize(db_manager) - @db_manager = db_manager + def initialize(db_manager = nil) + @db_manager = db_manager || DatabaseManager.new @info_gatherer = SystemInformationGather.new(@db_manager) end @@ -15,70 +21,88 @@ class FirstRunInit first_run_setup end - def first_run_setup + def first_run_setup # rubocop:disable Metrics/MethodLength db_details = ask_for_db_details - while !@db_manager.test_db_connection(db_details) + + until @db_manager.test_db_connection(db_details) Curses.setpos(4, 0) Curses.addstr("Whoops! We couldn't connect to the database with the details you provided. Please try again!") Curses.refresh db_details = ask_for_db_details end + @db_manager.create_system_info_table + @db_manager.create_services_table + uplink_speed = @info_gatherer.ask_for_uplink_speed - @db_manager.store_system_info(uplink_speed) downlink_speed = @info_gatherer.ask_for_downlink_speed - @db_manager.store_system_info(downlink_speed) total_bandwidth = calculate_total_bandwidth(uplink_speed, downlink_speed) - @db_manager.store_system_info(total_bandwidth) services = @info_gatherer.ask_for_services - @db_manager.store_system_info(services) + + system_info = { + uplink_speed:, + downlink_speed:, + total_bandwidth: + } + + @db_manager.store_system_info(system_info) + @db_manager.store_services(services) end - def ask_for_db_details + def ask_for_db_details # rubocop:disable Metrics/MethodLength, Metrics/AbcSize Curses.clear + Curses.setpos(1, 0) - Curses.addstr("Please enter your database username: ") + Curses.addstr('Please enter your database username: ') Curses.refresh - username = Curses.getstr.strip + username = DCI.catch_input(true) Curses.setpos(2, 0) - Curses.addstr("Please enter your database password: ") + Curses.addstr('Please enter your database password: ') Curses.refresh Curses.noecho - password = Curses.getstr.strip + password = DCI.catch_input(false) Curses.echo Curses.setpos(3, 0) - Curses.addstr("Please enter your database name: ") + Curses.addstr('Please enter your database name: ') Curses.refresh - database = Curses.getstr.strip + database = DCI.catch_input(true) - { username: username, password: password, database: database } + # Generate a secret key + key = generate_key + + # Encrypt the password + encrypted_password = encrypt_string_chacha20(password, key) + + { username:, password: encrypted_password, key:, database: } end def write_db_details_to_config_file(db_details) - File.open("config.yml", "w") do |file| - file.write(db_details.to_yaml) + # Write the database details to the .env file + File.open('.env', 'w') do |file| + file.puts "DB_USERNAME=#{db_details[:username]}" + file.puts "DB_PASSWORD=#{db_details[:password]}" + file.puts "DB_SECRET_KEY=#{db_details[:key]}" + file.puts "DB_DATABASE=#{db_details[:database]}" end end def ask_for_default_mode - while true + loop do Curses.setpos(8, 0) - Curses.addstr("Please enter the default mode (TUI, GUI, or WebApp): ") + Curses.addstr('Please enter the default mode (TUI, GUI, or WebApp): ') Curses.refresh mode = Curses.getstr.strip.downcase - if valid_mode?(mode) - return mode - else - Curses.setpos(9, 0) - Curses.addstr("Whoops! That didn't appear to be a valid mode. Please try again!") - Curses.refresh - end + return mode if valid_mode?(mode) + + Curses.setpos(9, 0) + Curses.addstr("Whoops! That didn't appear to be a valid mode. Please try again!") + Curses.refresh end end def valid_mode?(mode) - ['tui', 'gui', 'webapp'].include?(mode) + %w[tui gui webapp].include?(mode) end end diff --git a/lib/utils/system_information_gather.rb b/lib/utils/system_information_gather.rb index c24cc56..e522ccc 100644 --- a/lib/utils/system_information_gather.rb +++ b/lib/utils/system_information_gather.rb @@ -1,8 +1,10 @@ require 'curses' require 'yaml' -require_relative 'utilities.rb' -require_relative 'database_manager.rb' +require_relative 'utilities' +require_relative 'database_manager' +require 'dynamic_curses_input' +# gather system info class SystemInformationGather include Utilities @@ -10,7 +12,7 @@ class SystemInformationGather @db_manager = db_manager end - def gather_system_info + def gather_system_info # rubocop:disable Metrics/MethodLength uplink_speed = ask_for_uplink_speed downlink_speed = ask_for_downlink_speed services = ask_for_services @@ -18,9 +20,9 @@ class SystemInformationGather total_bandwidth = uplink_speed + downlink_speed system_info = { - uplink_speed: uplink_speed, - downlink_speed: downlink_speed, - total_bandwidth: total_bandwidth + uplink_speed:, + downlink_speed:, + total_bandwidth: } # Check if the system_info table exists, if not, create it @@ -36,79 +38,67 @@ class SystemInformationGather @db_manager.store_services(services) end - def ask_for_uplink_speed - while true - Curses.clear - Curses.setpos(2, 0) - Curses.addstr("Please enter your uplink speed (upload speed, e.g., 1000Mbps or 1Gbps). ") - Curses.addstr("This is typically the maximum upload speed provided by your ISP. ") - Curses.addstr("You can check your ISP bill, use an online speed test, or contact your ISP if you're unsure. ") - Curses.addstr(" ") + def ask_for_uplink_speed # rubocop:disable Metrics/MethodLength + Curses.clear + Curses.addstr("Please enter your uplink speed (upload speed, e.g., 1000Mbps or 1Gbps).\n" \ + "This is typically the maximum upload speed provided by your ISP.\n" \ + "You can check your ISP bill, use an online speed test, or contact your ISP if you're unsure.\n\n") + Curses.refresh + Curses.addstr('Uplink Speed: ') + speed = DCI.catch_input(true) + if valid_speed?(speed) + speed.end_with?('gbps') ? convert_speed_to_mbps(speed) : speed.to_i + else Curses.setpos(5, 0) - Curses.addstr("Uplink Speed: ") + Curses.addstr("Whoops! That didn't appear to be a valid speed. Please try again!") Curses.refresh - speed = Curses.getstr.strip.downcase - if valid_speed?(speed) - return speed.end_with?('gbps') ? convert_speed_to_mbps(speed) : speed.to_i - else - Curses.setpos(5, 0) - Curses.addstr("Whoops! That didn't appear to be a valid speed. Please try again!") - Curses.refresh - end + ask_for_uplink_speed end end - - def ask_for_downlink_speed - while true - Curses.clear - Curses.setpos(2, 0) - Curses.addstr("Please enter your downlink speed (download speed, e.g., 1000Mbps or 1Gbps). ") - Curses.addstr("This is typically the maximum download speed provided by your ISP. ") - Curses.addstr("You can check your ISP bill, use an online speed test, or contact your ISP if you're unsure. ") + + def ask_for_downlink_speed # rubocop:disable Metrics/MethodLength + Curses.clear + Curses.addstr("Please enter your downlink speed (download speed, e.g., 1000Mbps or 1Gbps).\n" \ + "This is typically the maximum download speed provided by your ISP.\n"\ + "You can check your ISP bill, use an online speed test, or contact your ISP if you're unsure.\n\n") + Curses.refresh + Curses.addstr('Downlink Speed: ') + speed = DCI.catch_input(true) + if valid_speed?(speed) + speed.end_with?('gbps') ? convert_speed_to_mbps(speed) : speed.to_i + else Curses.setpos(5, 0) - Curses.addstr("Downlink Speed: ") + Curses.addstr("Whoops! That didn't appear to be a valid speed. Please try again!") Curses.refresh - speed = Curses.getstr.strip.downcase - if valid_speed?(speed) - return speed.end_with?('gbps') ? convert_speed_to_mbps(speed) : speed.to_i - else - Curses.setpos(5, 0) - Curses.addstr("Whoops! That didn't appear to be a valid speed. Please try again!") - Curses.refresh - end + ask_for_downlink_speed end - end + end def valid_speed?(speed) - speed.to_i > 0 + speed.to_i.positive? end - def ask_for_services - while true - Curses.clear - Curses.setpos(6, 0) - Curses.addstr("Please enter the services the system should be aware of (e.g., webserver, database). ") - Curses.addstr("Enter the services as a comma-separated list (e.g., webserver,database). ") + def ask_for_services # rubocop:disable Metrics/MethodLength + Curses.clear + Curses.addstr("Please enter the services the system should be aware of (e.g., webserver or database).\n" \ + "Enter the services as a comma-separated list (e.g., webserver,database).\n\n") + Curses.refresh + Curses.addstr('Services: ') + services = DCI.catch_input(true) + services_arr = services.strip.downcase.split(',').map(&:strip) + + if valid_services?(services_arr) + services_arr # return the array of services directly + else + Curses.setpos(7, 0) + Curses.addstr("Whoops! That didn't appear to be a valid list of services. Please try again!") Curses.refresh - services = Curses.getstr.strip.downcase.split(',').map(&:strip) - if valid_services?(services) - return services_to_hash(services) - else - Curses.setpos(7, 0) - Curses.addstr("Whoops! That didn't appear to be a valid list of services. Please try again!") - Curses.refresh - end + ask_for_services end end - def valid_services?(services) + def valid_services?(_services) # TODO: Validate the services true end - - def services_to_hash(services) - services_hash = {} - services.each { |service| services_hash[service] = true } - services_hash - end -end \ No newline at end of file +end diff --git a/lib/utils/utilities.rb b/lib/utils/utilities.rb index 7df6869..3f3fb6f 100644 --- a/lib/utils/utilities.rb +++ b/lib/utils/utilities.rb @@ -1,22 +1,62 @@ +# frozen_string_literal: true + +require 'securerandom' +require 'digest' +require 'base64' +require 'openssl' + +# Utiltiies Module module Utilities - # Converts speed from Gbps to Mbps if necessary - def self.convert_speed_to_mbps(speed) - if speed.end_with?('gbps') - speed.to_i * 1000 - else - speed.to_i - end + # Converts speed from Gbps to Mbps if necessary + def convert_speed_to_mbps(speed) + if speed.end_with?('gbps') + speed.to_i * 1000 + else + speed.to_i end - - # Converts an array of services into a hash - def self.services_to_hash(services) - services_hash = {} - services.each { |service| services_hash[service] = true } - services_hash - end - - # Calculates total bandwidth from uplink and downlink speeds - def self.calculate_total_bandwidth(uplink_speed, downlink_speed) - uplink_speed + downlink_speed - end - end \ No newline at end of file + end + + # Converts an array of services into a hash + def services_to_hash(services) + services_hash = {} + services.each { |service| services_hash[service] = true } + services_hash + end + + # Calculates total bandwidth from uplink and downlink speeds + def calculate_total_bandwidth(uplink_speed, downlink_speed) + uplink_speed + downlink_speed + end + + def generate_key + Base64.encode64(SecureRandom.bytes(32)).chomp + end + + def encrypt_string_chacha20(string, key) + cipher = OpenSSL::Cipher.new('chacha20') + cipher.encrypt + cipher.key = Base64.decode64(key) + encrypted = cipher.update(string) + cipher.final + Base64.encode64(encrypted).chomp + end + + def decrypt_string_chacha20(encrypted_string, key) + decipher = OpenSSL::Cipher.new('chacha20') + decipher.decrypt + decipher.key = Base64.decode64(key) + decrypted = Base64.decode64(encrypted_string) + decipher.update(decrypted) + decipher.final + end + + # Encrypts a given data object using Blowfish and returns the encrypted string + def encrypt_data_blowfish(data, key) + plain_text = YAML.dump(data) + encrypt_string_blowfish(plain_text, key) + end + + # Decrypts a given encrypted string using Blowfish and returns the original data object + def decrypt_data_blowfish(encrypted_text, key) + plain_text = decrypt_string_blowfish(encrypted_text, key) + YAML.load(plain_text) + end +end