Merge pull request #648 from metamaps/feature/load-url-title-hack
hack to load link titles when pulling a url into a map
This commit is contained in:
commit
fb563c6eed
8 changed files with 123 additions and 0 deletions
1
Gemfile
1
Gemfile
|
@ -23,6 +23,7 @@ gem 'pg'
|
|||
gem 'pundit'
|
||||
gem 'pundit_extra'
|
||||
gem 'rack-cors'
|
||||
gem 'rack-attack'
|
||||
gem 'redis'
|
||||
gem 'slack-notifier'
|
||||
gem 'snorlax'
|
||||
|
|
|
@ -177,6 +177,8 @@ GEM
|
|||
activesupport (>= 3.0.0)
|
||||
pundit_extra (0.3.0)
|
||||
rack (2.0.1)
|
||||
rack-attack (5.0.1)
|
||||
rack
|
||||
rack-cors (0.4.0)
|
||||
rack-test (0.6.3)
|
||||
rack (>= 1.0)
|
||||
|
@ -316,6 +318,7 @@ DEPENDENCIES
|
|||
pry-rails
|
||||
pundit
|
||||
pundit_extra
|
||||
rack-attack
|
||||
rack-cors
|
||||
rails (~> 5.0.0)
|
||||
rails3-jquery-autocomplete
|
||||
|
|
37
app/controllers/hacks_controller.rb
Normal file
37
app/controllers/hacks_controller.rb
Normal file
|
@ -0,0 +1,37 @@
|
|||
# bad code that should be checked over before entering one of the
|
||||
# nice files from the right side of this repo
|
||||
class HacksController < ApplicationController
|
||||
include ActionView::Helpers::TextHelper # string truncate method
|
||||
|
||||
# rate limited by rack-attack - currently 5r/s
|
||||
# TODO: what else can we do to make get_with_redirects safer?
|
||||
def load_url_title
|
||||
authorize :Hack
|
||||
url = params[:url]
|
||||
response, url = get_with_redirects(url)
|
||||
title = get_encoded_title(response)
|
||||
render json: { success: true, title: title, url: url }
|
||||
rescue StandardError => e
|
||||
render json: { success: false }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def get_with_redirects(url)
|
||||
uri = URI.parse(url)
|
||||
response = Net::HTTP.get_response(uri)
|
||||
while response.code == '301'
|
||||
uri = URI.parse(response['location'])
|
||||
response = Net::HTTP.get_response(uri)
|
||||
end
|
||||
[response, uri.to_s]
|
||||
end
|
||||
|
||||
def get_encoded_title(http_response)
|
||||
title = http_response.body.sub(/.*<title>(.*)<\/title>.*/m, '\1')
|
||||
charset = http_response['content-type'].sub(/.*charset=(.*);?.*/, '\1')
|
||||
charset = nil if charset == 'text/html'
|
||||
title = title.force_encoding(charset) if charset
|
||||
truncate(title, length: 140)
|
||||
end
|
||||
end
|
5
app/policies/hack_policy.rb
Normal file
5
app/policies/hack_policy.rb
Normal file
|
@ -0,0 +1,5 @@
|
|||
class HackPolicy < ApplicationPolicy
|
||||
def load_url_title?
|
||||
true
|
||||
end
|
||||
end
|
|
@ -26,6 +26,8 @@ module Metamaps
|
|||
Doorkeeper::ApplicationController.helper ApplicationHelper
|
||||
end
|
||||
|
||||
config.middleware.use Rack::Attack
|
||||
|
||||
# Configure sensitive parameters which will be filtered from the log file.
|
||||
config.filter_parameters += [:password]
|
||||
|
||||
|
|
59
config/initializers/rack-attack.rb
Normal file
59
config/initializers/rack-attack.rb
Normal file
|
@ -0,0 +1,59 @@
|
|||
class Rack::Attack
|
||||
Rack::Attack.cache.store = ActiveSupport::Cache::MemoryStore.new
|
||||
|
||||
# Throttle all requests by IP (60rpm)
|
||||
#
|
||||
# Key: "rack::attack:#{Time.now.to_i/:period}:req/ip:#{req.ip}"
|
||||
throttle('req/ip', :limit => 300, :period => 5.minutes) do |req|
|
||||
req.ip # unless req.path.start_with?('/assets')
|
||||
end
|
||||
|
||||
# Throttle POST requests to /login by IP address
|
||||
#
|
||||
# Key: "rack::attack:#{Time.now.to_i/:period}:logins/ip:#{req.ip}"
|
||||
throttle('logins/ip', :limit => 5, :period => 20.seconds) do |req|
|
||||
if req.path == '/login' && req.post?
|
||||
req.ip
|
||||
end
|
||||
end
|
||||
|
||||
# Throttle POST requests to /login by email param
|
||||
#
|
||||
# Key: "rack::attack:#{Time.now.to_i/:period}:logins/email:#{req.email}"
|
||||
#
|
||||
# Note: This creates a problem where a malicious user could intentionally
|
||||
# throttle logins for another user and force their login requests to be
|
||||
# denied, but that's not very common and shouldn't happen to you. (Knock
|
||||
# on wood!)
|
||||
throttle("logins/email", :limit => 5, :period => 20.seconds) do |req|
|
||||
if req.path == '/login' && req.post?
|
||||
# return the email if present, nil otherwise
|
||||
req.params['email'].presence
|
||||
end
|
||||
end
|
||||
|
||||
throttle('load_url_title/req/ip', :limit => 5, :period => 1.second) do |req|
|
||||
# If the return value is truthy, the cache key for the return value
|
||||
# is incremented and compared with the limit. In this case:
|
||||
# "rack::attack:#{Time.now.to_i/1.second}:load_url_title/req/ip:#{req.ip}"
|
||||
#
|
||||
# If falsy, the cache key is neither incremented nor checked.
|
||||
|
||||
req.ip if req.path == 'hacks/load_url_title'
|
||||
end
|
||||
|
||||
self.throttled_response = lambda do |env|
|
||||
now = Time.now
|
||||
match_data = env['rack.attack.match_data']
|
||||
period = match_data[:period]
|
||||
limit = match_data[:limit]
|
||||
|
||||
headers = {
|
||||
'X-RateLimit-Limit' => limit.to_s,
|
||||
'X-RateLimit-Remaining' => '0',
|
||||
'X-RateLimit-Reset' => (now + (period - now.to_i % period)).to_s
|
||||
}
|
||||
|
||||
[429, headers, ['']]
|
||||
end
|
||||
end
|
|
@ -80,4 +80,8 @@ Metamaps::Application.routes.draw do
|
|||
get 'users/:id/details', to: 'users#details', as: :details
|
||||
post 'user/updatemetacodes', to: 'users#updatemetacodes', as: :updatemetacodes
|
||||
resources :users, except: [:index, :destroy]
|
||||
|
||||
namespace :hacks do
|
||||
get 'load_url_title'
|
||||
end
|
||||
end
|
||||
|
|
|
@ -88,6 +88,18 @@ const PasteInput = {
|
|||
import_id,
|
||||
{
|
||||
success: function(topic) {
|
||||
$.get('/hacks/load_url_title', {
|
||||
url: text
|
||||
}, function success(data, textStatus) {
|
||||
var selector = '#showcard #topic_' + topic.get('id') + ' .best_in_place'
|
||||
if ($(selector).find('form').length > 0) {
|
||||
$(selector).find('textarea, input').val(data.title)
|
||||
} else {
|
||||
$(selector).html(data.title)
|
||||
}
|
||||
topic.set('name', data.title)
|
||||
topic.save()
|
||||
})
|
||||
TopicCard.showCard(topic.get('node'), function() {
|
||||
$('#showcard #titleActivator').click()
|
||||
.find('textarea, input').focus()
|
||||
|
|
Loading…
Reference in a new issue