diff --git a/app/api/repp/contact_v1.rb b/app/api/repp/contact_v1.rb deleted file mode 100644 index 85065f5a0..000000000 --- a/app/api/repp/contact_v1.rb +++ /dev/null @@ -1,149 +0,0 @@ -module Repp - class ContactV1 < Grape::API - version 'v1', using: :path - - resource :contacts do - desc 'Return list of contact' - params do - optional :limit, type: Integer, values: (1..200).to_a, desc: 'How many contacts to show' - optional :offset, type: Integer, desc: 'Contact number to start at' - optional :details, type: String, values: %w(true false), desc: 'Whether to include details' - end - - get '/' do - limit = params[:limit] || 200 - offset = params[:offset] || 0 - - if params[:details] == 'true' - contacts = current_user.registrar.contacts.limit(limit).offset(offset) - - unless Contact.address_processing? - attributes = Contact.attribute_names - Contact.address_attribute_names - contacts = contacts.select(attributes) - end - else - contacts = current_user.registrar.contacts.limit(limit).offset(offset).pluck(:code) - end - - @response = { - contacts: contacts, - total_number_of_records: current_user.registrar.contacts.count - } - end - - desc 'Creates a new contact object' - params do - requires :contact, type: Hash, allow_blank: false do - # Contact info - requires :name, type: String, desc: 'Full name of contact' - requires :phone, type: String, - desc: 'Phone number of contact. In format of +country_prefix.number' - requires :email, type: String, desc: 'Email address of contact' - optional :fax, type: String, allow_blank: true, desc: 'Fax number of contact' - - # Ident - requires :ident, type: Hash do - requires :ident, type: String, allow_blank: false, - desc: 'Government identifier of contact' - requires :ident_type, type: String, allow_blank: false, desc: 'Type of contact ident' - requires :ident_country_code, type: String, allow_blank: false, - desc: 'Ident country code' - end - - # Physical address - optional :addr, type: Hash do - requires :country_code, type: String, allow_blank: false, desc: 'Address country' - requires :street, type: String, allow_blank: false, desc: 'Address street' - requires :city, type: String, allow_blank: false, desc: 'Address city' - requires :zip, type: String, allow_blank: false, desc: 'Address ZIP' - end - end - - # Legal document - optional :legal_document, type: Hash, allow_blank: false do - requires :body, type: String, desc: 'Raw data of legal document' - requires :type, type: String, desc: 'Format of legal document' - end - end - - post '/' do - @legal_doc = params[:legal_documents] - @contact_params = params[:contact] - - # Ident object - @ident = @contact_params[:ident] - @contact_params.delete(:ident) - - # Address - address_present = params[:contact][:addr].keys.any? - - %w[city street zip country_code].each { |k| @contact_params[k] = @contact_params[:addr][k] } - @contact_params.delete(:addr) - - @contact = Epp::Contact.new(@contact_params, current_user.registrar, epp: false) - - action = Actions::ContactCreate.new(@contact, @legal_doc, @ident) - - if action.call - if !Contact.address_processing? && address_present - @response_code = 1100 - @response_description = I18n.t('epp.contacts.completed_without_address') - else - @response_code = 1000 - @response_description = I18n.t('epp.contacts.completed') - end - - @response = { code: @response_code, - description: @response_description, - data: { contact: { id: @contact.code } } } - else - status(:bad_request) - @response = { errors: @contact.errors } - end - end - - desc 'Update contact properties' - params do - requires :contact, type: Hash, allow_blank: false do - optional :ident, type: Hash, allow_blank: false do - requires :ident, type: String, desc: 'Government identifier of contact' - requires :ident_type, type: String, desc: 'Type of contact ident' - requires :ident_country_code, type: String, desc: 'Ident country code' - end - optional :name, type: String, desc: 'Full name of contact' - optional :country_code, type: String, desc: 'Address country' - optional :phone, type: String, - desc: 'Phone number of contact. In format of +country_prefix.number' - optional :email, type: String, desc: 'Email address of contact' - optional :fax, type: String, desc: 'Fax number of contact' - optional :street, type: String, desc: 'Address street' - optional :city, type: String, desc: 'Address city' - optional :zip, type: String, desc: 'Address ZIP' - end - optional :legal_document, type: Hash, allow_blank: false do - requires :body, type: String, desc: 'Raw data of legal document' - requires :type, type: String, desc: 'Format of legal document' - end - end - - put '/:code' do - @contact = current_user.registrar.contacts.find_by(code: params[:code]) - (status(:not_found) && return) unless @contact - - @new_params = params[:contact] - @legal_doc = params[:legal_document] - @ident = params[:contact][:ident] || {} - - action = Actions::ContactUpdate.new(@contact, @new_params, - @legal_doc, @ident, current_user) - - if action.call - @response = {} - else - status(:bad_request) - @response = { errors: @contact.errors } - end - end - end - end -end diff --git a/app/controllers/repp/v1/base_controller.rb b/app/controllers/repp/v1/base_controller.rb new file mode 100644 index 000000000..30e37b9f7 --- /dev/null +++ b/app/controllers/repp/v1/base_controller.rb @@ -0,0 +1,82 @@ +module Repp + module V1 + class BaseController < ActionController::API + rescue_from ActiveRecord::RecordNotFound, with: :not_found_error + before_action :authenticate_user + before_action :check_ip_restriction + + attr_reader :current_user + + rescue_from ActionController::ParameterMissing do |exception| + render json: { code: 2003, message: exception }, status: :bad_request + end + + private + + def epp_errors + @errors ||= [] + end + + def handle_errors(obj = nil, update: false) + @errors ||= [] + + if obj + obj.construct_epp_errors + @errors += obj.errors[:epp_errors] + end + + if update + @errors.each_with_index do |errors, index| + if errors[:code] == '2304' && + errors[:value].present? && + errors[:value][:val] == DomainStatus::SERVER_DELETE_PROHIBITED && + errors[:value][:obj] == 'status' + @errors[index][:value][:val] = DomainStatus::PENDING_UPDATE + end + end + end + + @errors.uniq! + + render_epp_error + end + + def render_epp_error + render(json: { code: @errors[0][:code], message: @errors[0][:msg] }, status: :bad_request) + end + + def ip_whitelisted? + return false unless @api_user.registrar.api_ip_white?(request.ip) + end + + def basic_token + pattern = /^Basic / + header = request.headers['Authorization'] + header.gsub(pattern, '') if header&.match(pattern) + end + + def authenticate_user + username, password = Base64.urlsafe_decode64(basic_token).split(':') + @current_user ||= ApiUser.find_by(username: username, plain_text_password: password) + + return if @current_user + + render(json: { errors: [{ base: ['Not authorized'] }] }, status: :unauthorized) + end + + def check_ip_restriction + ip_restriction = Authorization::RestrictedIP.new(request.ip) + allowed = ip_restriction.can_access_registrar_area?(@current_user.registrar) + + return if allowed + + flash[:alert] = t('registrar.authorization.ip_not_allowed', ip: request.ip) + render(json: { errors: [{ base: [I18n.t('registrar.authorization.ip_not_allowed', ip: request.ip)] }] }, status: :unauthorized) + end + + def not_found_error + render(json: { code: 2303, message: 'Object does not exist' }, status: :not_found) + end + end + end +end diff --git a/app/controllers/repp/v1/contacts_controller.rb b/app/controllers/repp/v1/contacts_controller.rb new file mode 100644 index 000000000..70bf91297 --- /dev/null +++ b/app/controllers/repp/v1/contacts_controller.rb @@ -0,0 +1,111 @@ +module Repp + module V1 + class ContactsController < BaseController + before_action :find_contact, only: [:update] + + ## GET /repp/v1/contacts + def index + limit = params[:limit] || 200 + offset = params[:offset] || 0 + + record_count = current_user.registrar.contacts.count + contacts = current_user.registrar.contacts.limit(limit).offset(offset) + + unless Contact.address_processing? && params[:details] == 'true' + contacts = contacts.select(Contact.attribute_names - Contact.address_attribute_names) + end + + contacts = contacts.pluck(:code) unless params[:details] + resp = { contacts: contacts, total_number_of_records: record_count } + render(json: resp, status: :ok) + end + + ## POST /repp/v1/contacts + def create + @legal_doc = params[:legal_documents] + @contact_params = contact_create_params + @ident = contact_ident_params + address_present = contact_addr_params.keys.any? + %w[city street zip country_code].each { |k| @contact_params[k] = contact_addr_params[k] } + + @contact = Epp::Contact.new(@contact_params, current_user.registrar, epp: false) + + action = Actions::ContactCreate.new(@contact, @legal_doc, @ident) + + if action.call + if !Contact.address_processing? && address_present + @response_code = 1100 + @response_description = I18n.t('epp.contacts.completed_without_address') + else + @response_code = 1000 + @response_description = I18n.t('epp.contacts.completed') + end + + render(json: { code: @response_code, + message: @response_description, + data: { contact: { id: @contact.code } } }, + status: :created) + else + handle_errors(@contact) + end + end + + ## PUT /repp/v1/contacts/1 + def update + @update = contact_create_params + %w[city street zip country_code].each { |k| @new_params[k] = contact_addr_params[k] } + + @legal_doc = params[:legal_document] + @ident = contact_ident_params || {} + address_present = contact_addr_params.keys.any? + action = Actions::ContactUpdate.new(@contact, @update, @legal_doc, @ident, current_user) + + if action.call + if !Contact.address_processing? && address_present + @response_code = 1100 + @response_description = I18n.t('epp.contacts.completed_without_address') + else + @response_code = 1000 + @response_description = I18n.t('epp.contacts.completed') + end + + render(json: { code: @response_code, + message: @response_description, + data: { contact: { id: @contact.code } } }, + status: :ok) + else + handle_errors(@contact) + end + end + + def find_contact + code = params[:id] + @contact = Epp::Contact.find_by!(code: code) + end + + def contact_create_params + params.require(:contact).require(%i[name email phone]) + params.require(:contact).permit(:name, :email, :phone) + end + + def contact_ident_params + params.require(:contact).require(:ident).require(%i[ident ident_type ident_country_code]) + params.require(:contact).require(:ident).permit(:ident, :ident_type, :ident_country_code) + end + + def contact_addr_params + if Contact.address_processing? + params.require(:contact).require(:addr).require(%i[country_code city street zip]) + params.require(:contact).require(:addr).permit(:country_code, :city, :street, :zip) + else + params.require(:contact).permit(addr: %i[country_code city street zip]) + end + end + + def legal_document_params + params.require(:legal_document).require(%i[body type]) + params.require(:legal_document).permit(:body, :type) + end + end + end +end diff --git a/config/routes.rb b/config/routes.rb index 41f857bc8..afad7b947 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -37,10 +37,9 @@ Rails.application.routes.draw do get 'error/:command', to: 'errors#error' end - mount Repp::API => '/' - namespace :repp do namespace :v1 do + resources :contacts resources :auctions, only: %i[index] resources :retained_domains, only: %i[index] end