Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
birger
users
Commits
dddbd9b6
Commit
dddbd9b6
authored
May 27, 2019
by
mh
Browse files
add the activate your totp flow & make password mandotary when setting a recovery email
parent
740d28f8
Changes
9
Hide whitespace changes
Inline
Side-by-side
app/assets/javascripts/application.js
View file @
dddbd9b6
...
...
@@ -35,3 +35,11 @@ function mainOnLoad() {
}
}
window
.
addEventListener
(
"
load
"
,
mainOnLoad
)
function
toggle_visibility
(
id
)
{
var
e
=
document
.
getElementById
(
id
);
if
(
e
.
style
.
display
==
'
block
'
)
e
.
style
.
display
=
'
none
'
;
else
e
.
style
.
display
=
'
block
'
;
}
app/controllers/concerns/api_backend.rb
View file @
dddbd9b6
...
...
@@ -221,23 +221,27 @@ module ApiBackend
post
([
'users'
,
'enable_mail_crypt'
],
{
"password"
=>
pw
})
end
def
enabl
e_totp
(
name
)
post
([
'users'
,
'
enabl
e_totp'
],
{
'name'
=>
name
})
def
creat
e_totp
(
name
,
password
)
post
([
'users'
,
'
creat
e_totp'
],
{
'name'
=>
name
,
'password'
=>
password
})
end
def
get_totp_names
get
([
'users'
,
'totp_names'
])
end
def
delete_totp
(
name
)
post
([
'users'
,
'delete_totp'
],
{
'name'
=>
name
})
def
verify_totp
(
name
,
totp
)
post
([
'users'
,
'verify_totp'
],
{
'name'
=>
name
,
'totp'
=>
totp
})
end
def
delete_totp
(
name
,
pw
)
post
([
'users'
,
'delete_totp'
],
{
'name'
=>
name
,
'password'
=>
pw
})
end
def
mail_crypt_token
(
pw
)
post
([
'users'
,
'generate_recovery_token'
],
{
"password"
=>
pw
})
end
def
set_recovery_email
(
recovery
,
email_token
,
pass
=
nil
)
def
set_recovery_email
(
recovery
,
email_token
,
pass
)
post
([
'users'
,
'set_recovery_email'
],
{
"recovery_email"
=>
recovery
,
'email_recovery_token'
=>
email_token
,
'password'
=>
pass
})
end
...
...
app/controllers/tfa_controller.rb
View file @
dddbd9b6
...
...
@@ -6,15 +6,15 @@ class TfaController < ApplicationController
def
show
fetch_existing_2fa
end
def
enabl
e
def
creat
e
if
params
[
:name
]
begin
res
=
api
.
enabl
e_totp
(
params
[
:name
])
res
=
api
.
creat
e_totp
(
params
[
:name
]
,
params
[
:password
]
)
totp
=
ROTP
::
TOTP
.
new
(
res
[
'secret'
],
issuer:
"immerda.ch"
)
url
=
totp
.
provisioning_uri
(
current_user
)
@totp_qr
=
RQRCode
::
QRCode
.
new
(
url
,
:size
=>
12
,
:level
=>
:h
)
@totp_secret
=
res
[
'secret'
]
fetch_existing_2fa
@
new_
totp_qr
=
RQRCode
::
QRCode
.
new
(
url
,
:size
=>
12
,
:level
=>
:h
)
@
new_
totp_secret
=
res
[
'secret'
]
@new_totp_name
=
res
[
'name'
]
flash
[
:notice
]
=
nil
rescue
ApiBackend
::
ApiError
flash
[
:notice
]
=
:failed
...
...
@@ -22,13 +22,14 @@ class TfaController < ApplicationController
else
flash
[
:notice
]
=
t
(
:name_must_be_set
)
end
fetch_existing_2fa
render
'show'
end
def
delete_totp
if
params
[
'name'
].
present?
begin
api
.
delete_totp
(
params
[
'name'
])
api
.
delete_totp
(
params
[
'name'
]
,
params
[
'password'
]
)
flash
[
:notice
]
=
:success
rescue
ApiBackend
::
ApiError
flash
[
:notice
]
=
:failed
...
...
@@ -39,6 +40,19 @@ class TfaController < ApplicationController
redirect_to
tfa_path
end
def
verify_totp
if
params
[
'name'
].
present?
begin
api
.
verify_totp
(
params
[
:name
],
params
[
:totp
])
rescue
ApiBackend
::
ApiError
flash
[
:notice
]
=
:failed
end
else
flash
[
:notice
]
=
t
(
:name_must_be_set
)
end
redirect_to
tfa_path
end
private
def
fetch_existing_2fa
res
=
api
.
get_totp_names
...
...
app/views/recovery_email/show.html.erb
View file @
dddbd9b6
...
...
@@ -17,20 +17,12 @@
type=
"checkbox"
id=
"email_recovery_token"
name=
"email_recovery_token"
<%=
if
@email_recovery_token
then
'checked="checked"'
else
''
end
%>
onclick=
"document.getElementById('show-pass').style.display = (document.getElementById('email_recovery_token').checked ? 'block' : 'none');"
/>
<%=
t
(
:backup_recovery_token_email
)
%>
<br
/>
<div
id=
"show-pass"
<%
unless
@email_recovery_token
%>
style=
"display:none"
<%
end
%>
>
<%=
label_tag
(
:pass
,
(
t
:password_for_recovery_token
))
%>
<%=
password_field_tag
(
:pass
)
%>
</div>
<%
end
%>
<br
/>
<%=
label_tag
(
:pass
,
t
(
:your_main_pw
))
%><%=
password_field_tag
(
:password
,
nil
,
placeholder:
t
(
:enter_your_current
_pw
))
%>
<br
/>
<%=
submit_tag
(
recovery_email_set?
?
t
(
:overwrite
)
:
t
(
:submit
))
%>
<%=
link_to
t
(
:back
),
root_path
%>
...
...
app/views/tfa/_new_totp.html.erb
View file @
dddbd9b6
<p>
<h4>
<%=
t
(
:your_new_totp_token
)
%>
</h4>
<div
class=
"qr-field"
>
<%=
raw
(
@totp_qr
.
as_html
)
%>
</div>
<div
class=
"qr-field"
>
<%=
raw
(
@
new_
totp_qr
.
as_html
)
%>
</div>
<br/>
Secret:
<%=
@totp_secret
%>
Secret:
<%=
@new_totp_secret
%>
</p>
<p>
<h4>
<%=
t
(
:activate_your_totp
)
%>
</h4>
<%=
t
(
:activate_your_totp_long
)
%>
<br/>
<%=
form_tag
(
"/tfa/
#{
URI
.
encode
(
@new_totp_name
)
}
/verify_totp"
,
method:
"post"
)
do
%>
<%=
label_tag
(
:totp
,
t
(
:totp
))
%><%=
text_field_tag
(
:totp
,
nil
,
autofocus:
true
,
autocomplete: :off
)
%>
<%=
submit_tag
t
(
:verify
)
%>
<%
end
-%>
</p>
app/views/tfa/show.html.erb
View file @
dddbd9b6
<h3>
<%=
@page_title
=
t
(
:tfa_enable
)
%>
</h3>
<p>
<%=
t
(
:status
)
%>
:
<%=
@existing_totps
.
present
?
?
"<b>
#{
t
(
:activated
)
}
</b>"
.
html_safe
:
t
(
:disabled
)
%>
<%=
t
(
:status
)
%>
:
<%=
@existing_totps
.
values
.
any
?
?
"<b>
#{
t
(
:activated
)
}
</b>"
.
html_safe
:
t
(
:disabled
)
%>
</p>
<%
if
@totp_qr
-%>
<%
if
@
new_
totp_qr
-%>
<%=
render
'new_totp'
%>
<%=
link_to
t
(
:back
),
tfa_path
%>
...
...
@@ -18,18 +18,38 @@
<%=
t
:totp_enable_help
%>
<%=
link_to
'FreeOTP'
,
'https://freeotp.github.io/'
,
target:
'_new'
%>
</p>
<%
has_default
=
false
if
@existing_totps
.
present?
-%>
<%
has_default
=
false
if
@existing_totps
.
present?
-%>
<hr
/>
<h4>
<%=
t
(
:existing_totps
)
%>
</h4>
<ul>
<%
@existing_totps
.
each
do
|
name
,
kind
|
has_default
=
true
if
name
==
'default'
-%>
<li>
<%=
name
%>
<%=
link_to
t
(
:delete
),
"/tfa/
#{
URI
.
encode
(
name
)
}
/delete_totp"
,
method: :post
%>
</li>
<%
@existing_totps
.
each
do
|
name
,
verified
|
has_default
=
true
if
name
==
'default'
-%>
<li>
<%=
name
%>
<%
if
verified
-%>
<%=
t
(
:active
)
%>
<%
else
-%>
<%=
t
(
:not_active
)
%>
-
<a
href=
"#"
onclick=
"toggle_visibility('activate_form_
<%=
URI
.
encode
(
name
)
%>
')"
>
<%=
t
(
:verify_to_activate
)
%>
</a>
<div
id=
"activate_form_
<%=
URI
.
encode
(
name
)
%>
"
style=
"display:none"
>
<%=
t
(
:activate_your_totp_long
)
%>
<br/>
<%=
form_tag
(
"/tfa/
#{
URI
.
encode
(
name
)
}
/verify_totp"
,
method:
"post"
)
do
%>
<%=
label_tag
(
:totp
,
t
(
:totp
))
%><%=
text_field_tag
(
:totp
,
nil
,
autofocus:
true
,
autocomplete: :off
)
%>
<%=
submit_tag
t
(
:verify
)
%>
<%
end
-%>
</div>
<%
end
-%>
<a
href=
"#"
onclick=
"toggle_visibility('delete_form_
<%=
URI
.
encode
(
name
)
%>
')"
/>
<%=
t
(
:delete
)
%>
</a>
<div
id=
"delete_form_
<%=
URI
.
encode
(
name
)
%>
"
style=
"display:none"
>
<br
/>
<%=
form_tag
(
"/tfa/
#{
URI
.
encode
(
name
)
}
/delete_totp"
,
method:
"post"
)
do
%>
<%=
label_tag
(
:pass
,
t
(
:your_main_pw
))
%><%=
password_field_tag
(
:password
,
nil
,
placeholder:
t
(
:enter_your_current_pw
))
%>
<%=
submit_tag
t
(
:delete
)
%>
<%
end
-%>
</div>
</li>
<%
end
-%>
</ul>
<%
end
-%>
<hr
/>
<p>
...
...
@@ -42,6 +62,11 @@
</td><td>
<%=
text_field_tag
(
:name
,
''
,
value:
(
has_default
?
"default-
#{
Time
.
now
.
strftime
(
"%Y%m%d%H%M"
)
}
"
:
'default'
),
maxlength:
255
,
autocomplete:
'off'
)
%>
</td></tr>
<tr><td>
<%=
label_tag
(
:pass
,
t
(
:your_main_pw
))
%>
</td><td>
<%=
password_field_tag
(
:password
,
nil
,
placeholder:
t
(
:enter_your_current_pw
))
%>
</td></tr>
</table>
<%=
hidden_field
(
''
,
:kind
,
:value
=>
'totp'
)
%>
<%=
submit_tag
(
t
(
:create
))
%>
...
...
config/locales/de.yml
View file @
dddbd9b6
...
...
@@ -13,6 +13,7 @@ de:
present
:
"
Gesetzt"
edit
:
"
Bearbeiten"
deleted
:
"
Gelöscht"
verify
:
"
Verifizieren"
mail_crypt_settings
:
"
Geschützte
Mailablage"
mail_crypt_short_help
:
"
Bestimme
wie
deine
Mails
auf
unserem
Server
gespeichert
werden."
...
...
@@ -113,6 +114,9 @@ de:
totp_enable_help
:
'
Zeitbasierte
OneTimePassword
(TOTP)
sind
ein
zweiter
Faktor,
der
auf
der
aktuellen
Zeit
und
einem
geteilten
Passwort
basiert.
Üblicherweise
kannst
du
eine
Mobile
App
verwenden,
welche
dir
das
aktuell
gültige
Passwort
anzeigt.
Unsere
TOTPs
sind
kompatibel
mit
RFC6263.
Eine
populäre
und
freie
mobile
App
für
TOTPs
ist
FreeOTP,
welche
in
den
Appstores
von
Android
und
iOS
verfügbar
ist.
Wie
auch
auf
der
folgenden
Webseite:'
your_new_totp_token
:
'
Dein
neuer
TOTP
Token'
existing_totps
:
'
Bestehende
TOTPs'
activate_your_totp
:
'
Aktiviere
dein
TOTP
Token'
activate_your_totp_long
:
'
Damit
wir
sicherstellen
können,
dass
dein
neuer
TOTP
auch
richtig
funktioniert,
musst
du
ihn
ein
erstes
mal
verifizieren.
Erst
nach
einer
erfolgreichen
Verifikation
aktivieren
wir
diesen
TOTP
Token.
Um
die
Verifikation
durchzuführen
musst
du
den
den
aktuellen
Wert
des
TOTP
Tokens
eingeben'
auth_wait
:
"
Bitte
warte
während
wir
deine
Eingabe
überprüfen..."
enter_captcha
:
"
Bitte
kopiere
diese
Buchstaben
und
Zahlen
in
das
untere
Feld."
admin_lock
:
"
Sperre
Account"
...
...
config/locales/en.yml
View file @
dddbd9b6
...
...
@@ -13,6 +13,7 @@ en:
present
:
"
present"
edit
:
"
Edit"
deleted
:
"
Deleted"
verify
:
"
verify"
mail_crypt_settings
:
"
Secured
Mailstorage"
mail_crypt_short_help
:
"
Decide
how
your
mails
are
stored
on
our
servers."
...
...
@@ -111,8 +112,11 @@ en:
tfa_enable_help
:
"
You
can
define
a
second
factor
that
is
required
to
successfully
authenticate
your
account.
This
provides
an
additional
layer
of
protection
for
your
account.
NOTE:
As
two
factor
authentication
only
works
when
authenticating
against
our
user
portal,
you
will
need
to
create
application
passwords
e.g.
for
your
mail
programm
like
thunderbird.
Once
you
enable
TwoFactor
Authentication
your
main
password
will
only
work
on
the
portal
login.
We
recommend
you
to
set
a
recovery
email
address
for
your
account,
so
in
case
of
loosing
your
second
factor,
we
can
remove
the
two
factor
authentication.
You
can
define
multiple
second
factors,
though
to
successfully
login
only
one
valid
is
required."
totp_enable
:
'
Timebased
OneTimePasswords'
totp_enable_help
:
"
Timebased
onetimepasswords
(TOTP)
are
a
second
factor
that
is
based
on
the
current
time
and
a
shared
secret.
Usually
you
can
use
a
mobile
app,
which
will
show
you
the
current
valid
password.
We
are
providing
TOTPs
which
are
compatible
with
RFC6263.
A
popular
and
free
mobile
application
for
TOTPs
is
FreeOTP,
which
is
available
on
Android's
and
iOS'
appstores
or
on
the
following
website:"
your_new_totp_token
:
'
Your
new
TOTP
T
oken'
your_new_totp_token
:
'
Your
new
TOTP
t
oken'
existing_totps
:
'
Existing
TOTPs'
activate_your_totp
:
'
Activate
your
TOTP
token'
activate_your_totp_long
:
'
To
be
able
to
verify,
that
your
new
TOTP
token
also
works
correctly,
you
must
verify
it
for
a
first
time.
Only
after
a
successfull
verification,
we
will
activate
this
token.
To
verify
your
token,
you
need
enter
the
current
value
of
your
TOTP
for
a
first
time.'
auth_wait
:
"
Please
wait
while
we
validate
your
credentials..."
enter_captcha
:
"
Please
copy
the
following
letters
into
the
field
below."
admin_lock
:
"
Lock
account"
...
...
config/routes.rb
View file @
dddbd9b6
...
...
@@ -41,8 +41,13 @@ Rails.application.routes.draw do
post
'signup/:token'
,
to:
'signup#create'
get
'tfa'
,
to:
'tfa#show'
post
'tfa'
,
to:
'tfa#enable'
post
'tfa/:name/delete_totp'
,
to:
'tfa#delete_totp'
post
'tfa'
,
to:
'tfa#create'
constraints
(
:name
=>
/[a-zA-Z0-9\-]+/
)
do
post
'tfa/:name/delete_totp'
,
to:
'tfa#delete_totp'
get
'tfa/:name/delete_totp'
,
to:
redirect
(
'tfa'
)
post
'tfa/:name/verify_totp'
,
to:
'tfa#verify_totp'
post
'tfa/:name/verify_totp'
,
to:
redirect
(
'tfa'
)
end
root
to:
'welcome#index'
get
'/welcome'
,
to:
'welcome#index'
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment