From 5868377210dce6e894cd6163a3be395c1e6ec9a6 Mon Sep 17 00:00:00 2001 From: Pascal Date: Thu, 25 Feb 2021 12:52:23 -0500 Subject: [PATCH] [add] added maximum client and demo client protection - default demo clients can no longer be modified/deleted when in demo mode - increased default demo reset to 60 minutes --- api.yml | 33 +++++++++++++++++++++++---- app/controllers/clients_controller.rb | 9 ++++++++ app/models/client.rb | 4 +++- config/application.rb | 1 + docker-compose.yml | 4 +++- docker/files/entrypoint.sh | 2 +- spec/requests/clients_request_spec.rb | 14 ++++++++++++ 7 files changed, 60 insertions(+), 7 deletions(-) diff --git a/api.yml b/api.yml index 1610d3d..8b685ac 100644 --- a/api.yml +++ b/api.yml @@ -1,11 +1,34 @@ openapi: "3.0.0" info: - description: "Simple dynDNS via Rest-API." + description: "Simple dynDNS via Rest-API. + + + The demo database will reset every **60** minutes. A maximum of 250 clients can exist in the demo database. + + The following default clients and api keys can be used: + +| client name | api key | + +|--------------|------------------------------------------------------------------------------------------| + +| admin | `zW3If4s5.41a7c846.23b142e6451df545d5034ca8f6050b80ff3e9689f20d5e45d72d160fa7e1cf32` | + +| read_client | `zW3If4s5.28aef5cb.d502cb94ba0802c6363f95ecb3a70de7e2e4b889779e435b7c033f2c72ed4f20` | + +| write_client | `zW3If4s5.3d6129cc.48d41d2a28b16a9a274d3f407026aee2b82cddbb3c11a5496ffe329c78e5d54b` | + + In `demo-mode` it is not possible to call `POST /admin/client`, `PUT /admin/client` or `DELETE /admin/clients/{client_name}` for any of the above default clients. Any newly created client can be freely modified. + + + ***Note**: the X-API-Key value entered when using Authorize takes precedence over values entered in the X-API-Key parameters field. + + Since the X-API-Key parameter field is flagged as required a simple space (or any other character) will still be needed for executing the request here.* + " version: "1.0" title: "rynDNS" servers: - - url: "https://api.pdev.dev/rynDNS/v1" + - url: "https://demo.api.pdev.dev/rynDNS" description: "" security: @@ -49,6 +72,8 @@ paths: description: "Invalid input" "406": description: "Not Acceptable - Can not remove last admin account permissions" + "429": + description: "Too many clients - Maximum client count reached" put: tags: - "admin" @@ -356,5 +381,5 @@ components: example: "115.16.0.99" pattern: "^(((25[0-5]|(2[0-4]|1[0-9]|[1-9]|)[0-9])(.(?!$)|$)){4})?$" externalDocs: - description: "Source" - url: "https://git.pdev.dev" + description: "" + url: "https://git.pdev.dev/pdev/ryndns" diff --git a/app/controllers/clients_controller.rb b/app/controllers/clients_controller.rb index 1b4b814..bb1537e 100644 --- a/app/controllers/clients_controller.rb +++ b/app/controllers/clients_controller.rb @@ -9,6 +9,7 @@ class ClientsController < ApplicationController before_action :authorize_perm_w, only: %i[update_ipv4 destroy_ipv4] before_action :authorize_perm_x, only: %i[create update index show destroy] before_action :client_authorized, except: [:show_public_ipv4] + before_action :demo, only: %i[create update destroy] if ENV['RAILS_API_DEMO'] # URL Methods # POST /admin/client @@ -22,6 +23,7 @@ class ClientsController < ApplicationController # create new client or update existing if client.nil? client ||= Client.new_client(client_params) + return head(429) if client.nil? # reached max client count else # return 406 if request is trying to remove perm_x from last admin account to prevent lockout # TODO: move this into the model (update) @@ -217,4 +219,11 @@ class ClientsController < ApplicationController def client_authorized return head(403) unless @authorized end + + # TODO: put this into a demo environment + def demo + demo_clients = %w[admin read_client write_client] + request_params = params.key?(:name) ? params[:name] : '' + return head(406) if demo_clients.include? request_params + end end diff --git a/app/models/client.rb b/app/models/client.rb index c37cfdb..c19ca7a 100644 --- a/app/models/client.rb +++ b/app/models/client.rb @@ -3,6 +3,8 @@ class Client < ApplicationRecord def self.new_client(params) + return nil if count >= RynDNS::Application::MAX_CLIENTS + perm_string = params[:permission] || 'w' # create write client by default perm_rwx = Client.parse_perm_string(perm_string) @@ -28,7 +30,7 @@ class Client < ApplicationRecord def gen_api_key api_key = SecureRandom.hex(32) self.api_key = api_key - "#{RynDNS::Application::API_KEY_PREFIX}.#{self.name_id}.#{api_key}" + "#{RynDNS::Application::API_KEY_PREFIX}.#{name_id}.#{api_key}" end def api_key diff --git a/config/application.rb b/config/application.rb index addd7c6..040605e 100644 --- a/config/application.rb +++ b/config/application.rb @@ -58,5 +58,6 @@ module RynDNS puts "WARNING: Please modify api_key -> #{API_KEY_PREFIX}.########.############" end API_KEY_HEADER = ENV['RAILS_API_KEY_HEADER'] || 'X-API-Key' + MAX_CLIENTS = ENV['RAILS_MAX_CLIENTS'] || 250 end end diff --git a/docker-compose.yml b/docker-compose.yml index f3e5810..b938606 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -10,6 +10,8 @@ services: environment: # set to run app in subdirectory RAILS_RELATIVE_URL_ROOT: /rynDNS + # set maximum number of clients in db + #RAILS_MAX_CLIENTS: 250 # set custom api key prefix #API_KEY_PREFIX: zW3If4s5 # set custom header name for api key @@ -19,7 +21,7 @@ services: # set app to run as demo #RAILS_API_DEMO: 1 # set cycle to reset demo data - #RAILS_DEMO_RESET: 300 + #RAILS_DEMO_RESET: 60m restart: always image: ryndns container_name: ryndns diff --git a/docker/files/entrypoint.sh b/docker/files/entrypoint.sh index afb9873..104a7e5 100755 --- a/docker/files/entrypoint.sh +++ b/docker/files/entrypoint.sh @@ -7,7 +7,7 @@ prefix_file="${API_PREFIX_FILE:-/app/data/api_prefix}" db_file="${RAILS_DB_PATH:-/app/data/rynDNS.sqlite3}" default_admin_file="${API_DEFAULT_KEY_FILE:-/app/data/api_default_admin}" rails_bin="${RAILS_BINARY:-/app/bin/rails}" -demo_cycle="${RAILS_DEMO_RESET:-300s}" +demo_cycle="${RAILS_DEMO_RESET:-60m}" demo_prefix="zW3If4s5" seed_default_key_text="Created default admin with api_key" # for grep'ing the default admin key diff --git a/spec/requests/clients_request_spec.rb b/spec/requests/clients_request_spec.rb index 0af5d50..87e041a 100644 --- a/spec/requests/clients_request_spec.rb +++ b/spec/requests/clients_request_spec.rb @@ -160,6 +160,20 @@ RSpec.describe 'Clients', type: :request do expect(response).to have_http_status(200) end end + + context 'when sending a valid request to add another client above the MAX_CLIENTS limit' do + before do + clients_to_create = RynDNS::Application::MAX_CLIENTS - Client.count + create_list(:client, clients_to_create) + post '/admin/client', params: { name: 'extra_user' }, headers: api_key_header(admin_client) + end + + it 'does not add another client and returns status code 429' do + expect(Client.count).to eq RynDNS::Application::MAX_CLIENTS + expect(response.body).to be_empty + expect(response).to have_http_status(429) + end + end end describe 'PUT /admin/client' do