redirectr 0.0.8 → 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +157 -0
- data/Rakefile +32 -0
- data/app/assets/config/redirectr_manifest.js +2 -0
- data/app/assets/javascripts/redirectr/application.js +13 -0
- data/app/assets/stylesheets/redirectr/application.css +15 -0
- data/app/controllers/redirectr/application_controller.rb +5 -0
- data/app/helpers/redirectr/application_helper.rb +19 -0
- data/app/jobs/redirectr/application_job.rb +4 -0
- data/app/models/redirectr/application_record.rb +5 -0
- data/app/views/layouts/redirectr/application.html.erb +16 -0
- data/config/routes.rb +5 -0
- data/db/migrate/20201120110532_create_redirectr_referrer_tokens.rb +10 -0
- data/lib/redirectr/engine.rb +5 -0
- data/lib/redirectr/version.rb +3 -0
- data/lib/redirectr.rb +233 -50
- data/lib/tasks/redirectr_tasks.rake +4 -0
- metadata +58 -18
- data/README +0 -52
- data/init.rb +0 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 39a819683a6431c465429c1c8cc26ca69025baf7de9733127ba09c3c871362ef
|
4
|
+
data.tar.gz: e9367fbb4d37863273194b4704f32632689420b7c8118240557f6344290ee71a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 19cd53b6e4899399ef7a2f3e2a41c138e4b97fc7b981307d2c947422c2f1a739ec8fe39a4d6792597f040901110aad40fbf095622da45ccb245d6a90e577f3b2
|
7
|
+
data.tar.gz: ec84882c583f6c1e2d585f44b489838009633bf2521847f5c3540bf4f8ef705499e97aaeec55ebef1c71143cf8cf92e021598ad5a40292a0381ea15b38bad606
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2020 Willem van Kerkhof
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,157 @@
|
|
1
|
+
# Redirectr
|
2
|
+
|
3
|
+
In many web applications, the user triggers actions that result in simple or complex workflows that should, after that workflow is finished, result in the user being redirected to the page where he initially started it. Another example would be a "back"-Link on any page.
|
4
|
+
A simple but completely Un-RESTful way would be to store the "current" page in a cookie each time the user calls an action and redirect to the url stored there if needed.
|
5
|
+
|
6
|
+
A much better (and potentially even cacheable) way is to encode the "backlink" URL in an URL parameter or form field and pass it along with every workflow step until the last (or only) action uses it to redirect the user back to where he initially came from.
|
7
|
+
|
8
|
+
Redirectr really does nothing more than provide a simple API for exactly that.
|
9
|
+
|
10
|
+
Redirectr provides a few Controller and Helper methods that will be included in your ApplicationController and ApplicationHelper, respectively.
|
11
|
+
|
12
|
+
## Installation
|
13
|
+
|
14
|
+
when Using bundler, just at the following to your Gemfile
|
15
|
+
|
16
|
+
gem 'redirectr'
|
17
|
+
|
18
|
+
and then call
|
19
|
+
|
20
|
+
bundle install
|
21
|
+
|
22
|
+
## Migrating from 0.1.x to 1.0.0
|
23
|
+
|
24
|
+
Please read this section if you are already using an older version of Redirectr in yout project. Otherwise, you may skip it.
|
25
|
+
|
26
|
+
Pre-1.0 versions of Redirectr automatically included some view helpers (`hidden_referrer_input_tag`, `link_to_back`). This is no longer the case, so please add the following to your `app/helper/application_helper.rb`:
|
27
|
+
|
28
|
+
module ApplicationHelper
|
29
|
+
include Redirectr::ApplicationHelper
|
30
|
+
end
|
31
|
+
|
32
|
+
Please note that methods like `current_path`, `referrer_path` have been removed. Only `current_url`, `referrer_url` exist. Please do also note that the value returned by these methods is not a String containing an URI value anymore. Instead, a Redirectr::ReferrerToken is returned which maps a token to an URI. To get the URI value, call `#to_s` (e.g. when used in a `redirect_to` call). When used as an URL parameter, Rails calls `#to_param` which returns the token.
|
33
|
+
|
34
|
+
Summary:
|
35
|
+
|
36
|
+
# pre-1.0.0:
|
37
|
+
referrer_url.inspect # => 'https://example.com/...'
|
38
|
+
redirect_to referrer_url
|
39
|
+
redirect_to back_or_default
|
40
|
+
|
41
|
+
# post-1.0.0:
|
42
|
+
referrer_url.inspect # => '#<Redirectr::ReferrerToken:... @url="..." @token="...">'
|
43
|
+
redirect_to referrer_url.to_s
|
44
|
+
redirect_to back_or_default.to_s
|
45
|
+
# OR, if you mount Redirectr::Engine in your routes
|
46
|
+
redirect_to referrer_url
|
47
|
+
redirect_to back_or_default
|
48
|
+
|
49
|
+
# pre-1.0.0:
|
50
|
+
link_to 'take me back', back_or_default(my_url)
|
51
|
+
|
52
|
+
# post-1.0.0:
|
53
|
+
link_to 'take me back', back_or_default(my_url).to_s
|
54
|
+
# OR, if you mount Redirectr::Engine in your routes
|
55
|
+
link_to 'take me back', back_or_default(my_url)
|
56
|
+
|
57
|
+
## Examples
|
58
|
+
|
59
|
+
### Contact Form
|
60
|
+
|
61
|
+
Suppose you have an application with a contact form that can be reached via a footer link on every page. After submitting the form, the user should be redirected to the page he was before clicking on the "contact form" link.
|
62
|
+
|
63
|
+
for the footer link to the contact form:
|
64
|
+
|
65
|
+
<%= link_to 'Contact us!', new_contact_path(referrer_param => current_url) %>
|
66
|
+
|
67
|
+
In the 'new contact' view:
|
68
|
+
|
69
|
+
<%= form_for ... do |f| %>
|
70
|
+
<%= hidden_referrer_input_tag %>
|
71
|
+
<!-- ... -->
|
72
|
+
<% end %>
|
73
|
+
|
74
|
+
and finally, in the 'create' action of your ContactsController:
|
75
|
+
|
76
|
+
def create
|
77
|
+
# ...
|
78
|
+
redirect_to back_or_default.to_s
|
79
|
+
end
|
80
|
+
|
81
|
+
### Custom default_url
|
82
|
+
|
83
|
+
The above will redirect the user back to the page specified in the referrer param. However, if you want to provide a custom fallback url per controller in case no referrer param is provided, just define the `#default_url` in your controller:
|
84
|
+
|
85
|
+
class MyController < ApplicationController
|
86
|
+
def default_url
|
87
|
+
if @record
|
88
|
+
my_record_path(@record)
|
89
|
+
else
|
90
|
+
my_record_index_path
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
### Nesting referrers
|
96
|
+
|
97
|
+
Referrer params can be nested, which is helpful if your workflow involves branching into subworkflows. Thus, it is always possible to pass the referrer_param to another url:
|
98
|
+
|
99
|
+
<%= link_to 'go back directly', referrer_or_current_url %>
|
100
|
+
<%= link_to 'add new Foobar before going back', new_foobar_url(:foobar => {:name => 'My Foo'}, referrer_param => referrer_or_current_url) %>
|
101
|
+
|
102
|
+
NOTE: If your URLs include lots of params, it is very advisable to use Referrer Tokens instead of plain URLs to avoud "URI too long" errors. See next section.
|
103
|
+
|
104
|
+
## Unvalidated Redirect Mitigation
|
105
|
+
|
106
|
+
Simply redirecting to an URI provided by HTTP params is considered a security vulnerability (see OWASP cheat sheet https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html). Earlier versions of redirectr did not take any potential issues into account, allowing all kinds of phishing attacs.
|
107
|
+
|
108
|
+
Redirectr offers three kinds of mitigation, two of them being optional:
|
109
|
+
|
110
|
+
* checking the referrer param against a whitelist before performing a redirect (mandatory): by default, the request's host name and port number are allowed and all other hosts are disallowed.
|
111
|
+
* encrypting and signing the referrer URL using the Rails secret key base: makes the referrer param absolutely tamper-proof but requires all services to use the same secret_key_base in a multi-service deployment.
|
112
|
+
* using random tokens instead of referrer URLs and an token-to-URL lookup service. This leaves you with the additional side effect of also having an URL shortener.
|
113
|
+
|
114
|
+
### Using the whitelist
|
115
|
+
|
116
|
+
By default, Redirectr checks the protocol, hostname and port of the referrer against the corresponding values of the current request. You may add your own:
|
117
|
+
|
118
|
+
YourApp::Application.configure do
|
119
|
+
config.x.redirectr.whitelist = %w( http://localhost:3000 https://my.host.com )
|
120
|
+
end
|
121
|
+
|
122
|
+
### Token instead of URL (URL-shortener)
|
123
|
+
|
124
|
+
Instead of using a URL in the referrer token, redirectr can act as an URL shortener that maps random tokens to URLs. This requires a storage_implementation to be defined:
|
125
|
+
|
126
|
+
YourApp::Application.configure do
|
127
|
+
config.x.redirectr.use_referrer_token = true
|
128
|
+
config.x.redirectr.reuse_tokens = true # set to false to generate a new token for each and every link
|
129
|
+
config.x.redirectr.storage_implementation = Redirectr::ReferrerToken::ActiveRecordStorage
|
130
|
+
end
|
131
|
+
|
132
|
+
This example requires a table named 'redirectr_referrer_tokens' to be present with two columns: `url` and `token`. To install and apply the required schema migration, run:
|
133
|
+
|
134
|
+
bundle exec rails redirectr:install:migrations
|
135
|
+
bundle exec rails db:migrate
|
136
|
+
|
137
|
+
Redirectr::ReferrerToken has two representations: #to_s displays the URL and #to_param its tokenized form. Depending on your config, this can be either a random token, an encrypted URL or the plaintext URL.
|
138
|
+
|
139
|
+
## Contributions
|
140
|
+
|
141
|
+
Contributions like bugfixes and new ideas are more than welcome. Please just fork this project on github (https://github.com/wvk/redirectr) and send me a pull request with your changes.
|
142
|
+
|
143
|
+
Thanks so far to:
|
144
|
+
|
145
|
+
* Falk Hoppe for Rails 2.3.x interoperability
|
146
|
+
* Dimitar Haralanov for Rails 3.0.x interoperability
|
147
|
+
* Raffael Schmid for spotting a typo in the gemspec description ;)
|
148
|
+
|
149
|
+
## Changelog
|
150
|
+
|
151
|
+
* 1.0.0: Validate Redirect urls against whitelist; Allow Token instead of URL referrer param
|
152
|
+
* 0.1.1: deprecate *_path methods; improve Rails 5 compatibility by removing `alias` in view helpers
|
153
|
+
* 0.1.0: Use absolute URI instead of path in current_path method
|
154
|
+
* 0.0.8: Use ActiveSupport::Concern (Thanks to Dimitar Haralanov)
|
155
|
+
* 0.0.7: Add Rails 3.0 compatibility (Thanks to Falk Hoppe)
|
156
|
+
|
157
|
+
Copyright (c) 2010 Willem van Kerkhof <wvk@consolving.de>, released under the MIT license
|
data/Rakefile
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rdoc/task'
|
8
|
+
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
11
|
+
rdoc.title = 'Redirectr'
|
12
|
+
rdoc.options << '--line-numbers'
|
13
|
+
rdoc.rdoc_files.include('README.md')
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
|
+
end
|
16
|
+
|
17
|
+
APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
|
18
|
+
load 'rails/tasks/engine.rake'
|
19
|
+
|
20
|
+
load 'rails/tasks/statistics.rake'
|
21
|
+
|
22
|
+
require 'bundler/gem_tasks'
|
23
|
+
|
24
|
+
require 'rake/testtask'
|
25
|
+
|
26
|
+
Rake::TestTask.new(:test) do |t|
|
27
|
+
t.libs << 'test'
|
28
|
+
t.pattern = 'test/**/*_test.rb'
|
29
|
+
t.verbose = false
|
30
|
+
end
|
31
|
+
|
32
|
+
task default: :test
|
@@ -0,0 +1,13 @@
|
|
1
|
+
// This is a manifest file that'll be compiled into application.js, which will include all the files
|
2
|
+
// listed below.
|
3
|
+
//
|
4
|
+
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
|
5
|
+
// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
|
6
|
+
//
|
7
|
+
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
8
|
+
// compiled file. JavaScript code in this file should be added after the last require_* statement.
|
9
|
+
//
|
10
|
+
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
|
11
|
+
// about supported directives.
|
12
|
+
//
|
13
|
+
//= require_tree .
|
@@ -0,0 +1,15 @@
|
|
1
|
+
/*
|
2
|
+
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
3
|
+
* listed below.
|
4
|
+
*
|
5
|
+
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
6
|
+
* or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
|
7
|
+
*
|
8
|
+
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
|
9
|
+
* compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
|
10
|
+
* files in this directory. Styles in this file should be added after the last require_* statement.
|
11
|
+
* It is generally better to create a new file per style scope.
|
12
|
+
*
|
13
|
+
*= require_tree .
|
14
|
+
*= require_self
|
15
|
+
*/
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Redirectr
|
2
|
+
module ApplicationHelper
|
3
|
+
# Create a link back to the path specified in the referrer-param.
|
4
|
+
# title can be either a text string or anything else like an image.
|
5
|
+
# Remember to call #html_safe on the title argument if it contains
|
6
|
+
# HTML and you are using Rails >=3.
|
7
|
+
def link_to_back(title, options = {})
|
8
|
+
link_to title, back_or_default.to_s, options.merge(rel: 'back')
|
9
|
+
end
|
10
|
+
|
11
|
+
# Create a hidden input field containing the referrer or current path.
|
12
|
+
# Handy for use in forms that are called with a referrer param which
|
13
|
+
# has to be passed on and respected by the form processing action.
|
14
|
+
def hidden_referrer_input_tag(options = {})
|
15
|
+
hidden_field_tag :referrer, referrer_or_current_url.to_param, options
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>Redirectr</title>
|
5
|
+
<%= csrf_meta_tags %>
|
6
|
+
<%= csp_meta_tag %>
|
7
|
+
|
8
|
+
<%= stylesheet_link_tag "redirectr/application", media: "all" %>
|
9
|
+
<%= javascript_include_tag "redirectr/application" %>
|
10
|
+
</head>
|
11
|
+
<body>
|
12
|
+
|
13
|
+
<%= yield %>
|
14
|
+
|
15
|
+
</body>
|
16
|
+
</html>
|
data/config/routes.rb
ADDED
data/lib/redirectr.rb
CHANGED
@@ -1,15 +1,160 @@
|
|
1
|
+
require 'redirectr/engine'
|
2
|
+
require 'securerandom'
|
3
|
+
|
4
|
+
def ReferrerToken(url, token=nil)
|
5
|
+
case url
|
6
|
+
when Redirectr::ReferrerToken
|
7
|
+
url
|
8
|
+
when nil
|
9
|
+
nil
|
10
|
+
else
|
11
|
+
Redirectr::ReferrerToken.new(url, token).tap do |rt|
|
12
|
+
#puts "Token for #{url}: #{rt.token}"
|
13
|
+
rt.save
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
$referrer_lookup ||= {}
|
19
|
+
|
1
20
|
module Redirectr
|
2
21
|
REFERRER_PARAM_NAME = :referrer
|
3
22
|
|
23
|
+
class UrlNotInWhitelist < ArgumentError
|
24
|
+
end
|
25
|
+
|
26
|
+
class InvalidReferrerToken < ArgumentError
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.config
|
30
|
+
Rails.configuration.x.redirectr
|
31
|
+
end
|
32
|
+
|
33
|
+
class ReferrerToken
|
34
|
+
extend ActiveModel::Naming
|
35
|
+
|
36
|
+
# this is a stora implementation for activerecord-
|
37
|
+
class ActiveRecordStorage < ActiveRecord::Base
|
38
|
+
validates_presence_of :url, :token
|
39
|
+
|
40
|
+
self.table_name = :redirectr_referrer_tokens
|
41
|
+
|
42
|
+
class << self
|
43
|
+
def store(record)
|
44
|
+
self.find_or_create_by url: record.url, token: record.token
|
45
|
+
record.url
|
46
|
+
end
|
47
|
+
|
48
|
+
def fetch(token)
|
49
|
+
url = self.find_by(token: token)&.url
|
50
|
+
ReferrerToken(url) if url
|
51
|
+
end
|
52
|
+
|
53
|
+
def token_for_url(url)
|
54
|
+
self.find_by(url: url)&.token
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
class GlobalVarStorage
|
60
|
+
extend ActiveModel::Naming
|
61
|
+
|
62
|
+
def persisted?
|
63
|
+
false
|
64
|
+
end
|
65
|
+
|
66
|
+
class << self
|
67
|
+
def store(record)
|
68
|
+
$referrer_lookup[record.token] = record.url
|
69
|
+
end
|
70
|
+
|
71
|
+
def fetch(token)
|
72
|
+
ReferrerToken($referrer_lookup[token])
|
73
|
+
end
|
74
|
+
|
75
|
+
def token_for_url(url)
|
76
|
+
$referrer_lookup.key(url)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
attr_reader :url, :token
|
82
|
+
|
83
|
+
def initialize(url, token=nil)
|
84
|
+
@url = url
|
85
|
+
@token = token
|
86
|
+
|
87
|
+
if Redirectr.config.use_referrer_token
|
88
|
+
if Redirectr.config.storage_implementation.nil?
|
89
|
+
raise "Missing storage implementation for referrer tokens! please define config.x.redirectr.storage_implementation"
|
90
|
+
end
|
91
|
+
|
92
|
+
if Redirectr.config.reuse_tokens
|
93
|
+
@token ||= Redirectr.config.storage_implementation.token_for_url(url)
|
94
|
+
end
|
95
|
+
@token ||= SecureRandom.hex(16)
|
96
|
+
elsif Redirectr.config.encrypt_referrer
|
97
|
+
@token ||= self.class.cryptr.encrypt_and_sign url
|
98
|
+
else
|
99
|
+
@token ||= url
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def to_param
|
104
|
+
@token
|
105
|
+
end
|
106
|
+
|
107
|
+
def to_model
|
108
|
+
self
|
109
|
+
end
|
110
|
+
|
111
|
+
def to_s
|
112
|
+
@url
|
113
|
+
end
|
114
|
+
|
115
|
+
def persisted?
|
116
|
+
true
|
117
|
+
end
|
118
|
+
|
119
|
+
def save
|
120
|
+
if Redirectr.config.use_referrer_token
|
121
|
+
Redirectr.config.storage_implementation.store self
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def self.from_param(param)
|
126
|
+
if Redirectr.config.encrypt_referrer
|
127
|
+
ReferrerToken.new self.class.cryptr.decrypt_and_verify param
|
128
|
+
elsif Redirectr.config.use_referrer_token
|
129
|
+
self.lookup(param)
|
130
|
+
else
|
131
|
+
ReferrerToken.new param
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def self.lookup(token)
|
136
|
+
Redirectr.config.storage_implementation.fetch token
|
137
|
+
end
|
138
|
+
|
139
|
+
private
|
140
|
+
|
141
|
+
def self.cryptr
|
142
|
+
@cryptr ||= ActiveSupport::MessageEncryptor.new(Rails.application.secrets.secret_key_base)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
|
4
147
|
module ControllerMethods
|
5
148
|
extend ActiveSupport::Concern
|
6
149
|
|
7
150
|
included do
|
8
|
-
helper_method :
|
9
|
-
:
|
151
|
+
helper_method :current_url,
|
152
|
+
:referrer_or_current_url,
|
10
153
|
:back_or_default,
|
11
|
-
:
|
12
|
-
:referrer_param
|
154
|
+
:referrer_url,
|
155
|
+
:referrer_param,
|
156
|
+
:redirectr_referrer_token_url,
|
157
|
+
:redirectr_referrer_token_path
|
13
158
|
end
|
14
159
|
|
15
160
|
module ClassMethods
|
@@ -23,34 +168,47 @@ module Redirectr
|
|
23
168
|
# end
|
24
169
|
#
|
25
170
|
def use_referrer_param_name(new_name)
|
26
|
-
|
171
|
+
Redirectr.remove_constant :REFERRER_PARAM_NAME
|
172
|
+
Redirectr.const_set :REFERRER_PARAM_NAME, new_name.to_sym
|
27
173
|
end
|
28
174
|
end
|
29
175
|
|
176
|
+
def redirectr_referrer_token_url(rt)
|
177
|
+
rt.to_s
|
178
|
+
end
|
179
|
+
|
180
|
+
def redirectr_referrer_token_path(rt)
|
181
|
+
rt.to_s
|
182
|
+
end
|
30
183
|
|
31
184
|
# Return the name of the parameter used to pass the referrer path.
|
32
185
|
# Use this instead of the real param name in creating your own links
|
33
186
|
# to allow easily changing the name later
|
34
187
|
# Example:
|
35
188
|
#
|
36
|
-
# <%= link_to
|
189
|
+
# <%= link_to my_messages_url :filter_by => 'date', referrer_param => current_url %>
|
37
190
|
#
|
38
191
|
def referrer_param
|
39
192
|
Redirectr::REFERRER_PARAM_NAME
|
40
193
|
end
|
41
194
|
|
42
|
-
# Return the
|
43
|
-
#
|
44
|
-
# thus allowing you to navigate
|
45
|
-
#
|
46
|
-
# in your controller.
|
195
|
+
# Return the complete URL of the current request.
|
196
|
+
# Note that this does include ALL query parameters and the host name,
|
197
|
+
# thus allowing you to navigate back and forth between different hosts. If you
|
198
|
+
# want the pre-0.1.0 behaviour back, just overwrite this method
|
199
|
+
# in your controller so it returns "request.env['PATH_INFO']".
|
47
200
|
# Example:
|
48
201
|
#
|
49
|
-
# <%= link_to
|
202
|
+
# <%= link_to my_messages_url referrer_param => current_url %>
|
50
203
|
#
|
51
|
-
def
|
52
|
-
|
53
|
-
|
204
|
+
def current_url
|
205
|
+
if request.respond_to? :url # for rack >= 2.0.0
|
206
|
+
ReferrerToken(request.url)
|
207
|
+
elsif request.respond_to? :original_url # for rails >= 4.0.0
|
208
|
+
ReferrerToken(request.original_url)
|
209
|
+
else
|
210
|
+
ReferrerToken(request.env['REQUEST_URI'])
|
211
|
+
end
|
54
212
|
end
|
55
213
|
|
56
214
|
# Return the referrer or the current path, it the former is not set.
|
@@ -59,28 +217,28 @@ module Redirectr
|
|
59
217
|
# redirected back to.
|
60
218
|
# Example:
|
61
219
|
#
|
62
|
-
# <%= link_to
|
220
|
+
# <%= link_to my_messages_url referrer_param => referrer_or_current_url %>
|
63
221
|
#
|
64
|
-
def
|
65
|
-
|
222
|
+
def referrer_or_current_url
|
223
|
+
referrer_url.blank? ? current_url : referrer_url
|
66
224
|
end
|
67
225
|
|
68
226
|
# Used in back links, referrer based redirection after actions etc.
|
69
227
|
# Accepts a default redirect path in case no param[referrer_param]
|
70
|
-
# is set, default being
|
228
|
+
# is set, default being root_url.
|
71
229
|
# To set an own default path (per controller), you can overwrite
|
72
|
-
# the
|
230
|
+
# the default_url method (see below).
|
73
231
|
# Example:
|
74
232
|
#
|
75
233
|
# class MyController
|
76
234
|
# def create
|
77
235
|
# @my = My.create(...)
|
78
|
-
# redirect_to back_or_default(
|
236
|
+
# redirect_to back_or_default(my_url)
|
79
237
|
# end
|
80
238
|
# end
|
81
239
|
#
|
82
|
-
# The above example will redirect to the
|
83
|
-
# it will redirect to the
|
240
|
+
# The above example will redirect to the referrer_url if it is defined, otherwise
|
241
|
+
# it will redirect to the my_url
|
84
242
|
#
|
85
243
|
# Example:
|
86
244
|
#
|
@@ -91,47 +249,72 @@ module Redirectr
|
|
91
249
|
# end
|
92
250
|
# end
|
93
251
|
#
|
94
|
-
# The above example will redirect to the
|
95
|
-
# it will redirect to the
|
252
|
+
# The above example will redirect to the referrer_url if it is defined, otherwise
|
253
|
+
# it will redirect to the root_url of the application.
|
96
254
|
def back_or_default(default = nil)
|
97
|
-
|
98
|
-
|
255
|
+
if self.referrer_url.present?
|
256
|
+
self.referrer_url
|
99
257
|
else
|
100
|
-
default
|
258
|
+
case default
|
259
|
+
when nil
|
260
|
+
ReferrerToken(self.default_url)
|
261
|
+
when String
|
262
|
+
ReferrerToken(default)
|
263
|
+
else
|
264
|
+
ReferrerToken(url_for(default))
|
265
|
+
end
|
101
266
|
end
|
102
267
|
end
|
103
268
|
|
104
269
|
# to be overwritten by your controllers
|
105
|
-
def
|
106
|
-
|
270
|
+
def default_url
|
271
|
+
root_url
|
107
272
|
end
|
108
273
|
|
109
|
-
#
|
110
|
-
def
|
111
|
-
params[referrer_param]
|
274
|
+
# reads referrer_param from HTTP params and validates it against the whitelist
|
275
|
+
def referrer_url
|
276
|
+
return nil if params[referrer_param].blank?
|
277
|
+
|
278
|
+
referrer_token = ReferrerToken.from_param params[referrer_param]
|
279
|
+
raise Redirectr::InvalidReferrerToken, "no URL matches given token value #{params[referrer_param]}" if referrer_token.blank?
|
280
|
+
|
281
|
+
parsed_url = URI.parse referrer_token.to_s
|
282
|
+
if parsed_url.absolute? and in_whitelist? parsed_url
|
283
|
+
referrer_token
|
284
|
+
elsif parsed_url.relative?
|
285
|
+
referrer_token
|
286
|
+
else
|
287
|
+
raise Redirectr::UrlNotInWhitelist, "#{parsed_url.inspect} - #{redirect_whitelist.inspect}"
|
288
|
+
end
|
112
289
|
end
|
113
|
-
end
|
114
290
|
|
115
|
-
|
116
|
-
|
291
|
+
def redirect_to_with_whitelist(redirect_url)
|
292
|
+
case redirect_url
|
293
|
+
when nil
|
294
|
+
raise 'Cannot redirect to nil'
|
295
|
+
when String
|
296
|
+
parsed_url = URI.parse(redirect_url)
|
297
|
+
if parsed_url.relative? or in_whitelist? parsed_url
|
298
|
+
redirect_to parsed_url
|
299
|
+
else
|
300
|
+
raise Redirectr::UrlNotInWhitelist, "#{parsed_url.inspect} - #{redirect_whitelist.inspect}"
|
301
|
+
end
|
302
|
+
else
|
303
|
+
redirect_to default
|
304
|
+
end
|
305
|
+
end
|
117
306
|
|
118
|
-
|
119
|
-
|
120
|
-
# Remember to call #html_safe on the title argument if it contains
|
121
|
-
# HTML and you are using Rails 3.
|
122
|
-
def link_to_back(title, options = {})
|
123
|
-
link_to title, back_or_default, options
|
307
|
+
def in_whitelist?(parsed_url)
|
308
|
+
redirect_whitelist.find {|url| parsed_url.host == url.host and parsed_url.port == url.port }
|
124
309
|
end
|
125
310
|
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
def hidden_referrer_input_tag(options = {})
|
130
|
-
hidden_field_tag :referrer, referrer_or_current_path, options
|
311
|
+
def redirect_whitelist
|
312
|
+
@redirect_whitelist ||= [URI.parse(self.current_url.to_s)] +
|
313
|
+
Array(Redirectr.config.whitelist).map {|url| URI.parse url.to_s }
|
131
314
|
end
|
132
|
-
end
|
315
|
+
end
|
316
|
+
|
133
317
|
end # module Redirectr
|
134
318
|
|
135
319
|
ActionController::Base.send :include, Redirectr::ControllerMethods
|
136
|
-
|
137
|
-
ActionView::Base.send :include, Redirectr::Helpers
|
320
|
+
|
metadata
CHANGED
@@ -1,49 +1,89 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: redirectr
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
5
|
-
prerelease:
|
4
|
+
version: 1.0.2
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Willem van Kerkhof
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date:
|
13
|
-
dependencies:
|
11
|
+
date: 2022-09-13 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '5.2'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '5.2'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: sqlite3
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
14
41
|
description: Provides Rails-helper methods for referrer-style backlinks and setting
|
15
|
-
redirect URLs after form
|
16
|
-
email:
|
42
|
+
redirect URLs after form submission
|
43
|
+
email:
|
44
|
+
- wvk@consolving.de
|
17
45
|
executables: []
|
18
46
|
extensions: []
|
19
47
|
extra_rdoc_files: []
|
20
48
|
files:
|
21
|
-
-
|
22
|
-
-
|
49
|
+
- MIT-LICENSE
|
50
|
+
- README.md
|
51
|
+
- Rakefile
|
52
|
+
- app/assets/config/redirectr_manifest.js
|
53
|
+
- app/assets/javascripts/redirectr/application.js
|
54
|
+
- app/assets/stylesheets/redirectr/application.css
|
55
|
+
- app/controllers/redirectr/application_controller.rb
|
56
|
+
- app/helpers/redirectr/application_helper.rb
|
57
|
+
- app/jobs/redirectr/application_job.rb
|
58
|
+
- app/models/redirectr/application_record.rb
|
59
|
+
- app/views/layouts/redirectr/application.html.erb
|
60
|
+
- config/routes.rb
|
61
|
+
- db/migrate/20201120110532_create_redirectr_referrer_tokens.rb
|
23
62
|
- lib/redirectr.rb
|
63
|
+
- lib/redirectr/engine.rb
|
64
|
+
- lib/redirectr/version.rb
|
65
|
+
- lib/tasks/redirectr_tasks.rake
|
24
66
|
homepage: http://github.com/wvk/redirectr
|
25
|
-
licenses:
|
67
|
+
licenses:
|
68
|
+
- MIT
|
69
|
+
metadata: {}
|
26
70
|
post_install_message:
|
27
71
|
rdoc_options: []
|
28
72
|
require_paths:
|
29
73
|
- lib
|
30
74
|
required_ruby_version: !ruby/object:Gem::Requirement
|
31
|
-
none: false
|
32
75
|
requirements:
|
33
|
-
- -
|
76
|
+
- - ">="
|
34
77
|
- !ruby/object:Gem::Version
|
35
78
|
version: '0'
|
36
79
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
37
|
-
none: false
|
38
80
|
requirements:
|
39
|
-
- -
|
81
|
+
- - ">="
|
40
82
|
- !ruby/object:Gem::Version
|
41
83
|
version: '0'
|
42
84
|
requirements: []
|
43
|
-
|
44
|
-
rubygems_version: 1.8.23
|
85
|
+
rubygems_version: 3.2.27
|
45
86
|
signing_key:
|
46
|
-
specification_version:
|
47
|
-
summary:
|
87
|
+
specification_version: 4
|
88
|
+
summary: Rails referrer-URL handling done right
|
48
89
|
test_files: []
|
49
|
-
has_rdoc:
|
data/README
DELETED
@@ -1,52 +0,0 @@
|
|
1
|
-
# Redirectr
|
2
|
-
|
3
|
-
In many web applications, the user triggers actions that result in simple or complex workflows that should, after that workflow is finished, result in the user being redirected to the page where he initially started it. Another example would be a "back"-Link on any page.
|
4
|
-
A simple but completely Un-RESTful way would be to store the "current" page in a cookie each time the user calls an action and redirect to the url stored there if needed.
|
5
|
-
|
6
|
-
A much better (and potentially even cacheable) way is to encode the "backlink" URL in an URL parameter or form field and pass it along with every workflow step until the last (or only) action uses it to redirect the user back to where he initially came from.
|
7
|
-
|
8
|
-
Redirectr really does nothing more than provide a simple API for exactly that.
|
9
|
-
|
10
|
-
Redirectr provides a few Controller and Helper methods that will be included in your ApplicationController and ApplicationHelper, respectively.
|
11
|
-
|
12
|
-
## Installation
|
13
|
-
|
14
|
-
With Rails 3, just at the following to your Gemfile
|
15
|
-
|
16
|
-
gem 'redirectr', :git => 'https://github.com/wvk/redirectr.git'
|
17
|
-
|
18
|
-
and then call
|
19
|
-
|
20
|
-
bundle install
|
21
|
-
|
22
|
-
## Example
|
23
|
-
|
24
|
-
Suppose you have an application with a contact form that can be reached via a footer link on every page. After submitting the form, the user should be redirected to the page he was before clicking on the "contact form" link.
|
25
|
-
|
26
|
-
for the footer link to the contact form:
|
27
|
-
|
28
|
-
<%= link_to 'Contact us!', new_contact_path(referrer_param => current_path) %>
|
29
|
-
|
30
|
-
In the 'new contact' view:
|
31
|
-
|
32
|
-
<%= form for ... do |f| %>
|
33
|
-
<%= hidden_referrer_input_tag %>
|
34
|
-
<!-- ... -->
|
35
|
-
<% end %>
|
36
|
-
|
37
|
-
and finally, in the 'create' action of your ContactsController:
|
38
|
-
|
39
|
-
def create
|
40
|
-
# ...
|
41
|
-
redirect_to back_or_default
|
42
|
-
end
|
43
|
-
|
44
|
-
for more detailled examples, see the Rdoc documentation.
|
45
|
-
|
46
|
-
|
47
|
-
## Changelog
|
48
|
-
|
49
|
-
0.0.8: Use ActiveSupport::Concern (Thanks to Dimitar Haralanov)
|
50
|
-
0.0.7: Add Rails 3.0 compatibility (Thanks to Falk Hoppe)
|
51
|
-
|
52
|
-
Copyright (c) 2010 Willem van Kerkhof <wvk@consolving.de>, released under the MIT license
|
data/init.rb
DELETED
File without changes
|