|
1 | 1 | class SettingsController < ApplicationController |
2 | 2 | before_filter :require_logged_in_user |
3 | 3 |
|
| 4 | + TOTP_SESSION_TIMEOUT = (60 * 15) |
| 5 | + |
4 | 6 | def index |
5 | 7 | @title = "Account Settings" |
6 | 8 |
|
@@ -74,6 +76,88 @@ def update |
74 | 76 | render :action => "index" |
75 | 77 | end |
76 | 78 |
|
| 79 | + def twofa |
| 80 | + @title = "Two-Factor Authentication" |
| 81 | + end |
| 82 | + |
| 83 | + def twofa_auth |
| 84 | + if @user.authenticate(params[:user][:password].to_s) |
| 85 | + session[:last_authed] = Time.now.to_i |
| 86 | + session.delete(:totp_secret) |
| 87 | + |
| 88 | + if @user.has_2fa? |
| 89 | + @user.disable_2fa! |
| 90 | + flash[:success] = "Two-Factor Authentication has been disabled." |
| 91 | + return redirect_to "/settings" |
| 92 | + else |
| 93 | + return redirect_to twofa_enroll_url |
| 94 | + end |
| 95 | + else |
| 96 | + flash[:error] = "Your password was not correct." |
| 97 | + return redirect_to twofa_url |
| 98 | + end |
| 99 | + end |
| 100 | + |
| 101 | + def twofa_enroll |
| 102 | + @title = "Two-Factor Authentication" |
| 103 | + |
| 104 | + if (Time.now.to_i - session[:last_authed].to_i) > TOTP_SESSION_TIMEOUT |
| 105 | + flash[:error] = "Your enrollment period timed out." |
| 106 | + return redirect_to twofa_url |
| 107 | + end |
| 108 | + |
| 109 | + if !session[:totp_secret] |
| 110 | + session[:totp_secret] = ROTP::Base32.random_base32 |
| 111 | + end |
| 112 | + |
| 113 | + totp = ROTP::TOTP.new(session[:totp_secret], |
| 114 | + :issuer => Rails.application.name) |
| 115 | + totp_url = totp.provisioning_uri(@user.email) |
| 116 | + |
| 117 | + # no option for inline svg, so just strip off leading <?xml> tag |
| 118 | + qrcode = RQRCode::QRCode.new(totp_url) |
| 119 | + qr = qrcode.as_svg(:offset => 0, color: "000", :module_size => 5, |
| 120 | + :shape_rendering => "crispEdges").gsub(/^<\?xml.*>/, "") |
| 121 | + |
| 122 | + @qr_svg = "<a href=\"#{totp_url}\">#{qr}</a>" |
| 123 | + end |
| 124 | + |
| 125 | + def twofa_verify |
| 126 | + @title = "Two-Factor Authentication" |
| 127 | + |
| 128 | + if ((Time.now.to_i - session[:last_authed].to_i) > TOTP_SESSION_TIMEOUT) || |
| 129 | + !session[:totp_secret] |
| 130 | + flash[:error] = "Your enrollment period timed out." |
| 131 | + return redirect_to twofa_url |
| 132 | + end |
| 133 | + end |
| 134 | + |
| 135 | + def twofa_update |
| 136 | + if ((Time.now.to_i - session[:last_authed].to_i) > TOTP_SESSION_TIMEOUT) || |
| 137 | + !session[:totp_secret] |
| 138 | + flash[:error] = "Your enrollment period timed out." |
| 139 | + return redirect_to twofa_url |
| 140 | + end |
| 141 | + |
| 142 | + @user.totp_secret = session[:totp_secret] |
| 143 | + if @user.authenticate_totp(params[:totp_code]) |
| 144 | + # re-roll, just in case |
| 145 | + @user.session_token = nil |
| 146 | + @user.save! |
| 147 | + |
| 148 | + session[:u] = @user.session_token |
| 149 | + |
| 150 | + flash[:success] = "Two-Factor Authentication has been enabled on " << |
| 151 | + "your account." |
| 152 | + session.delete(:totp_secret) |
| 153 | + return redirect_to "/settings" |
| 154 | + else |
| 155 | + flash[:error] = "Your TOTP code was invalid, please verify the " << |
| 156 | + "current code in your TOTP application." |
| 157 | + return redirect_to twofa_verify_url |
| 158 | + end |
| 159 | + end |
| 160 | + |
77 | 161 | private |
78 | 162 |
|
79 | 163 | def user_params |
|
0 commit comments