hi i got existing project within a model (let call at a) that call a model (let call at b) i need from the the last model a.k.a get the ip of the client the issue is i kinda bound with restrictions.
the way the model beed add if from include mean it not for reall passed from controller
one of the restrictions is make the few changes as possible
there for my question is simple let say i need get the ip not from the controller but from the model and it not save anywhere so what way i can get the ip.
p.s i did try request.remote_ip but it don't know request
if possible can u show me link or example code so i will understand how to do.
You can do the following for getting the remote ip of the client, this is using a controller.
class TestsController < ApplicationController
def get_ip
ip = request.remote_ip
Test.use_ip(ip)
end
end
Assuming you have a model. I'm assuming it as Test
class Test < ActiveRecord::Base
def self.use_ip(ip)
puts ip
end
end
As per your requirement, which is going against convention of Rails (which is not a very good practice)
define the following in application_controller.rb
before_filter :pass_request_around
def pass_request_around
Thread.current[:request] = request
end
In model, request object should be available now
def get_ip
request = Thread.current[:request]
ip = request.remote_ip
end
I have a model called booking and I am using active admin plugin as a resource management. I have a form called booking in the backend in which I need to insert ip address and username of the currently logged in user(who insert the form data) to the database without making any input field in backend. Please suggest. I am using mysql as database platform.
here is my controller syntax
class CustomizedBookingsController < ApplicationController
def create
customized_booking = CustomizedBooking.new(params[:customized_booking])
customized_booking.booked_by = current_admin_user
customized_booking.booking_ip = request.remote_ip
end
end
I tried lots but still not working.
If you're using Devise for authentication, you can use the method current_user, see the documentation here: https://github.com/plataformatec/devise#controller-filters-and-helpers
To add the IP address, you have the ip method, part of the ActionDispatch::Request. Check the documentation here: http://api.rubyonrails.org/classes/ActionDispatch/Request.html#method-i-ip
So if your booking model has both user and ip fields/methods, you can set them in the create action in your BookingsController
def create
booking = Booking.new(booking_params)
booking.user = current_user
booking.ip = request.ip
end
My application controller looks like:
class ApplicationController
before_action :set_customer
def customer?
#customer.nil?
end
private
def set_customer
if request.host != "example.com"
#customer = Customer.find_by_domain("...")
else
if request.subdomain != "www"
#customer = Customer.find_by_sub("...")
end
end
end
end
How can I make tests for the ApplicationController?
I want to pass in URLS and then check if #customer is nil etc.
e.g URLS to test
example.com
www.example.com
google.com
hello.example.com
You shouldn't test instance variables. Move the code to a service object if needed.
assigns is going to be deprecated in Rails 5, so will be probably removed at some point. In controller tests, you want to check that the response is what you expect (response code) based on your user (authentication) and authorization, and possibly that the correct format is used.
In a different test (a "view" test, I mean the integration tests) you check also that the output in the view contains some value based on your instance variable.
In this case you can simply test that the service calls Customer.find_by_sub (or by_domain) based on your clause, and that's all you need to test, instance variable shouldn't be something you care about.
I have a User model with an "ip" attribute. I want to save the user's IP address when they sign up in the User model.
The only problem is that it seems like when I make a method in the User model:
def set_ip
self.ip = request.remote_ip
end
I get an error message saying "request" doesn't exist, so it doesn't look like it exists in the model.
Is there any way to set an IP address in the model or do I have to do this in the controller?
request is a controller method so you can't call it from inside a model. Why not add the IP to params before you create your user? Something like this:
params[:user][:ip] = request.ip
#user = User.create(params[:user])
I've a rails app and I'm trying to overload the request.remote_ip and request.ip in order to use the cloudflare header (HTTP_CF_CONNECTING_IP) if it's present...
I've tried these, but none of them work:
module Rack
class Request
class << self
def ip
#ip ||= (#env['HTTP_CF_CONNECTING_IP'] || super)
end
end
end
end
module ActionDispatch
class Request < Rack::Request
class << self
def remote_ip
#remote_ip ||= (#env['HTTP_CF_CONNECTING_IP'] || super)
end
end
end
end
I can't use an extra method like
def connecting_ip
#env['HTTP_CF_CONNECTING_IP'] || request.remote_ip
end
in the application_controller because I've some other gems (like devise) which use request.ip
Thank you!
I believe request is an instance. But you are defining class methods. Remove the class << self nonsense and you instead be redefining instance methods.
Just a note though, this sounds kind of crazy. Be careful in there. Frameworks dont always like having their guts rearranged.
Your error message when using instance methods means something else is going on. super calls the superclass implementation. But when you reopen a class and override things, you are literally overwriting the original implementation. Since the method doesn't exist in the superclass, super doesn't work.
Instead you can use alias to save the original implementation before you declare the new method that would replace it.
module ActionDispatch
class Request < Rack::Request
alias :remote_ip_orig :remote_ip
def remote_ip
#remote_ip ||= (#env['HTTP_CF_CONNECTING_IP'] || remote_ip_orig)
end
end
end
If you you want to use CloudFlare and detect the true IP like I did without an Ngingx or Apache module, this monkey patching approach is a really bad idea, this will lead to unexpected results in the future. You'd be much better off using a Middleware as it should be used. Here is one that I came up with and have implemented.
module Rack
class CloudFlareFixup
def initialize(app)
#app = app
end
def call(env)
if env['HTTP_CF_CONNECTING_IP']
env['HTTP_X_FORWARDED_FOR'] = env['HTTP_CF_CONNECTING_IP']
env['REMOTE_ADDR'] = env['HTTP_CF_CONNECTING_IP']
end
#app.call(env)
end
end
end
Simply add this into your application.rb
config.middleware.insert_before(0, Rack::CloudFlareFixup)
You can see the complete Gist for this at https://gist.github.com/mattheworiordan/9024372
I tried many solutions to this problem. Here is what I found:
cloudflare-rails gem => this sets both ip and remote_ip to the originating ip. I wanted ip to represent the cloudflare ip that it came from, and remote_ip to represent the originating ip of the user, so that wouldn't work for me. It operates by looking up the list of Cloudflare IP ranges every 12 hours and adding those to trusted_proxies. It then patches: - ActionDispatch::Request.ip, ActionDispatch::Request.remote_ip and Rack::Attack::Request.trusted_proxy? with the cloudflare trusted proxies so that ip and remote_ip ignore them and use the originating ip of the user that initiated the request.
actionpack-cloudflare gem => this is much simpler. all it does is directly set ActionDispatch.remote_ip = #env['HTTP_CF_CONNECTING_IP'] which is in accordance with Cloudflare best recommended best practice, but it didn't seem worth it to use a gem for 1 line of code.
Hand Rolled
# config/initializers/cloudflare.rb
# These values should rarely or never change and Cloudflare should alert us before that happens.
# The list of Cloudflare proxy server ip ranges comes from https://www.cloudflare.com/ips/
CLOUDFLARE_IP_RANGES = [IPAddr.new("103.21.244.0/22"),IPAddr.new("103.22.200.0/22"),IPAddr.new("103.31.4.0/22"),IPAddr.new("104.16.0.0/12"),IPAddr.new("108.162.192.0/18"),IPAddr.new("131.0.72.0/22"),IPAddr.new("141.101.64.0/18"),IPAddr.new("162.158.0.0/15"),IPAddr.new("172.64.0.0/13"),IPAddr.new("173.245.48.0/20"),IPAddr.new("188.114.96.0/20"),IPAddr.new("190.93.240.0/20"),IPAddr.new("197.234.240.0/22"),IPAddr.new("2405:8100::/32"),IPAddr.new("2405:b500::/32"),IPAddr.new("2606:4700::/32"),IPAddr.new("2803:f800::/32"),IPAddr.new("2a06:98c0::/29"),IPAddr.new("2c0f:f248::/32")
]
# By adding the cloudflare IP ranges as trusted_proxies, rails ignores those IPs when setting remote_ip and correctly sets it to the originating IP
Rails.application.config.action_dispatch.trusted_proxies = CLOUDFLARE_IP_RANGES + ActionDispatch::RemoteIp::TRUSTED_PROXIES
Lastly, I wanted to block non-proxied requests
# config/initializers/rack_attack.rb
class Rack::Attack
class Request < ::Rack::Request
# Create a remote_ip method for rack request by setting it equal to the cloudflare connecting ip header
# To restore original visitor IP addresses at your origin web server, Cloudflare recommends your logs or applications
# look at CF-Connecting-IP instead of X-Forwarded-For since CF-Connecting-IP has a consistent format containing only one IP.
# https://support.cloudflare.com/hc/en-us/articles/200170986-How-does-CloudFlare-handle-HTTP-Request-headers-
def remote_ip
#remote_ip ||= (env['HTTP_CF_CONNECTING_IP'] || ip).to_s
end
# This checks the request IP against Cloudflare IP ranges and the action dispatch default trusted proxies.
# These include various local IPs like 127.0.0.1 so that local requests won't be blocked.
def proxied?
Rails.application.config.action_dispatch.trusted_proxies.any? { |range| range === ip }
end
end
# Block all requests coming from non-Cloudflare IPs
blocklist("block non-proxied requests in production") do |req|
if req.proxied?
false
else
req.log :warn
true
end
end
end