Merge pull request #2584 from internetee/api-user-management

Added ApiUser and WhiteIp endpoints to REPP API
This commit is contained in:
Timo Võhmar 2023-06-06 15:29:33 +03:00 committed by GitHub
commit 4dd355ffa2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
32 changed files with 934 additions and 27 deletions

View file

@ -25,6 +25,7 @@ module Repp
types_for_select: AccountActivity.types_for_select }) types_for_select: AccountActivity.types_for_select })
end end
# rubocop:disable Metrics/MethodLength
api :get, '/repp/v1/accounts/details' api :get, '/repp/v1/accounts/details'
desc 'Get current registrar account details' desc 'Get current registrar account details'
def details def details
@ -34,10 +35,15 @@ module Repp
iban: registrar.iban, iban: registrar.iban,
iban_max_length: Iban.max_length, iban_max_length: Iban.max_length,
linked_users: serialized_users(current_user.linked_users), linked_users: serialized_users(current_user.linked_users),
api_users: serialized_users(current_user.api_users),
white_ips: serialized_ips(registrar.white_ips),
balance_auto_reload: type, balance_auto_reload: type,
min_deposit: Setting.minimum_deposit } } min_deposit: Setting.minimum_deposit },
roles: ApiUser::ROLES,
interfaces: WhiteIp::INTERFACES }
render_success(data: resp) render_success(data: resp)
end end
# rubocop:enable Metrics/MethodLength
api :put, '/repp/v1/accounts' api :put, '/repp/v1/accounts'
desc 'Update current registrar account details' desc 'Update current registrar account details'
@ -148,7 +154,8 @@ module Repp
arr = [] arr = []
users.each do |u| users.each do |u|
arr << { id: u.id, username: u.username, arr << { id: u.id, username: u.username,
role: u.roles.first, registrar_name: u.registrar.name } role: u.roles.first, registrar_name: u.registrar.name,
active: u.active }
end end
arr arr
@ -165,6 +172,10 @@ module Repp
arr arr
end end
def serialized_ips(ips)
ips.as_json(only: %i[id ipv4 ipv6 interfaces])
end
end end
end end
end end

View file

@ -0,0 +1,73 @@
require 'serializers/repp/api_user'
module Repp
module V1
class ApiUsersController < BaseController
load_and_authorize_resource
THROTTLED_ACTIONS = %i[index show create update destroy].freeze
include Shunter::Integration::Throttle
api :GET, '/repp/v1/api_users'
desc 'Get all api users'
def index
users = current_user.registrar.api_users
render_success(data: { users: serialized_users(users),
count: users.count })
end
api :GET, '/repp/v1/api_users/:id'
desc 'Get a specific api user'
def show
serializer = Serializers::Repp::ApiUser.new(@api_user)
render_success(data: { user: serializer.to_json, roles: ApiUser::ROLES })
end
api :POST, '/repp/v1/api_users'
desc 'Create a new api user'
def create
@api_user = current_user.registrar.api_users.build(api_user_params)
@api_user.active = api_user_params[:active]
unless @api_user.save
handle_non_epp_errors(@api_user)
return
end
render_success(data: { api_user: { id: @api_user.id } })
end
api :PUT, '/repp/v1/api_users/:id'
desc 'Update api user'
def update
unless @api_user.update(api_user_params)
handle_non_epp_errors(@api_user)
return
end
render_success(data: { api_user: { id: @api_user.id } })
end
api :DELETE, '/repp/v1/api_users/:id'
desc 'Delete a specific api user'
def destroy
unless @api_user.destroy
handle_non_epp_errors(@api_user)
return
end
render_success
end
private
def api_user_params
params.require(:api_user).permit(:username, :plain_text_password, :active,
:identity_code, { roles: [] })
end
def serialized_users(users)
users.map { |i| Serializers::Repp::ApiUser.new(i).to_json }
end
end
end
end

View file

@ -0,0 +1,65 @@
module Repp
module V1
class WhiteIpsController < BaseController
load_and_authorize_resource
THROTTLED_ACTIONS = %i[index show create update destroy].freeze
include Shunter::Integration::Throttle
api :GET, '/repp/v1/white_ips'
desc 'Get all whitelisted IPs'
def index
ips = current_user.registrar.white_ips
render_success(data: { ips: ips.as_json,
count: ips.count })
end
api :GET, '/repp/v1/white_ips/:id'
desc 'Get a specific whitelisted IP address'
def show
render_success(data: { ip: @white_ip.as_json, interfaces: WhiteIp::INTERFACES })
end
api :POST, '/repp/v1/white_ips'
desc 'Add new whitelisted IP'
def create
@white_ip = current_user.registrar.white_ips.build(white_ip_params)
unless @white_ip.save
handle_non_epp_errors(@white_ip)
return
end
render_success(data: { ip: { id: @white_ip.id } })
end
api :PUT, '/repp/v1/white_ips/:id'
desc 'Update whitelisted IP address'
def update
unless @white_ip.update(white_ip_params)
handle_non_epp_errors(@white_ip)
return
end
render_success(data: { ip: { id: @white_ip.id } })
end
api :DELETE, '/repp/v1/white_ips/:id'
desc 'Delete a specific whitelisted IP address'
def destroy
unless @white_ip.destroy
handle_non_epp_errors(@white_ip)
return
end
render_success
end
private
def white_ip_params
params.require(:white_ip).permit(:ipv4, :ipv6, interfaces: [])
end
end
end
end

View file

@ -28,6 +28,8 @@ class Ability
def super # Registrar/api_user dynamic role def super # Registrar/api_user dynamic role
epp epp
billing billing
can :manage, ApiUser
can :manage, WhiteIp
end end
def epp # Registrar/api_user dynamic role def epp # Registrar/api_user dynamic role

View file

@ -24,6 +24,7 @@ class ApiUser < User
validates :username, :plain_text_password, :registrar, :roles, presence: true validates :username, :plain_text_password, :registrar, :roles, presence: true
validates :plain_text_password, length: { minimum: min_password_length } validates :plain_text_password, length: { minimum: min_password_length }
validates :username, uniqueness: true validates :username, uniqueness: true
validates :identity_code, uniqueness: { scope: :registrar_id }, if: -> { identity_code.present? }
delegate :code, :name, to: :registrar, prefix: true delegate :code, :name, to: :registrar, prefix: true
delegate :legaldoc_mandatory?, to: :registrar delegate :legaldoc_mandatory?, to: :registrar
@ -36,6 +37,8 @@ class ApiUser < User
ROLES = %w[super epp billing].freeze # should not match to admin roles ROLES = %w[super epp billing].freeze # should not match to admin roles
scope :non_super, -> { where.not('roles @> ARRAY[?]::varchar[]', ['super']) }
def ability def ability
@ability ||= Ability.new(self) @ability ||= Ability.new(self)
end end
@ -81,12 +84,16 @@ class ApiUser < User
end end
def linked_users def linked_users
self.class.where(identity_code: identity_code) self.class.where(identity_code: identity_code, active: true)
.where("identity_code IS NOT NULL AND identity_code != ''") .where("identity_code IS NOT NULL AND identity_code != ''")
.where.not(id: id) .where.not(id: id)
.includes(:registrar) .includes(:registrar)
end end
def api_users
self.class.where(registrar_id: registrar_id)
end
def linked_with?(another_api_user) def linked_with?(another_api_user)
another_api_user.identity_code == identity_code another_api_user.identity_code == identity_code
end end
@ -109,6 +116,14 @@ class ApiUser < User
'Accreditation Expire Date', 'Created', 'Updated'] 'Accreditation Expire Date', 'Created', 'Updated']
end end
def self.ransackable_associations(*)
authorizable_ransackable_associations
end
def self.ransackable_attributes(*)
authorizable_ransackable_attributes
end
private private
def machine_readable_certificate(cert) def machine_readable_certificate(cert)

View file

@ -5,15 +5,15 @@ class Certificate < ApplicationRecord
belongs_to :api_user belongs_to :api_user
SIGNED = 'signed' SIGNED = 'signed'.freeze
UNSIGNED = 'unsigned' UNSIGNED = 'unsigned'.freeze
EXPIRED = 'expired' EXPIRED = 'expired'.freeze
REVOKED = 'revoked' REVOKED = 'revoked'.freeze
VALID = 'valid' VALID = 'valid'.freeze
API = 'api' API = 'api'.freeze
REGISTRAR = 'registrar' REGISTRAR = 'registrar'.freeze
INTERFACES = [API, REGISTRAR] INTERFACES = [API, REGISTRAR].freeze
scope 'api', -> { where(interface: API) } scope 'api', -> { where(interface: API) }
scope 'registrar', -> { where(interface: REGISTRAR) } scope 'registrar', -> { where(interface: REGISTRAR) }
@ -21,6 +21,7 @@ class Certificate < ApplicationRecord
validate :validate_csr_and_crt_presence validate :validate_csr_and_crt_presence
def validate_csr_and_crt_presence def validate_csr_and_crt_presence
return if csr.try(:scrub).present? || crt.try(:scrub).present? return if csr.try(:scrub).present? || crt.try(:scrub).present?
errors.add(:base, I18n.t(:crt_or_csr_must_be_present)) errors.add(:base, I18n.t(:crt_or_csr_must_be_present))
end end
@ -28,8 +29,8 @@ class Certificate < ApplicationRecord
def validate_csr_and_crt def validate_csr_and_crt
parsed_crt parsed_crt
parsed_csr parsed_csr
rescue OpenSSL::X509::RequestError, OpenSSL::X509::CertificateError rescue OpenSSL::X509::RequestError, OpenSSL::X509::CertificateError
errors.add(:base, I18n.t(:invalid_csr_or_crt)) errors.add(:base, I18n.t(:invalid_csr_or_crt))
end end
validate :assign_metadata, on: :create validate :assign_metadata, on: :create
@ -67,7 +68,8 @@ class Certificate < ApplicationRecord
@cached_status = SIGNED @cached_status = SIGNED
@cached_status = EXPIRED if parsed_crt.not_before > Time.zone.now.utc && parsed_crt.not_after < Time.zone.now.utc expired = parsed_crt.not_before > Time.zone.now.utc && parsed_crt.not_after < Time.zone.now.utc
@cached_status = EXPIRED if expired
crl = OpenSSL::X509::CRL.new(File.open("#{ENV['crl_dir']}/crl.pem").read) crl = OpenSSL::X509::CRL.new(File.open("#{ENV['crl_dir']}/crl.pem").read)
return @cached_status unless crl.revoked.map(&:serial).include?(parsed_crt.serial) return @cached_status unless crl.revoked.map(&:serial).include?(parsed_crt.serial)
@ -144,7 +146,8 @@ class Certificate < ApplicationRecord
end end
def parse_md_from_string(crt) def parse_md_from_string(crt)
return nil if crt.blank? return if crt.blank?
crt = crt.split(' ').join("\n") crt = crt.split(' ').join("\n")
crt.gsub!("-----BEGIN\nCERTIFICATE-----\n", "-----BEGIN CERTIFICATE-----\n") crt.gsub!("-----BEGIN\nCERTIFICATE-----\n", "-----BEGIN CERTIFICATE-----\n")
crt.gsub!("\n-----END\nCERTIFICATE-----", "\n-----END CERTIFICATE-----") crt.gsub!("\n-----END\nCERTIFICATE-----", "\n-----END CERTIFICATE-----")

View file

@ -5,6 +5,8 @@ class WhiteIp < ApplicationRecord
validate :valid_ipv4? validate :valid_ipv4?
validate :valid_ipv6? validate :valid_ipv6?
validate :validate_ipv4_and_ipv6 validate :validate_ipv4_and_ipv6
validate :validate_only_one_ip
validate :validate_max_ip_count
before_save :normalize_blank_values before_save :normalize_blank_values
def normalize_blank_values def normalize_blank_values
@ -17,6 +19,12 @@ class WhiteIp < ApplicationRecord
errors.add(:base, I18n.t(:ipv4_or_ipv6_must_be_present)) errors.add(:base, I18n.t(:ipv4_or_ipv6_must_be_present))
end end
def validate_only_one_ip
return unless ipv4.present? && ipv6.present?
errors.add(:base, I18n.t(:ip_must_be_one))
end
def valid_ipv4? def valid_ipv4?
return if ipv4.blank? return if ipv4.blank?
@ -33,6 +41,31 @@ class WhiteIp < ApplicationRecord
errors.add(:ipv6, :invalid) errors.add(:ipv6, :invalid)
end end
def validate_max_ip_count
return if errors.any?
ip_addresses = registrar.white_ips
total = ip_addresses.size + count_network_addresses(ipv4.presence || ipv6)
limit = Setting.ip_whitelist_max_count
return unless total >= limit
errors.add(:base, I18n.t(:ip_limit_exceeded, total: total, limit: limit))
end
def count_network_addresses(ip)
address = IPAddr.new(ip)
if address.ipv4?
subnet_mask = address.prefix
2**(32 - subnet_mask) - 2
elsif address.ipv6?
subnet_mask = address.prefix
2**(128 - subnet_mask) - 2
else
0
end
end
API = 'api'.freeze API = 'api'.freeze
REGISTRAR = 'registrar'.freeze REGISTRAR = 'registrar'.freeze
INTERFACES = [API, REGISTRAR].freeze INTERFACES = [API, REGISTRAR].freeze
@ -77,6 +110,10 @@ class WhiteIp < ApplicationRecord
def csv_header def csv_header
%w[IPv4 IPv6 Interfaces Created Updated] %w[IPv4 IPv6 Interfaces Created Updated]
end end
def ransackable_attributes(*)
authorizable_ransackable_attributes
end
end end
def as_csv_row def as_csv_row

View file

@ -545,6 +545,8 @@ en:
upload_crt: 'Upload CRT' upload_crt: 'Upload CRT'
crt_or_csr_must_be_present: 'CRT or CSR must be present' crt_or_csr_must_be_present: 'CRT or CSR must be present'
ipv4_or_ipv6_must_be_present: 'IPv4 or IPv6 must be present' ipv4_or_ipv6_must_be_present: 'IPv4 or IPv6 must be present'
ip_must_be_one: 'Please enter only one IP address'
ip_limit_exceeded: 'IP address limit exceeded. Total addresses: %{total}. Limit: %{limit}.'
white_ip: 'White IP' white_ip: 'White IP'
edit_white_ip: 'Edit white IP' edit_white_ip: 'Edit white IP'
confirm_domain_delete: 'Confirm domain delete' confirm_domain_delete: 'Confirm domain delete'

View file

@ -108,6 +108,8 @@ Rails.application.routes.draw do
get '/market_share_growth_rate', to: 'stats#market_share_growth_rate' get '/market_share_growth_rate', to: 'stats#market_share_growth_rate'
end end
end end
resources :api_users, only: %i[index show update create destroy]
resources :white_ips, only: %i[index show update create destroy]
namespace :registrar do namespace :registrar do
resources :notifications, only: %i[index show update] do resources :notifications, only: %i[index show update] do
collection do collection do

View file

@ -0,0 +1,11 @@
class AddIpWhitelistMaxCountSetting < ActiveRecord::Migration[6.1]
def up
Setting.create(code: 'ip_whitelist_max_count',
value: 256, format: 'integer',
group: 'other')
end
def down
Setting.find_by(code: 'ip_whitelist_max_count').destroy
end
end

View file

@ -61,6 +61,7 @@ ActiveRecord::Base.transaction do
SettingEntry.create(code: 'dispute_period_in_months', value: '36', format: 'integer', group: 'other') SettingEntry.create(code: 'dispute_period_in_months', value: '36', format: 'integer', group: 'other')
SettingEntry.create(code: 'registry_whois_disclaimer', value: 'Search results may not be used for commercial, advertising, recompilation, repackaging, redistribution, reuse, obscuring or other similar activities.', format: 'string', group: 'contacts') SettingEntry.create(code: 'registry_whois_disclaimer', value: 'Search results may not be used for commercial, advertising, recompilation, repackaging, redistribution, reuse, obscuring or other similar activities.', format: 'string', group: 'contacts')
SettingEntry.create(code: 'legal_document_is_mandatory', value: 'true', format: 'boolean', group: 'domain_validation') SettingEntry.create(code: 'legal_document_is_mandatory', value: 'true', format: 'boolean', group: 'domain_validation')
SettingEntry.create(code: 'ip_whitelist_max_count', value: '256', format: 'integer', group: 'other')
AdminUser.where(username: 'admin').first_or_create!( AdminUser.where(username: 'admin').first_or_create!(
username: 'admin', username: 'admin',

View file

@ -5467,6 +5467,7 @@ INSERT INTO "schema_migrations" (version) VALUES
('20221206091556'), ('20221206091556'),
('20221207102831'), ('20221207102831'),
('20221214073933'), ('20221214073933'),
('20221214074252'); ('20221214074252'),
('20230531111154');

View file

@ -0,0 +1,42 @@
module Serializers
module Repp
class ApiUser
attr_reader :user
def initialize(user)
@user = user
end
# rubocop:disable Metrics/MethodLength
def to_json(obj = user)
json = {
id: obj.id,
name: obj.username,
password: obj.plain_text_password,
identity_code: obj.identity_code,
roles: obj.roles.join(', '),
active: obj.active,
accredited: obj.accredited?,
accreditation_expired: obj.accreditation_expired?,
accreditation_expire_date: obj.accreditation_expire_date,
created_at: obj.created_at,
updated_at: obj.updated_at,
creator: obj.creator_str,
updator: obj.updator_str,
}
json[:certificates] = certificates
json
end
# rubocop:enable Metrics/MethodLength
private
def certificates
user.certificates.map do |x|
subject = x.csr ? x.parsed_csr.try(:subject) : x.parsed_crt.try(:subject)
{ subject: subject.to_s, status: x.status }
end
end
end
end
end

View file

@ -1,2 +1,3 @@
Username,Password,Identity Code,Role,Active,Accredited,Accreditation Expire Date,Created,Updated Username,Password,Identity Code,Role,Active,Accredited,Accreditation Expire Date,Created,Updated
test_bestnames,testtest,1234,super,true,false,,2010-07-05 10:30:00 +0300,2010-07-05 10:30:00 +0300 test_bestnames,testtest,1234,super,true,false,,2010-07-05 10:30:00 +0300,2010-07-05 10:30:00 +0300
test_bestnames_epp,testtest,,epp,true,false,,2010-07-05 10:30:00 +0300,2010-07-05 10:30:00 +0300

1 Username Password Identity Code Role Active Accredited Accreditation Expire Date Created Updated
2 test_bestnames testtest 1234 super true false 2010-07-05 10:30:00 +0300 2010-07-05 10:30:00 +0300
3 test_bestnames_epp testtest epp true false 2010-07-05 10:30:00 +0300 2010-07-05 10:30:00 +0300

View file

@ -1,2 +1,3 @@
IPv4,IPv6,Interfaces,Created,Updated IPv4,IPv6,Interfaces,Created,Updated
127.0.0.1,,"REGISTRAR, API",2010-07-05 10:30:00 +0300,2010-07-05 10:30:00 +0300 127.0.0.1,,"REGISTRAR, API",2010-07-05 10:30:00 +0300,2010-07-05 10:30:00 +0300
,2001:0db8:85a3:0000:0000:8a2e:0370:7334,"REGISTRAR, API",2010-07-05 10:30:00 +0300,2010-07-05 10:30:00 +0300

1 IPv4 IPv6 Interfaces Created Updated
2 127.0.0.1 REGISTRAR, API 2010-07-05 10:30:00 +0300 2010-07-05 10:30:00 +0300
3 2001:0db8:85a3:0000:0000:8a2e:0370:7334 REGISTRAR, API 2010-07-05 10:30:00 +0300 2010-07-05 10:30:00 +0300

View file

@ -461,3 +461,11 @@ legal_document_is_mandatory:
format: boolean format: boolean
created_at: <%= Time.zone.parse('2010-07-05') %> created_at: <%= Time.zone.parse('2010-07-05') %>
updated_at: <%= Time.zone.parse('2010-07-05') %> updated_at: <%= Time.zone.parse('2010-07-05') %>
ip_whitelist_max_count:
code: ip_whitelist_max_count
value: '256'
group: other
format: integer
created_at: <%= Time.zone.parse('2010-07-05') %>
updated_at: <%= Time.zone.parse('2010-07-05') %>

View file

@ -8,6 +8,16 @@ api_bestnames:
roles: roles:
- super - super
api_bestnames_epp:
username: test_bestnames_epp
plain_text_password: testtest
identity_code:
type: ApiUser
registrar: bestnames
active: true
roles:
- epp
api_goodnames: api_goodnames:
username: test_goodnames username: test_goodnames
plain_text_password: testtest plain_text_password: testtest

View file

@ -3,4 +3,11 @@ one:
ipv4: 127.0.0.1 ipv4: 127.0.0.1
interfaces: interfaces:
- <%= WhiteIp::REGISTRAR %> - <%= WhiteIp::REGISTRAR %>
- <%= WhiteIp::API %> - <%= WhiteIp::API %>
two:
registrar: bestnames
ipv6: 2001:0db8:85a3:0000:0000:8a2e:0370:7334
interfaces:
- <%= WhiteIp::REGISTRAR %>
- <%= WhiteIp::API %>

View file

@ -2,7 +2,6 @@ require 'test_helper'
require 'application_system_test_case' require 'application_system_test_case'
class AdminAreaWhiteIpsIntegrationTest < JavaScriptApplicationSystemTestCase class AdminAreaWhiteIpsIntegrationTest < JavaScriptApplicationSystemTestCase
setup do setup do
WebMock.allow_net_connect! WebMock.allow_net_connect!
sign_in users(:admin) sign_in users(:admin)
@ -17,11 +16,11 @@ class AdminAreaWhiteIpsIntegrationTest < JavaScriptApplicationSystemTestCase
def test_create_new_whitelisted_ip def test_create_new_whitelisted_ip
visit_new_whitelisted_ip_page visit_new_whitelisted_ip_page
fill_in 'IPv4', with: "127.0.0.1" # fill_in 'IPv4', with: '127.0.0.1'
fill_in 'IPv6', with: "::ffff:192.0.2.1" fill_in 'IPv6', with: '::ffff:192.0.2.1'
find(:css, "#white_ip_interfaces_api").set(true) find(:css, '#white_ip_interfaces_api').set(true)
find(:css, "#white_ip_interfaces_registrar").set(true) find(:css, '#white_ip_interfaces_registrar').set(true)
click_on 'Save' click_on 'Save'
@ -30,7 +29,7 @@ class AdminAreaWhiteIpsIntegrationTest < JavaScriptApplicationSystemTestCase
def test_failed_to_create_new_whitelisted_ip def test_failed_to_create_new_whitelisted_ip
visit_new_whitelisted_ip_page visit_new_whitelisted_ip_page
fill_in 'IPv4', with: "asdadadad.asd" fill_in 'IPv4', with: 'asdadadad.asd'
click_on 'Save' click_on 'Save'
@ -45,8 +44,8 @@ class AdminAreaWhiteIpsIntegrationTest < JavaScriptApplicationSystemTestCase
visit_info_whitelisted_ip_page visit_info_whitelisted_ip_page
click_on 'Edit' click_on 'Edit'
fill_in 'IPv4', with: "127.0.0.2" fill_in 'IPv4', with: '127.0.0.2'
find(:css, "#white_ip_interfaces_api").set(false) find(:css, '#white_ip_interfaces_api').set(false)
click_on 'Save' click_on 'Save'
assert_text 'Record updated' assert_text 'Record updated'
@ -55,7 +54,7 @@ class AdminAreaWhiteIpsIntegrationTest < JavaScriptApplicationSystemTestCase
def test_failed_to_update_whitelisted_ip def test_failed_to_update_whitelisted_ip
visit_info_whitelisted_ip_page visit_info_whitelisted_ip_page
click_on 'Edit' click_on 'Edit'
fill_in 'IPv4', with: "asdadad#" fill_in 'IPv4', with: 'asdadad#'
click_on 'Save' click_on 'Save'

View file

@ -0,0 +1,82 @@
require 'test_helper'
class ReppV1ApiUsersCreateTest < ActionDispatch::IntegrationTest
def setup
@user = users(:api_bestnames)
token = Base64.encode64("#{@user.username}:#{@user.plain_text_password}")
token = "Basic #{token}"
@auth_headers = { 'Authorization' => token }
adapter = ENV['shunter_default_adapter'].constantize.new
adapter&.clear!
end
def test_creates_new_api_user
request_body = {
api_user: {
username: 'username',
plain_text_password: 'password',
active: true,
identity_code: '123',
roles: ['super'],
},
}
post '/repp/v1/api_users', headers: @auth_headers, params: request_body
json = JSON.parse(response.body, symbolize_names: true)
assert_response :ok
assert_equal 1000, json[:code]
assert_equal 'Command completed successfully', json[:message]
api_user = ApiUser.find(json[:data][:api_user][:id])
assert api_user.present?
assert api_user.active
assert_equal(request_body[:api_user][:username], api_user.username)
end
def test_validates_identity_code_per_registrar
request_body = {
api_user: {
username: 'username',
plain_text_password: 'password',
active: true,
identity_code: @user.identity_code,
roles: ['super'],
},
}
post '/repp/v1/api_users', headers: @auth_headers, params: request_body
json = JSON.parse(response.body, symbolize_names: true)
assert_response :bad_request
assert json[:message].include? 'Identity code already exists'
end
def test_returns_error_response_if_throttled
ENV['shunter_default_threshold'] = '1'
ENV['shunter_enabled'] = 'true'
request_body = {
api_user: {
username: 'username',
plain_text_password: 'password',
active: true,
identity_code: '123',
roles: ['super'],
},
}
post '/repp/v1/api_users', headers: @auth_headers, params: request_body
post '/repp/v1/api_users', headers: @auth_headers, params: request_body
json = JSON.parse(response.body, symbolize_names: true)
assert_response :bad_request
assert_equal json[:code], 2502
assert response.body.include?(Shunter.default_error_message)
ENV['shunter_default_threshold'] = '10000'
ENV['shunter_enabled'] = 'false'
end
end

View file

@ -0,0 +1,49 @@
require 'test_helper'
class ReppV1ApiUsersDeleteTest < ActionDispatch::IntegrationTest
def setup
@user = users(:api_bestnames)
token = Base64.encode64("#{@user.username}:#{@user.plain_text_password}")
token = "Basic #{token}"
@auth_headers = { 'Authorization' => token }
adapter = ENV['shunter_default_adapter'].constantize.new
adapter&.clear!
end
def test_deletes_api_user
epp_user = users(:api_bestnames_epp)
delete "/repp/v1/api_users/#{epp_user.id}", headers: @auth_headers
json = JSON.parse(response.body, symbolize_names: true)
assert_response :ok
assert_equal 1000, json[:code]
assert_equal 'Command completed successfully', json[:message]
refute ApiUser.exists?(epp_user.id)
end
def test_cannot_delete_api_user
delete '/repp/v1/api_users/wrong_id', headers: @auth_headers
json = JSON.parse(response.body, symbolize_names: true)
assert_response :not_found
assert_equal 2303, json[:code]
assert_equal 'Object does not exist', json[:message]
end
def test_returns_error_response_if_throttled
ENV['shunter_default_threshold'] = '1'
ENV['shunter_enabled'] = 'true'
delete "/repp/v1/api_users/#{users(:api_bestnames_epp).id}", headers: @auth_headers
delete "/repp/v1/api_users/#{users(:api_bestnames).id}", headers: @auth_headers
json = JSON.parse(response.body, symbolize_names: true)
assert_response :bad_request
assert_equal json[:code], 2502
assert response.body.include?(Shunter.default_error_message)
ENV['shunter_default_threshold'] = '10000'
ENV['shunter_enabled'] = 'false'
end
end

View file

@ -0,0 +1,39 @@
require 'test_helper'
class ReppV1ApiUsersListTest < ActionDispatch::IntegrationTest
def setup
@user = users(:api_bestnames)
token = Base64.encode64("#{@user.username}:#{@user.plain_text_password}")
token = "Basic #{token}"
@auth_headers = { 'Authorization' => token }
adapter = ENV['shunter_default_adapter'].constantize.new
adapter&.clear!
end
def test_returns_api_users
get repp_v1_api_users_url, headers: @auth_headers
assert_response :success
response_json = JSON.parse(response.body, symbolize_names: true)
assert_equal @user.registrar.api_users.count, response_json[:data][:count]
assert_equal @user.registrar.api_users.count, response_json[:data][:users].length
assert response_json[:data][:users][0].is_a? Hash
end
def test_returns_error_response_if_throttled
ENV['shunter_default_threshold'] = '1'
ENV['shunter_enabled'] = 'true'
get repp_v1_api_users_path, headers: @auth_headers
get repp_v1_api_users_path, headers: @auth_headers
json = JSON.parse(response.body, symbolize_names: true)
assert_response :bad_request
assert_equal json[:code], 2502
assert response.body.include?(Shunter.default_error_message)
ENV['shunter_default_threshold'] = '10000'
ENV['shunter_enabled'] = 'false'
end
end

View file

@ -0,0 +1,54 @@
require 'test_helper'
class ReppV1ApiUsersShowTest < ActionDispatch::IntegrationTest
def setup
@user = users(:api_bestnames)
token = Base64.encode64("#{@user.username}:#{@user.plain_text_password}")
token = "Basic #{token}"
@auth_headers = { 'Authorization' => token }
adapter = ENV['shunter_default_adapter'].constantize.new
adapter&.clear!
end
def test_returns_error_when_not_found
get repp_v1_api_user_path(id: 'definitelynotexistant'), headers: @auth_headers
json = JSON.parse(response.body, symbolize_names: true)
assert_response :not_found
assert_equal 2303, json[:code]
assert_equal 'Object does not exist', json[:message]
end
def test_shows_existing_api_user
user = @user.registrar.api_users.first
get repp_v1_api_user_path(id: user.id), headers: @auth_headers
json = JSON.parse(response.body, symbolize_names: true)
assert_response :ok
assert_equal 1000, json[:code]
assert_equal 'Command completed successfully', json[:message]
assert_equal user.id, json[:data][:user][:id]
assert_equal ApiUser::ROLES, json[:data][:roles]
end
def test_returns_error_response_if_throttled
ENV['shunter_default_threshold'] = '1'
ENV['shunter_enabled'] = 'true'
user = @user.registrar.api_users.first
get repp_v1_api_user_path(id: user.id), headers: @auth_headers
get repp_v1_api_user_path(id: user.id), headers: @auth_headers
json = JSON.parse(response.body, symbolize_names: true)
assert_response :bad_request
assert_equal json[:code], 2502
assert response.body.include?(Shunter.default_error_message)
ENV['shunter_default_threshold'] = '10000'
ENV['shunter_enabled'] = 'false'
end
end

View file

@ -0,0 +1,83 @@
require 'test_helper'
class ReppV1ApiUsersUpdateTest < ActionDispatch::IntegrationTest
def setup
@user = users(:api_bestnames)
token = Base64.encode64("#{@user.username}:#{@user.plain_text_password}")
token = "Basic #{token}"
@auth_headers = { 'Authorization' => token }
adapter = ENV['shunter_default_adapter'].constantize.new
adapter&.clear!
end
def test_updates_api_user
request_body = {
api_user: {
active: false,
},
}
put "/repp/v1/api_users/#{@user.id}", headers: @auth_headers, params: request_body
json = JSON.parse(response.body, symbolize_names: true)
assert_response :ok
assert_equal 1000, json[:code]
assert_equal 'Command completed successfully', json[:message]
api_user = ApiUser.find(json[:data][:api_user][:id])
assert_equal api_user.username, @user.username
refute api_user.active
end
def test_can_not_change_identity_code_if_already_exists_per_registrar
epp_user = users(:api_bestnames_epp)
request_body = {
api_user: {
identity_code: @user.identity_code,
},
}
put "/repp/v1/api_users/#{epp_user.id}", headers: @auth_headers, params: request_body
json = JSON.parse(response.body, symbolize_names: true)
assert_response :bad_request
assert json[:message].include? 'Identity code already exists'
end
def test_returns_error_if_password_wrong_format
request_body = {
api_user: {
plain_text_password: '123',
},
}
put "/repp/v1/api_users/#{@user.id}", headers: @auth_headers, params: request_body
json = JSON.parse(response.body, symbolize_names: true)
assert_response :bad_request
assert json[:message].include? 'Password is too short'
end
def test_returns_error_response_if_throttled
ENV['shunter_default_threshold'] = '1'
ENV['shunter_enabled'] = 'true'
request_body = {
api_user: {
active: true,
},
}
put "/repp/v1/api_users/#{@user.id}", headers: @auth_headers, params: request_body
put "/repp/v1/api_users/#{@user.id}", headers: @auth_headers, params: request_body
json = JSON.parse(response.body, symbolize_names: true)
assert_response :bad_request
assert_equal json[:code], 2502
assert response.body.include?(Shunter.default_error_message)
ENV['shunter_default_threshold'] = '10000'
ENV['shunter_enabled'] = 'false'
end
end

View file

@ -0,0 +1,75 @@
require 'test_helper'
class ReppV1WhiteIpsCreateTest < ActionDispatch::IntegrationTest
def setup
@user = users(:api_bestnames)
token = Base64.encode64("#{@user.username}:#{@user.plain_text_password}")
token = "Basic #{token}"
@auth_headers = { 'Authorization' => token }
adapter = ENV['shunter_default_adapter'].constantize.new
adapter&.clear!
end
def test_creates_new_white_ip
request_body = {
white_ip: {
ipv4: '127.0.0.1',
ipv6: '',
interfaces: ['API'],
},
}
post '/repp/v1/white_ips', headers: @auth_headers, params: request_body
json = JSON.parse(response.body, symbolize_names: true)
assert_response :ok
assert_equal 1000, json[:code]
assert_equal 'Command completed successfully', json[:message]
white_ip = WhiteIp.find(json[:data][:ip][:id])
assert white_ip.present?
assert_equal(request_body[:white_ip][:ipv4], white_ip.ipv4)
end
def test_validates_ip_max_count
request_body = {
white_ip: {
ipv4: '',
ipv6: '2001:db8::/120',
interfaces: ['API'],
},
}
post '/repp/v1/white_ips', headers: @auth_headers, params: request_body
json = JSON.parse(response.body, symbolize_names: true)
assert_response :bad_request
assert json[:message].include? 'IP address limit exceeded'
end
def test_returns_error_response_if_throttled
ENV['shunter_default_threshold'] = '1'
ENV['shunter_enabled'] = 'true'
request_body = {
white_ip: {
ipv4: '127.0.0.1',
ipv6: '',
interfaces: ['API'],
},
}
post '/repp/v1/white_ips', headers: @auth_headers, params: request_body
post '/repp/v1/white_ips', headers: @auth_headers, params: request_body
json = JSON.parse(response.body, symbolize_names: true)
assert_response :bad_request
assert_equal json[:code], 2502
assert response.body.include?(Shunter.default_error_message)
ENV['shunter_default_threshold'] = '10000'
ENV['shunter_enabled'] = 'false'
end
end

View file

@ -0,0 +1,40 @@
require 'test_helper'
class ReppV1WhiteIpsDeleteTest < ActionDispatch::IntegrationTest
def setup
@user = users(:api_bestnames)
token = Base64.encode64("#{@user.username}:#{@user.plain_text_password}")
token = "Basic #{token}"
@auth_headers = { 'Authorization' => token }
adapter = ENV['shunter_default_adapter'].constantize.new
adapter&.clear!
end
def test_deletes_white_ip
ip = white_ips(:one)
delete "/repp/v1/white_ips/#{ip.id}", headers: @auth_headers
json = JSON.parse(response.body, symbolize_names: true)
assert_response :ok
assert_equal 1000, json[:code]
assert_equal 'Command completed successfully', json[:message]
refute WhiteIp.exists?(ip.id)
end
def test_returns_error_response_if_throttled
ENV['shunter_default_threshold'] = '1'
ENV['shunter_enabled'] = 'true'
delete "/repp/v1/white_ips/#{white_ips(:one).id}", headers: @auth_headers
delete "/repp/v1/white_ips/#{white_ips(:two).id}", headers: @auth_headers
json = JSON.parse(response.body, symbolize_names: true)
assert_response :bad_request
assert_equal json[:code], 2502
assert response.body.include?(Shunter.default_error_message)
ENV['shunter_default_threshold'] = '10000'
ENV['shunter_enabled'] = 'false'
end
end

View file

@ -0,0 +1,39 @@
require 'test_helper'
class ReppV1ApiWhiteIpsListTest < ActionDispatch::IntegrationTest
def setup
@user = users(:api_bestnames)
token = Base64.encode64("#{@user.username}:#{@user.plain_text_password}")
token = "Basic #{token}"
@auth_headers = { 'Authorization' => token }
adapter = ENV['shunter_default_adapter'].constantize.new
adapter&.clear!
end
def test_returns_white_ips
get repp_v1_white_ips_url, headers: @auth_headers
assert_response :success
response_json = JSON.parse(response.body, symbolize_names: true)
assert_equal @user.registrar.white_ips.count, response_json[:data][:count]
assert_equal @user.registrar.white_ips.count, response_json[:data][:ips].length
assert response_json[:data][:ips][0].is_a? Hash
end
def test_returns_error_response_if_throttled
ENV['shunter_default_threshold'] = '1'
ENV['shunter_enabled'] = 'true'
get repp_v1_white_ips_path, headers: @auth_headers
get repp_v1_white_ips_path, headers: @auth_headers
json = JSON.parse(response.body, symbolize_names: true)
assert_response :bad_request
assert_equal json[:code], 2502
assert response.body.include?(Shunter.default_error_message)
ENV['shunter_default_threshold'] = '10000'
ENV['shunter_enabled'] = 'false'
end
end

View file

@ -0,0 +1,54 @@
require 'test_helper'
class ReppV1ApiWhiteIpsShowTest < ActionDispatch::IntegrationTest
def setup
@user = users(:api_bestnames)
token = Base64.encode64("#{@user.username}:#{@user.plain_text_password}")
token = "Basic #{token}"
@auth_headers = { 'Authorization' => token }
adapter = ENV['shunter_default_adapter'].constantize.new
adapter&.clear!
end
def test_returns_error_when_not_found
get repp_v1_white_ip_path(id: 'definitelynotexistant'), headers: @auth_headers
json = JSON.parse(response.body, symbolize_names: true)
assert_response :not_found
assert_equal 2303, json[:code]
assert_equal 'Object does not exist', json[:message]
end
def test_shows_existing_white_ip
white_ip = @user.registrar.white_ips.first
get repp_v1_white_ip_path(id: white_ip.id), headers: @auth_headers
json = JSON.parse(response.body, symbolize_names: true)
assert_response :ok
assert_equal 1000, json[:code]
assert_equal 'Command completed successfully', json[:message]
assert_equal white_ip.id, json[:data][:ip][:id]
assert_equal WhiteIp::INTERFACES, json[:data][:interfaces]
end
def test_returns_error_response_if_throttled
ENV['shunter_default_threshold'] = '1'
ENV['shunter_enabled'] = 'true'
white_ip = @user.registrar.white_ips.first
get repp_v1_white_ip_path(id: white_ip.id), headers: @auth_headers
get repp_v1_white_ip_path(id: white_ip.id), headers: @auth_headers
json = JSON.parse(response.body, symbolize_names: true)
assert_response :bad_request
assert_equal json[:code], 2502
assert response.body.include?(Shunter.default_error_message)
ENV['shunter_default_threshold'] = '10000'
ENV['shunter_enabled'] = 'false'
end
end

View file

@ -0,0 +1,82 @@
require 'test_helper'
class ReppV1ApiWhiteIpsUpdateTest < ActionDispatch::IntegrationTest
def setup
@user = users(:api_bestnames)
@white_ip = white_ips(:one)
token = Base64.encode64("#{@user.username}:#{@user.plain_text_password}")
token = "Basic #{token}"
@auth_headers = { 'Authorization' => token }
adapter = ENV['shunter_default_adapter'].constantize.new
adapter&.clear!
end
def test_updates_white_ip
request_body = {
white_ip: {
ipv4: '127.0.0.1',
},
}
put "/repp/v1/white_ips/#{@white_ip.id}", headers: @auth_headers, params: request_body
json = JSON.parse(response.body, symbolize_names: true)
assert_response :ok
assert_equal 1000, json[:code]
assert_equal 'Command completed successfully', json[:message]
ip = WhiteIp.find(json[:data][:ip][:id])
assert_equal ip.ipv4, @white_ip.ipv4
end
def test_returns_error_if_ipv4_wrong_format
request_body = {
white_ip: {
ipv4: 'wrongip',
},
}
put "/repp/v1/white_ips/#{@white_ip.id}", headers: @auth_headers, params: request_body
json = JSON.parse(response.body, symbolize_names: true)
assert_response :bad_request
assert json[:message].include? 'IPv4 is invalid'
end
def test_returns_error_if_both_ips
request_body = {
white_ip: {
ipv6: '2001:0db8:85a3:0000:0000:8a2e:0370:7334',
},
}
put "/repp/v1/white_ips/#{@white_ip.id}", headers: @auth_headers, params: request_body
json = JSON.parse(response.body, symbolize_names: true)
assert_response :bad_request
assert json[:message].include? 'Please enter only one IP address'
end
def test_returns_error_response_if_throttled
ENV['shunter_default_threshold'] = '1'
ENV['shunter_enabled'] = 'true'
request_body = {
white_ip: {
ipv4: '127.0.0.1',
},
}
put "/repp/v1/white_ips/#{@white_ip.id}", headers: @auth_headers, params: request_body
put "/repp/v1/white_ips/#{@white_ip.id}", headers: @auth_headers, params: request_body
json = JSON.parse(response.body, symbolize_names: true)
assert_response :bad_request
assert_equal json[:code], 2502
assert response.body.include?(Shunter.default_error_message)
ENV['shunter_default_threshold'] = '10000'
ENV['shunter_enabled'] = 'false'
end
end

View file

@ -22,9 +22,26 @@ class ApiUserTest < ActiveSupport::TestCase
assert another_user.invalid? assert another_user.invalid?
another_user.username = 'another' another_user.username = 'another'
another_user.identity_code = ''
assert another_user.valid? assert another_user.valid?
end end
def test_invalid_when_one_registrar_and_identity_code_is_already_taken
user = valid_user
another_user = user.dup
assert another_user.invalid?
another_user.username = 'another'
assert another_user.invalid?
end
def test_valid_when_another_registrar_and_identity_code_is_already_taken
another_user = valid_user
@user.identity_code = another_user.identity_code
assert @user.valid?
end
def test_invalid_without_password def test_invalid_without_password
user = valid_user user = valid_user
user.plain_text_password = '' user.plain_text_password = ''

View file

@ -30,6 +30,7 @@ class WhiteIpTest < ActiveSupport::TestCase
def test_validates_ipv6_format def test_validates_ipv6_format
white_ip = valid_white_ip white_ip = valid_white_ip
white_ip.ipv4 = nil
white_ip.ipv6 = 'invalid' white_ip.ipv6 = 'invalid'
assert white_ip.invalid? assert white_ip.invalid?
@ -47,7 +48,7 @@ class WhiteIpTest < ActiveSupport::TestCase
assert_nothing_raised { white_ip.save } assert_nothing_raised { white_ip.save }
assert white_ip.valid? assert white_ip.valid?
assert WhiteIp.include_ip?(white_ip.ipv6) assert WhiteIp.include_ip?(white_ip.ipv6)
assert_not WhiteIp.include_ip?('192.168.1.1') assert_not WhiteIp.include_ip?('192.168.1.1')
end end

View file

@ -85,6 +85,7 @@ class AdminRegistrarsApiUsersSystemTest < ApplicationSystemTestCase
def unassociated_api_user def unassociated_api_user
new_api_user = users(:api_bestnames).dup new_api_user = users(:api_bestnames).dup
new_api_user.username = "unique-#{rand(100)}" new_api_user.username = "unique-#{rand(100)}"
new_api_user.identity_code = rand(10)
new_api_user.save! new_api_user.save!
new_api_user new_api_user
end end