Skip to content

Commit

Permalink
Skip displaying links for disabled routes
Browse files Browse the repository at this point in the history
  • Loading branch information
janko authored and jeremyevans committed Oct 4, 2022
1 parent f1cfcbf commit 067d83e
Show file tree
Hide file tree
Showing 11 changed files with 270 additions and 9 deletions.
7 changes: 6 additions & 1 deletion lib/rodauth/features/login.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,12 @@ module Rodauth
session_key :login_redirect_session_key, :login_redirect

auth_cached_method :multi_phase_login_forms
auth_cached_method :login_form_footer_links
auth_cached_method :login_form_footer

auth_value_methods :login_return_to_requested_location_path

auth_private_methods :login_form_footer_links

internal_request_method
internal_request_method :valid_login_and_password?

Expand Down Expand Up @@ -125,6 +126,10 @@ def login_hidden_field
"<input type='hidden' name=\"#{login_param}\" value=\"#{scope.h param(login_param)}\" />"
end

def login_form_footer_links
@login_form_footer_links ||= _login_form_footer_links.sort.reject { |_, link, _| link.nil? }
end

def render_multi_phase_login_forms
multi_phase_login_forms.sort.map{|_, form, _| form}.join("\n")
end
Expand Down
22 changes: 18 additions & 4 deletions lib/rodauth/features/two_factor_base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,6 @@ module Rodauth
translatable_method :two_factor_disable_link_text, "Remove All Multifactor Authentication Methods"
auth_value_method :two_factor_auth_return_to_requested_location?, false

auth_cached_method :two_factor_auth_links
auth_cached_method :two_factor_setup_links
auth_cached_method :two_factor_remove_links

auth_value_methods :two_factor_modifications_require_password?

auth_methods(
Expand All @@ -57,6 +53,12 @@ module Rodauth
:two_factor_update_session
)

auth_private_methods(
:two_factor_auth_links,
:two_factor_setup_links,
:two_factor_remove_links
)

internal_request_method :two_factor_disable

route(:two_factor_manage, 'multifactor-manage') do |r|
Expand Down Expand Up @@ -206,6 +208,18 @@ def two_factor_remove
nil
end

def two_factor_auth_links
@two_factor_auth_links ||= _two_factor_auth_links.sort.reject { |_, link, _| link.nil? }
end

def two_factor_setup_links
@two_factor_setup_links ||= _two_factor_setup_links.sort.reject { |_, link, _| link.nil? }
end

def two_factor_remove_links
@two_factor_remove_links ||= _two_factor_remove_links.sort.reject { |_, link, _| link.nil? }
end

private

def _two_factor_auth_links
Expand Down
31 changes: 31 additions & 0 deletions spec/confirm_password_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,37 @@
page.body.must_include "Password Authentication Passed: bar"
end

it "should not display confirm password link on login page if route is disabled" do
route = "confirm-password"
rodauth do
enable :login, :confirm_password, :email_auth, :recovery_codes
confirm_password_route { route }
auto_add_recovery_codes? true
after_login { auto_add_missing_recovery_codes }
end
roda do |r|
r.rodauth
r.root{view :content=>"Home"}
end

visit '/login'
fill_in 'Login', with: '[email protected]'
click_button 'Login'
click_button 'Send Login Link Via Email'
link = email_link(/(\/email-auth\?key=.+)$/)

visit link
click_button 'Login'

visit '/multifactor-auth'
click_on 'Enter Password'
page.current_path.must_equal '/confirm-password'

route = nil
visit '/multifactor-auth'
page.current_path.must_equal '/recovery-auth'
end

[:jwt, :json].each do |json|
it "should support confirming passwords via #{json}" do
rodauth do
Expand Down
19 changes: 19 additions & 0 deletions spec/create_account_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,25 @@
page.html.must_include("Logged In: [email protected]")
end

it "should not display create account link on login page if route is disabled" do
route = 'create-account'
rodauth do
enable :create_account, :login
create_account_route { route }
end
roda do |r|
r.rodauth
end

visit '/login'
click_on 'Create a New Account'
page.current_path.must_equal '/create-account'

route = nil
visit '/login'
page.html.wont_include "Create a New Account"
end

[:jwt, :json].each do |json|
it "should support creating accounts via #{json}" do
rodauth do
Expand Down
19 changes: 19 additions & 0 deletions spec/reset_password_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,25 @@ def rodauth.raised_uniqueness_violation(*) StandardError.new; end
proc{click_button 'Request Password Reset'}.must_raise StandardError
end

it "should not display reset password request link on login page if route is disabled" do
route = 'reset-password-request'
rodauth do
enable :login, :reset_password
reset_password_request_route { route }
end
roda do |r|
r.rodauth
end

visit '/login'
click_on 'Forgot Password?'
page.current_path.must_equal '/reset-password-request'

route = nil
visit '/login'
page.html.wont_include "Forgot Password?"
end

[:jwt, :json].each do |json|
it "should support resetting passwords for accounts via #{json}" do
rodauth do
Expand Down
88 changes: 88 additions & 0 deletions spec/two_factor_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1679,6 +1679,94 @@ def reset_otp_last_use
page.html.must_include 'With OTP'
end

it "should not display links for routes that were disabled" do
otp_auth_route = 'otp-auth'
otp_setup_route = 'otp-setup'
otp_disable_route = 'otp-disable'
recovery_auth_route = 'recovery-auth'
recovery_codes_route = 'recovery-codes'
sms_request_route = 'sms-request'
sms_setup_route = 'sms-setup'
sms_disable_route = 'sms-disable'
sms_message = nil
rodauth do
enable :login, :logout, :otp, :recovery_codes, :sms_codes
auto_add_recovery_codes? true
sms_send { |phone, msg| sms_message = msg }
otp_auth_route { otp_auth_route }
otp_setup_route { otp_setup_route }
otp_disable_route { otp_disable_route }
recovery_auth_route { recovery_auth_route }
recovery_codes_route { recovery_codes_route }
sms_request_route { sms_request_route }
sms_setup_route { sms_setup_route }
sms_disable_route { sms_disable_route }
end
roda do |r|
r.rodauth
r.get('auth-links') { rodauth.two_factor_auth_links.map { |link| link[1] }.to_s }
r.get('setup-links') { rodauth.two_factor_setup_links.map { |link| link[1] }.to_s }
r.get('remove-links') { rodauth.two_factor_remove_links.map { |link| link[1] }.to_s }
r.root{view :content=>"Home"}
end

visit '/login'
fill_in 'Login', :with=>"[email protected]"
fill_in 'Password', :with=>"0123456789"
click_on 'Login'
page.find('#notice_flash').text.must_equal "You have been logged in"

otp_setup_route = nil
visit '/setup-links'
page.html.must_equal '[]'

otp_setup_route = 'otp-setup'
visit '/multifactor-auth'
secret = page.html.match(/Secret: ([a-z2-7]{#{secret_length}})/)[1]
totp = ROTP::TOTP.new(secret)
fill_in 'Password', :with=>'0123456789'
fill_in 'Authentication Code', :with=>totp.now
click_on 'Setup TOTP Authentication'
page.find('#notice_flash').text.must_equal 'TOTP authentication is now setup'

recovery_codes_route = nil
sms_setup_route = nil
visit '/setup-links'
page.html.must_equal '[]'

recovery_codes_route = 'recovery-codes'
sms_setup_route = 'sms-setup'
visit '/setup-links'
page.html.must_equal '["/sms-setup", "/recovery-codes"]'

visit '/sms-setup'
fill_in 'Password', :with=>'0123456789'
fill_in 'Phone Number', :with=>'(123) 456-7890'
click_button 'Setup SMS Backup Number'
page.find('#notice_flash').text.must_equal 'SMS authentication needs confirmation'
sms_code = sms_message[/\d{12}\z/]
fill_in 'SMS Code', :with=>sms_code
click_button 'Confirm SMS Backup Number'
page.find('#notice_flash').text.must_equal 'SMS authentication has been setup'

visit '/auth-links'
page.html.must_equal '["/otp-auth", "/sms-request", "/recovery-auth"]'

otp_auth_route = nil
recovery_auth_route = nil
sms_request_route = nil
visit '/auth-links'
page.html.must_equal '[]'

visit '/remove-links'
page.html.must_equal '["/otp-disable", "/sms-disable"]'

otp_disable_route = nil
sms_disable_route = nil
visit '/remove-links'
page.html.must_equal '[]'
end

it "should allow using otp via internal requests" do
rodauth do
enable :login, :logout, :otp, :internal_request
Expand Down
26 changes: 26 additions & 0 deletions spec/verify_account_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,32 @@ def bcrypt_password.==(other)
page.find('#notice_flash').text.must_equal "Your account has been verified"
end

it "should not display verify account resend link on login page when route is disabled" do
route = "verify-account-resend"
rodauth do
enable :login, :create_account, :verify_account
verify_account_resend_route { route }
end
roda do |r|
r.rodauth
r.root{view :content=>"Home"}
end

visit '/create-account'
fill_in 'Login', :with=>'[email protected]'
click_button 'Create Account'
page.find('#notice_flash').text.must_equal "An email has been sent to you with a link to verify your account"
page.current_path.must_equal '/'

Mail::TestMailer.deliveries.clear
visit '/login'
page.html.must_include "Resend Verify Account Information"

route = nil
visit '/login'
page.html.wont_include "Resend Verify Account Information"
end

[:jwt, :json].each do |json|
it "should support verifying accounts via #{json}" do
rodauth do
Expand Down
59 changes: 59 additions & 0 deletions spec/webauthn_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,65 @@
page.find('#error_flash').text.must_equal 'Error authenticating using WebAuthn'
end

it "should not display links for routes that were disabled" do
webauthn_auth_route = 'webauthn-route'
webauthn_setup_route = 'webauthn-setup'
webauthn_remove_route = 'webauthn-remove'
first_request = nil
rodauth do
enable :login, :logout, :webauthn
hmac_secret '123'
webauthn_auth_route { webauthn_auth_route }
webauthn_setup_route { webauthn_setup_route }
webauthn_remove_route { webauthn_remove_route }
end
roda do |r|
first_request = r
r.rodauth
r.get('auth-links') { rodauth.two_factor_auth_links.map { |link| link[1] }.to_s }
r.get('setup-links') { rodauth.two_factor_setup_links.map { |link| link[1] }.to_s }
r.get('remove-links') { rodauth.two_factor_remove_links.map { |link| link[1] }.to_s }
r.root{view :content=>"Home"}
end

login
origin = first_request.base_url
webauthn_client = WebAuthn::FakeClient.new(origin)

webauthn_setup_route = nil
visit '/setup-links'
page.html.must_equal '[]'

webauthn_setup_route = 'webauthn-setup'
visit '/setup-links'
page.html.must_equal '["/webauthn-setup"]'

visit '/webauthn-setup'
challenge = JSON.parse(page.find('#webauthn-setup-form')['data-credential-options'])['challenge']
fill_in 'Password', :with=>'0123456789'
fill_in 'webauthn_setup', :with=>webauthn_client.create(challenge: challenge).to_json
click_button 'Setup WebAuthn Authentication'
page.find('#notice_flash').text.must_equal 'WebAuthn authentication is now setup'
logout
login

webauthn_auth_route = nil
visit '/auth-links'
page.html.must_equal '[]'

webauthn_auth_route = "webauthn-auth"
visit '/auth-links'
page.html.must_equal '["/webauthn-auth"]'

webauthn_remove_route = nil
visit '/remove-links'
page.html.must_equal '[]'

webauthn_remove_route = "webauthn-remove"
visit '/remove-links'
page.html.must_equal '["/webauthn-remove"]'
end

[:jwt, :json].each do |json|
it "should allow webauthn authentication via #{json}" do
rodauth do
Expand Down
2 changes: 1 addition & 1 deletion templates/login-form-footer.str
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#{rodauth.login_form_footer_links_heading}
<ul class="rodauth-links rodauth-login-footer-links">
#{rodauth.login_form_footer_links.sort.map do |_, link, text|
#{rodauth.login_form_footer_links.map do |_, link, text|
"<li><a href=\"#{h link}\">#{h text}</a></li>"
end.join("\n")}
</ul>
2 changes: 1 addition & 1 deletion templates/two-factor-auth.str
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<ul class="rodauth-links rodauth-two-factor-auth-links">
#{rodauth.two_factor_auth_links.sort.map do |_, link, text|
#{rodauth.two_factor_auth_links.map do |_, link, text|
"<li><a href=\"#{h link}\">#{h text}</a></li>"
end.join}
</ul>
4 changes: 2 additions & 2 deletions templates/two-factor-manage.str
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
#{rodauth.two_factor_setup_heading unless rodauth.two_factor_setup_links.empty?}

<ul class="rodauth-links rodauth-multifactor-setup-links">
#{rodauth.two_factor_setup_links.sort.map do |_, link, text|
#{rodauth.two_factor_setup_links.map do |_, link, text|
"<li><a href=\"#{h link}\">#{h text}</a></li>"
end.join("\n")}
</ul>

#{rodauth.two_factor_remove_heading unless rodauth.two_factor_remove_links.empty?}

<ul class="rodauth-links rodauth-multifactor-remove-links">
#{rodauth.two_factor_remove_links.sort.map do |_, link, text|
#{rodauth.two_factor_remove_links.map do |_, link, text|
"<li><a href=\"#{h link}\">#{h text}</a></li>"
end.join("\n")}
#{"<li><a href=\"#{h rodauth.two_factor_disable_path}\">#{rodauth.two_factor_disable_link_text}</a></li>" if rodauth.two_factor_remove_links.length > 1}
Expand Down

0 comments on commit 067d83e

Please sign in to comment.