diff --git a/.github/workflows/ruby.yml b/.github/workflows/ruby.yml index d4b3c96..1f4d4b9 100644 --- a/.github/workflows/ruby.yml +++ b/.github/workflows/ruby.yml @@ -7,7 +7,7 @@ on: branches: [ "master" ] jobs: - test: + rubocop: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 @@ -15,9 +15,22 @@ jobs: uses: ruby/setup-ruby@v1 with: ruby-version: '3.3' + bundler-cache: true - name: Install dependencies run: bundle install - name: Run cops run: bundle exec rubocop + + specs: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.3' + bundler-cache: true + - name: Install dependencies + run: bundle install - name: Run specs run: bundle exec rspec diff --git a/.gitignore b/.gitignore index 8478a0a..21befb3 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ .byebug_history etc .rspec_status +tmp diff --git a/Gemfile b/Gemfile index 12ad06b..9a9e1ac 100644 --- a/Gemfile +++ b/Gemfile @@ -13,4 +13,5 @@ group :development do gem 'rspec', '~> 3.13' gem 'rubocop', '~> 1.63' gem 'rubocop-rspec', '~> 2.29', '>= 2.29.2' + gem 'super_diff', '~> 0.12.1' end diff --git a/Gemfile.lock b/Gemfile.lock index 47b6e57..a4363be 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -2,6 +2,7 @@ GEM remote: https://rubygems.org/ specs: ast (2.4.2) + attr_extras (7.1.0) base64 (0.2.0) byebug (11.1.3) config (5.4.0) @@ -15,10 +16,13 @@ GEM mustermann (3.0.0) ruby2_keywords (~> 0.0.1) nio4r (2.7.2) + optimist (3.1.0) parallel (1.24.0) parser (3.3.1.0) ast (~> 2.4.1) racc + patience_diff (1.2.0) + optimist (~> 3.0) puma (6.4.2) nio4r (~> 2.0) racc (1.7.3) @@ -76,6 +80,10 @@ GEM rack-protection (= 4.0.0) rack-session (>= 2.0.0, < 3) tilt (~> 2.0) + super_diff (0.12.1) + attr_extras (>= 6.2.4) + diff-lcs + patience_diff tilt (2.3.0) unicode-display_width (2.5.0) @@ -93,6 +101,7 @@ DEPENDENCIES rubocop (~> 1.63) rubocop-rspec (~> 2.29, >= 2.29.2) sinatra (~> 4.0) + super_diff (~> 0.12.1) BUNDLED WITH 2.5.9 diff --git a/README.md b/README.md index c7bd987..afe7a68 100644 --- a/README.md +++ b/README.md @@ -52,17 +52,22 @@ Returns an array with all clients on the server Example response: ```json -{ - "6": { - "id": 6, - "address": "10.8.0.7", +[ + { + "id": 15, + "server_public_key": "server_public_key", + "address": "10.8.0.16/24", "private_key": "private_key", - "public_key": "public_key", "preshared_key": "preshared_key", + "allowed_ips": "0.0.0.0/0, ::/0", + "dns": "1.1.1.1", + "persistent_keepalive": 0, + "endpoint": "0.0.0.0:51820", "data": { - "params": "value" + "params1": "value1" } -} + } +] ``` ### POST /clients diff --git a/VERSION b/VERSION index 7a5e833..02bf650 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v0.1.4 \ No newline at end of file +v0.1.5 \ No newline at end of file diff --git a/app/clients_controller.rb b/app/clients_controller.rb index 1678783..2756a64 100644 --- a/app/clients_controller.rb +++ b/app/clients_controller.rb @@ -9,7 +9,7 @@ def initialize end def index - wire_guard.all_configs.to_json + ClientsSerializer.each_serialize(wire_guard.all_configs, wire_guard.server_public_key) end def create(params) diff --git a/app/clients_serializer.rb b/app/clients_serializer.rb index 0a47328..40e95a5 100644 --- a/app/clients_serializer.rb +++ b/app/clients_serializer.rb @@ -12,17 +12,60 @@ def self.serialize(client_config, server_public_key) new(client_config, server_public_key).client.to_json end + def self.each_serialize(client_config, server_public_key) + new(client_config, server_public_key).clients.to_json + end + + # NOTE: If an instance of a class is created through the each_serialize method, + # then the client_config variable will contain a hash with configs of the type: + # { + # '1' => { + # id: 1, + # address: '10.8.0.2', + # private_key: '1', + # public_key: '2', + # preshared_key: '3', + # data: {} + # }, + # '2' => { + # id: 2, + # address: '10.8.0.3', + # private_key: '1', + # public_key: '2', + # preshared_key: '3', + # data: {} + # } + # } def initialize(client_config, server_public_key) @client_config = stringify_keys(client_config) @server_public_key = server_public_key end + def clients # rubocop:disable Metrics/MethodLength + client_config.map do |_config_id, config| + { + id: config['id'], + server_public_key:, + address: "#{config['address']}/24", + private_key: config['private_key'], + public_key: config['public_key'], + preshared_key: config['preshared_key'], + allowed_ips: WG_ALLOWED_IPS, + dns: DNS, + persistent_keepalive: WG_PERSISTENT_KEEPALIVE, + endpoint: "#{WG_HOST}:#{WG_PORT}", + data: config['data'] + } + end + end + def client # rubocop:disable Metrics/MethodLength { id: client_config['id'], server_public_key:, address: "#{client_config['address']}/24", private_key: client_config['private_key'], + public_key: client_config['public_key'], preshared_key: client_config['preshared_key'], allowed_ips: WG_ALLOWED_IPS, dns: DNS, diff --git a/config/settings/test.yaml b/config/settings/test.yaml index f0d51f2..dac865e 100644 --- a/config/settings/test.yaml +++ b/config/settings/test.yaml @@ -1,4 +1,4 @@ -wg_path: 'etc' +wg_path: 'tmp/etc' wg_device: 'eth0' wg_default_address: '10.8.0.x' wg_allowed_ips: '0.0.0.0/0, ::/0' diff --git a/lib/wire_guard/server.rb b/lib/wire_guard/server.rb index 4a7aec2..be03169 100644 --- a/lib/wire_guard/server.rb +++ b/lib/wire_guard/server.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true +require 'fileutils' require_relative 'config_builder' require_relative 'key_generator' require_relative 'config_updater' diff --git a/spec/app/clients_controller_spec.rb b/spec/app/clients_controller_spec.rb new file mode 100644 index 0000000..4713d97 --- /dev/null +++ b/spec/app/clients_controller_spec.rb @@ -0,0 +1,115 @@ +# frozen_string_literal: true + +RSpec.describe ClientsController do + subject(:controller) { described_class.new } + + let(:wg_conf_path) { "#{Settings.wg_path}/wg0.json" } + + before do + allow(WireGuard::ConfigUpdater).to receive(:update) + allow(WireGuard::KeyGenerator).to receive_messages(wg_genkey: 'wg_genkey', wg_pubkey: 'wg_pubkey', + wg_genpsk: 'wg_genpsk') + end + + after do + FileUtils.rm_rf(wg_conf_path) + end + + describe '#index' do + context 'when there is no configuration file' do + let(:expected_result) do + { + server: { + private_key: 'wg_genkey', + public_key: 'wg_pubkey', + address: '10.8.0.1' + }, + configs: { + last_id: 0, + last_address: '10.8.0.1' + } + } + end + + it 'creates a configuration file and returns an empty array' do # rubocop:disable RSpec/MultipleExpectations + result = controller.index + + expect(result).to eq([].to_json) + + config = File.read(wg_conf_path) + + expect(config).to eq(JSON.pretty_generate(expected_result)) + end + end + + context 'when there is already a configuration file without clients' do + before do + FileUtils.cp('spec/fixtures/empty_wg0.json', wg_conf_path) + end + + it 'returns an empty array' do + expect(controller.index).to eq([].to_json) + end + end + + context 'when there is already a configuration file with clients' do + before do + FileUtils.cp('spec/fixtures/wg0.json', wg_conf_path) + end + + let(:expected_result) do + [ + { + id: 1, + server_public_key: 'uygGKpQt7gOwrP+bqkiXytafHiM+XqFGc0jtZVJ5bnw=', + address: '10.8.0.2/24', + private_key: 'MJn6fwoyqG8S6wsrJzWrUow4leZuEM9O8s+G+kcXElU=', + public_key: 'LiXk4UOfnScgf4UnkcYNcz4wWeqTOW1UrHKRVhZ1OXg=', + preshared_key: '3UzAMA6mLIGjHOImShNb5tWlkwxsha8LZZP7dm49meQ=', + allowed_ips: '0.0.0.0/0, ::/0', + dns: '1.1.1.1', + persistent_keepalive: 0, + endpoint: '2.2.2.2:51820', + data: { + lol: 'kek' + } + }, + { + id: 2, + server_public_key: 'uygGKpQt7gOwrP+bqkiXytafHiM+XqFGc0jtZVJ5bnw=', + address: '10.8.0.3/24', + private_key: 'aN7ye98FKrmydwfA6tHgHE1PbiidWzUJ9cltnies8F4=', + public_key: 'hvIyIW2o8JROVKuY2yYFdUn0oA+43aLuT8KCy0YbORE=', + preshared_key: 'dVW/5kF8wnsx0zAwR4uPIa06btACxpQ/rHBL1B3qPnk=', + allowed_ips: '0.0.0.0/0, ::/0', + dns: '1.1.1.1', + persistent_keepalive: 0, + endpoint: '2.2.2.2:51820', + data: { + cheburek: 'hah' + } + }, + { + id: 3, + server_public_key: 'uygGKpQt7gOwrP+bqkiXytafHiM+XqFGc0jtZVJ5bnw=', + address: '10.8.0.4/24', + private_key: 'eF3Owsqd5MGAIXjmALGBi8ea8mkFUmAiyh80U3hVXn8=', + public_key: 'bPKBg66uC1J2hlkE31Of5wnkg+IjowVXgoLcjcLn0js=', + preshared_key: 'IyVg7fktkSBxJ0uK82j6nlI7Vmo0E53eBmYZ723/45E=', + allowed_ips: '0.0.0.0/0, ::/0', + dns: '1.1.1.1', + persistent_keepalive: 0, + endpoint: '2.2.2.2:51820', + data: { + key: 'value' + } + } + ] + end + + it 'returns a serialized array with all clients' do + expect(controller.index).to eq(expected_result.to_json) + end + end + end +end diff --git a/spec/app/clients_serializer_spec.rb b/spec/app/clients_serializer_spec.rb index 91fb678..dc5c7bf 100644 --- a/spec/app/clients_serializer_spec.rb +++ b/spec/app/clients_serializer_spec.rb @@ -1,37 +1,100 @@ # frozen_string_literal: true RSpec.describe ClientsSerializer do - subject(:serialize) { described_class.serialize(config, key) } - - let(:config) do - { - id: 1, - address: '10.8.0.2', - private_key: '1', - public_key: '2', - preshared_key: '3', - data: {} - } - end - let(:key) { '4' } - let(:excepted_result) do - { - id: 1, - server_public_key: '4', - address: '10.8.0.2/24', - private_key: '1', - preshared_key: '3', - allowed_ips: '0.0.0.0/0, ::/0', - dns: '1.1.1.1', - persistent_keepalive: 0, - endpoint: '2.2.2.2:51820', - data: {} - }.to_json + describe '#serialize' do + subject(:serialize) { described_class.serialize(config, key) } + + let(:config) do + { + id: 1, + address: '10.8.0.2', + private_key: '1', + public_key: '2', + preshared_key: '3', + data: {} + } + end + + let(:expected_result) do + { + id: 1, + server_public_key: '4', + address: '10.8.0.2/24', + private_key: '1', + public_key: '2', + preshared_key: '3', + allowed_ips: '0.0.0.0/0, ::/0', + dns: '1.1.1.1', + persistent_keepalive: 0, + endpoint: '2.2.2.2:51820', + data: {} + }.to_json + end + + it 'serializes one config' do + expect(serialize).to eq(expected_result) + end end - it do - expect(serialize).to eq(excepted_result) + describe '#each_serialize' do + subject(:serialize) { described_class.each_serialize(config, key) } + + let(:config) do + { + '1' => { + id: 1, + address: '10.8.0.2', + private_key: '1', + public_key: '2', + preshared_key: '3', + data: {} + }, + '2' => { + id: 2, + address: '10.8.0.3', + private_key: '1', + public_key: '2', + preshared_key: '3', + data: {} + } + } + end + + let(:expected_result) do + [ + { + id: 1, + server_public_key: '4', + address: '10.8.0.2/24', + private_key: '1', + public_key: '2', + preshared_key: '3', + allowed_ips: '0.0.0.0/0, ::/0', + dns: '1.1.1.1', + persistent_keepalive: 0, + endpoint: '2.2.2.2:51820', + data: {} + }, + { + id: 2, + server_public_key: '4', + address: '10.8.0.3/24', + private_key: '1', + public_key: '2', + preshared_key: '3', + allowed_ips: '0.0.0.0/0, ::/0', + dns: '1.1.1.1', + persistent_keepalive: 0, + endpoint: '2.2.2.2:51820', + data: {} + } + ] + end + + it 'serializes multiple configs' do + expect(serialize).to eq(expected_result.to_json) + end end end diff --git a/spec/fixtures/empty_wg0.json b/spec/fixtures/empty_wg0.json new file mode 100644 index 0000000..0f1bb37 --- /dev/null +++ b/spec/fixtures/empty_wg0.json @@ -0,0 +1,11 @@ +{ + "server": { + "private_key": "wg_genkey", + "public_key": "wg_pubkey", + "address": "10.8.0.1" + }, + "configs": { + "last_id": 0, + "last_address": "10.8.0.1" + } +} \ No newline at end of file diff --git a/spec/fixtures/wg0.json b/spec/fixtures/wg0.json new file mode 100644 index 0000000..19074de --- /dev/null +++ b/spec/fixtures/wg0.json @@ -0,0 +1,41 @@ +{ + "server": { + "private_key": "6Mlqg+1Umojm7a4VvgIi+YMp4oPrWNnZ5HLRFu4my2w=", + "public_key": "uygGKpQt7gOwrP+bqkiXytafHiM+XqFGc0jtZVJ5bnw=", + "address": "10.8.0.1" + }, + "configs": { + "last_id": 3, + "last_address": "10.8.0.4", + "1": { + "id": 1, + "address": "10.8.0.2", + "private_key": "MJn6fwoyqG8S6wsrJzWrUow4leZuEM9O8s+G+kcXElU=", + "public_key": "LiXk4UOfnScgf4UnkcYNcz4wWeqTOW1UrHKRVhZ1OXg=", + "preshared_key": "3UzAMA6mLIGjHOImShNb5tWlkwxsha8LZZP7dm49meQ=", + "data": { + "lol": "kek" + } + }, + "2": { + "id": 2, + "address": "10.8.0.3", + "private_key": "aN7ye98FKrmydwfA6tHgHE1PbiidWzUJ9cltnies8F4=", + "public_key": "hvIyIW2o8JROVKuY2yYFdUn0oA+43aLuT8KCy0YbORE=", + "preshared_key": "dVW/5kF8wnsx0zAwR4uPIa06btACxpQ/rHBL1B3qPnk=", + "data": { + "cheburek": "hah" + } + }, + "3": { + "id": 3, + "address": "10.8.0.4", + "private_key": "eF3Owsqd5MGAIXjmALGBi8ea8mkFUmAiyh80U3hVXn8=", + "public_key": "bPKBg66uC1J2hlkE31Of5wnkg+IjowVXgoLcjcLn0js=", + "preshared_key": "IyVg7fktkSBxJ0uK82j6nlI7Vmo0E53eBmYZ723/45E=", + "data": { + "key": "value" + } + } + } +} \ No newline at end of file diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 6f0a27b..caaaf9a 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -3,9 +3,14 @@ require 'json' require 'byebug' require 'config' +require 'fileutils' Config.load_and_set_settings('config/settings/test.yaml') require_relative '../app/clients_serializer' +require_relative '../app/clients_controller' +require_relative '../lib/wire_guard/server' + +require 'super_diff/rspec' RSpec.configure do |config| config.example_status_persistence_file_path = '.rspec_status'