Skip to content
Open
2 changes: 1 addition & 1 deletion app/controllers/admin/api_users_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def destroy

def api_user_params
params.require(:api_user).permit(:username, :plain_text_password, :active,
:identity_code, { roles: [] })
:subject, { roles: [] })
end

def registrar
Expand Down
2 changes: 1 addition & 1 deletion app/controllers/concerns/error_and_log_handler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def log_request
# rubocop:enable Metrics/MethodLength

def handle_record_not_found
@response = { code: 2303, message: 'Object does not exist' }
@response = { code: 2303, message: I18n.t('repp.object_does_not_exist') }
render(json: @response, status: :not_found)
end

Expand Down
102 changes: 84 additions & 18 deletions app/controllers/eeid/webhooks/identification_requests_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,48 @@ class IdentificationRequestsController < ActionController::Base
def create
return render_unauthorized unless ip_allowed?(request.remote_ip)

contact = Contact.find_by_code(permitted_params[:reference])
return render_invalid_signature unless valid_hmac_signature?(contact.ident_type, request.headers['X-HMAC-Signature'])
entity = resolve_entity
return render_not_found unless entity

poi = catch_poi(contact)
verify_contact(contact)
inform_registrar(contact, poi)
render json: { status: 'success' }, status: :ok
return render_invalid_signature unless valid_hmac_signature?(ident_type_for(entity), request.headers['X-HMAC-Signature'])

::PaperTrail.request(whodunnit: webhook_whodunnit(entity)) do
poi = catch_poi(entity)
process_verification(entity)
inform_registrar(entity, poi)
render json: { status: 'success' }, status: :ok
end
rescue StandardError => e
handle_error(e)
end

private
private

def permitted_params
params.permit(:identification_request_id, :reference, :client_id)
end

def resolve_entity
reference = permitted_params[:reference]
return nil if reference.blank?

Contact.find_by(code: reference) || ApiUser.find_by(uuid: reference)
end

def ident_type_for(entity)
entity.is_a?(ApiUser) ? 'priv' : entity.ident_type
end

def webhook_whodunnit(entity)
identifier = entity.is_a?(ApiUser) ? entity.username : entity.code
"eeid-webhook:#{entity.class.name}:#{identifier}"
end

def render_not_found
Rails.logger.error("Webhook reference not found: #{permitted_params[:reference]}")
render json: { error: 'Reference not found' }, status: :not_found
end

def render_unauthorized
Rails.logger.debug("IPAddress #{request.remote_ip} not authorized")
render json: { error: "IPAddress #{request.remote_ip} not authorized" }, status: :unauthorized
Expand Down Expand Up @@ -63,30 +88,71 @@ def valid_hmac_signature?(ident_type, hmac_signature)
result
end

def verify_contact(contact)
ref = permitted_params[:reference]
if contact&.ident_request_sent_at.present?
contact.update(verified_at: Time.zone.now, verification_id: permitted_params[:identification_request_id])
Rails.logger.info("Contact verified: #{ref}")
def process_verification(entity)
if entity.is_a?(ApiUser)
process_api_user(entity)
else
Rails.logger.error("Valid contact not found for reference: #{ref}")
process_contact(entity)
end
end

def catch_poi(contact)
def process_contact(contact)
ident_service = Eeid::IdentificationService.new(contact.ident_type)
response = ident_service.get_identification_request(permitted_params[:identification_request_id])
result = response[:result] || response['result'] || {}

@contact_outcome = Actions::ProcessContactIdentificationWebhook.new(
contact,
identification_request_id: permitted_params[:identification_request_id],
result: result
).call
end

def process_api_user(api_user)
ident_service = Eeid::IdentificationService.new('priv')
response = ident_service.get_identification_request(permitted_params[:identification_request_id])
result = response[:result] || response['result'] || {}

@api_user_outcome = Actions::ProcessApiUserIdentificationWebhook.new(
api_user,
identification_request_id: permitted_params[:identification_request_id],
result: result
).call
end

def catch_poi(entity)
ident_service = Eeid::IdentificationService.new(ident_type_for(entity))
response = ident_service.get_proof_of_identity(permitted_params[:identification_request_id])
raise StandardError, response[:error] if response[:error].present?

response[:data]
end

def inform_registrar(contact, poi)
email = contact&.registrar&.email
def inform_registrar(entity, poi)
email = entity&.registrar&.email
return unless email

RegistrarMailer.contact_verified(email: email, contact: contact, poi: poi)
.deliver_now
if entity.is_a?(ApiUser)
inform_registrar_api_user(email, entity, poi)
else
inform_registrar_contact(email, entity, poi)
end
end

def inform_registrar_contact(email, contact, poi)
if contact.verification_pending_at.present?
RegistrarMailer.contact_verification_pending(email: email, contact: contact, poi: poi).deliver_now
elsif contact.verified_at.present?
RegistrarMailer.contact_verified(email: email, contact: contact, poi: poi).deliver_now
end
end

def inform_registrar_api_user(email, api_user, poi)
if api_user.verification_pending_at.present?
RegistrarMailer.api_user_verification_pending(email: email, api_user: api_user, poi: poi).deliver_now
elsif api_user.verified_at.present?
RegistrarMailer.api_user_verified(email: email, api_user: api_user, poi: poi).deliver_now
end
end

def ip_allowed?(ip)
Expand Down
68 changes: 65 additions & 3 deletions app/controllers/repp/v1/api_users_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
module Repp
module V1
class ApiUsersController < BaseController
before_action :find_api_user, only: %i[show update destroy]
before_action :find_api_user, only: %i[show update destroy verify download_poi approve_verification reject_verification]
load_and_authorize_resource

THROTTLED_ACTIONS = %i[index show create update destroy].freeze
THROTTLED_ACTIONS = %i[index show create update destroy verify download_poi approve_verification reject_verification].freeze
include Shunter::Integration::Throttle

api :GET, '/repp/v1/api_users'
Expand Down Expand Up @@ -59,6 +59,64 @@ def destroy
render_success
end

api :POST, '/repp/v1/api_users/verify/:id'
desc 'Generate and send identification request to an api user'
def verify
authorize! :verify, ApiUser
action = Actions::ApiUserVerify.new(@api_user)

unless action.call
handle_non_epp_errors(@api_user)
return
end

render_success(data: { api_user: { id: @api_user.id } })
end

api :GET, '/repp/v1/api_users/download_poi/:id'
desc 'Get proof of identity pdf file for an api user'
def download_poi
authorize! :verify, ApiUser
ident_service = Eeid::IdentificationService.new('priv')
response = ident_service.get_proof_of_identity(@api_user.verification_id)

send_data response[:data], filename: "proof_of_identity_#{@api_user.verification_id}.pdf",
type: 'application/pdf', disposition: 'inline'
rescue Eeid::IdentError => e
handle_non_epp_errors(@api_user, e.message)
end

api :POST, '/repp/v1/api_users/approve_verification/:id'
desc 'Manually approve pending api user identification'
def approve_verification
authorize! :verify, ApiUser
action = Actions::ApiUserApproveVerification.new(
@api_user,
subject: approve_verification_params[:subject]
)

unless action.call
handle_non_epp_errors(@api_user)
return
end

render_success(data: { api_user: { id: @api_user.id } })
end

api :POST, '/repp/v1/api_users/reject_verification/:id'
desc 'Reject pending api user identification'
def reject_verification
authorize! :verify, ApiUser
action = Actions::ApiUserRejectVerification.new(@api_user)

unless action.call
handle_non_epp_errors(@api_user)
return
end

render_success(data: { api_user: { id: @api_user.id } })
end

private

def find_api_user
Expand All @@ -67,7 +125,11 @@ def find_api_user

def api_user_params
params.require(:api_user).permit(:username, :plain_text_password, :active,
:identity_code, { roles: [] })
:subject, :email, :identity_code, { roles: [] })
end

def approve_verification_params
params.fetch(:api_user, {}).permit(:subject)
end

def serialized_users(users)
Expand Down
22 changes: 17 additions & 5 deletions app/controllers/repp/v1/base_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ def set_paper_trail_whodunnit
end

def render_success(code: nil, message: nil, data: nil)
@response = { code: code || 1000, message: message || 'Command completed successfully',
@response = { code: code || 1000,
message: message || I18n.t('repp.command_completed_successfully'),
data: data || {} }

render(json: @response, status: :ok)
Expand Down Expand Up @@ -82,17 +83,28 @@ def basic_token
def authenticate_user
username, password = Base64.urlsafe_decode64(basic_token).split(':', 2)
@current_user ||= ApiUser.find_by(username: username, plain_text_password: password)
user_active = @current_user.active?
user_eligible = @current_user&.eligible_for_sign_in?

return if @current_user && user_active
return if @current_user && user_eligible

raise(ArgumentError)
rescue NoMethodError, ArgumentError
@response = { code: 2202, message: 'Invalid authorization information',
data: { username: username, password: password, active: user_active } }
@response = {
code: 2202,
message: authentication_failure_message,
data: { username: username, password: password, eligible_for_sign_in: user_eligible }
}
render(json: @response, status: :unauthorized)
end

def authentication_failure_message
if @current_user&.active? && !@current_user.identity_verified?
I18n.t('registrar.authorization.identity_not_verified')
else
I18n.t('registrar.authorization.invalid_authorization_information')
end
end

def check_api_ip_restriction
return if webclient_request?
return if @current_user.registrar.api_ip_white?(request.ip)
Expand Down
35 changes: 32 additions & 3 deletions app/controllers/repp/v1/contacts_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@
module Repp
module V1
class ContactsController < BaseController # rubocop:disable Metrics/ClassLength
before_action :find_contact, only: %i[show update destroy verify download_poi]
before_action :find_contact, only: %i[show update destroy verify download_poi approve_verification reject_verification]
skip_around_action :log_request, only: %i[search]

THROTTLED_ACTIONS = %i[index check search create show update destroy verify download_poi].freeze
THROTTLED_ACTIONS = %i[index check search create show update destroy verify download_poi
approve_verification reject_verification].freeze
include Shunter::Integration::Throttle

api :get, '/repp/v1/contacts'
Expand Down Expand Up @@ -144,6 +145,34 @@ def download_poi
handle_non_epp_errors(@contact, e.message)
end

api :POST, '/repp/v1/contacts/approve_verification/:contact_code'
desc 'Manually approve pending contact identification'
def approve_verification
authorize! :verify, Epp::Contact
action = Actions::ContactApproveVerification.new(@contact)

unless action.call
handle_non_epp_errors(@contact)
return
end

render_success(data: { contact: { code: @contact.code } })
end

api :POST, '/repp/v1/contacts/reject_verification/:contact_code'
desc 'Reject pending contact identification'
def reject_verification
authorize! :verify, Epp::Contact
action = Actions::ContactRejectVerification.new(@contact)

unless action.call
handle_non_epp_errors(@contact)
return
end

render_success(data: { contact: { code: @contact.code } })
end

private

def index_params
Expand Down Expand Up @@ -206,7 +235,7 @@ def contact_addr_present?
def create_update_success_body
{ code: opt_addr? ? 1100 : nil,
data: { contact: { code: @contact.code } },
message: opt_addr? ? I18n.t('epp.contacts.completed_without_address') : nil }
message: opt_addr? ? I18n.t('epp.contacts.completed_without_address') : I18n.t('repp.command_completed_successfully') }
end

def opt_addr?
Expand Down
2 changes: 1 addition & 1 deletion app/controllers/repp/v1/domains/renews_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ def bulk_renew_domains
next if domain

@epp_errors.add(:epp_errors,
msg: "Object does not exist: #{idn}",
msg: I18n.t('repp.object_does_not_exist_with_name', name: idn),
code: '2304')
end
else
Expand Down
15 changes: 14 additions & 1 deletion app/controllers/repp/v1/registrar/auth_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,31 @@ def index
def tara_callback
user = ApiUser.from_omniauth(auth_params)
response = { code: 401, message: I18n.t(:no_such_user), data: {} }
unless user&.active && webclient_request?
unless user && webclient_request?
render(json: response, status: :unauthorized)
return
end

::PaperTrail.request(whodunnit: eeid_auto_verify_whodunnit(user)) do
auto_verify_from_eeid!(user)
end
token = Base64.urlsafe_encode64("#{user.username}:#{user.plain_text_password}")
data = auth_values_to_data(user, mode: 'accreditation').merge(token: token)
render_success(data: data)
end

private

def eeid_auto_verify_whodunnit(user)
"eeid-auto-verify:#{user.username}"
end

def auto_verify_from_eeid!(user)
return if user.verified_at.present?

user.update!(verified_at: Time.zone.now, verification_pending_at: nil)
end

def auth_params
params.require(:auth).permit(:uid, :new_user_id)
end
Expand Down
Loading