Show More
@@ -34,7 +34,7 b' from pyramid.renderers import render' | |||||
34 | from pyramid.response import Response |
|
34 | from pyramid.response import Response | |
35 | from pyramid.httpexceptions import HTTPFound |
|
35 | from pyramid.httpexceptions import HTTPFound | |
36 |
|
36 | |||
37 |
|
37 | import rhodecode | ||
38 | from rhodecode.apps._base import BaseAppView |
|
38 | from rhodecode.apps._base import BaseAppView | |
39 | from rhodecode.authentication.base import authenticate, HTTP_TYPE |
|
39 | from rhodecode.authentication.base import authenticate, HTTP_TYPE | |
40 | from rhodecode.authentication.plugins import auth_rhodecode |
|
40 | from rhodecode.authentication.plugins import auth_rhodecode | |
@@ -510,9 +510,10 b' class LoginView(BaseAppView):' | |||||
510 | # only then we should persist it |
|
510 | # only then we should persist it | |
511 | secret = user_instance.init_secret_2fa(persist=False) |
|
511 | secret = user_instance.init_secret_2fa(persist=False) | |
512 |
|
512 | |||
513 | totp_name = f'RhodeCode token ({self.request.user.username})' |
|
513 | instance_name = rhodecode.ConfigGet().get_str('app.base_url', 'rhodecode') | |
|
514 | totp_name = f'{instance_name}:{self.request.user.username}' | |||
514 |
|
515 | |||
515 |
qr = qrcode.QRCode(version=1, box_size= |
|
516 | qr = qrcode.QRCode(version=1, box_size=5, border=4) | |
516 | qr.add_data(pyotp.totp.TOTP(secret).provisioning_uri(name=totp_name)) |
|
517 | qr.add_data(pyotp.totp.TOTP(secret).provisioning_uri(name=totp_name)) | |
517 | qr.make(fit=True) |
|
518 | qr.make(fit=True) | |
518 | img = qr.make_image(fill_color='black', back_color='white') |
|
519 | img = qr.make_image(fill_color='black', back_color='white') |
@@ -919,7 +919,7 b' class User(Base, BaseModel):' | |||||
919 | return '' |
|
919 | return '' | |
920 |
|
920 | |||
921 | def get_secret_2fa(self) -> str: |
|
921 | def get_secret_2fa(self) -> str: | |
922 |
secret_2fa = self.user_data |
|
922 | secret_2fa = self.user_data.get('secret_2fa') | |
923 | if secret_2fa: |
|
923 | if secret_2fa: | |
924 | strict_mode = ConfigGet().get_bool('rhodecode.encrypted_values.strict', missing=True) |
|
924 | strict_mode = ConfigGet().get_bool('rhodecode.encrypted_values.strict', missing=True) | |
925 | return safe_str( |
|
925 | return safe_str( |
@@ -6,9 +6,11 b'' | |||||
6 | · ${h.branding(c.rhodecode_name)} |
|
6 | · ${h.branding(c.rhodecode_name)} | |
7 | %endif |
|
7 | %endif | |
8 | </%def> |
|
8 | </%def> | |
|
9 | ||||
9 | <style>body{background-color:#eeeeee;}</style> |
|
10 | <style>body{background-color:#eeeeee;}</style> | |
10 |
|
11 | |||
11 | <div class="loginbox"> |
|
12 | <div class="loginbox" style="width: 600px"> | |
|
13 | ||||
12 | <div class="header-account"> |
|
14 | <div class="header-account"> | |
13 | <div id="header-inner" class="title"> |
|
15 | <div id="header-inner" class="title"> | |
14 | <div id="logo"> |
|
16 | <div id="logo"> | |
@@ -22,66 +24,67 b'' | |||||
22 | </div> |
|
24 | </div> | |
23 |
|
25 | |||
24 | <div class="loginwrapper"> |
|
26 | <div class="loginwrapper"> | |
25 | <h1>${_('Setup the authenticator app')}</h1> |
|
|||
26 |
|
||||
27 | <p>Authenticator apps like <a href='https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2' target="_blank" rel="noopener noreferrer">Google Authenticator</a>, etc. generate one-time passwords that are used as a second factor to verify you identity.</p> |
|
|||
28 | <rhodecode-toast id="notifications"></rhodecode-toast> |
|
27 | <rhodecode-toast id="notifications"></rhodecode-toast> | |
29 |
|
28 | |||
30 |
<div |
|
29 | <div class="sign-in-title"> | |
|
30 | <h1>${_('Set up the authenticator app')} - ${_('scan the QR code')}</h1> | |||
|
31 | </div> | |||
|
32 | <div class="inner form"> | |||
31 | ${h.secure_form(h.route_path('setup_2fa'), request=request, id='totp_form')} |
|
33 | ${h.secure_form(h.route_path('setup_2fa'), request=request, id='totp_form')} | |
32 | <div class="sign-in-title"> |
|
34 | <strong>${_('Use an authenticator app to scan.')}</strong><br/> | |
33 | <h1>${_('Scan the QR code')}: "${totp_name}"</h1> |
|
35 | ||
34 | </div> |
|
36 | ## QR CODE | |
35 | <p>${_('Use an authenticator app to scan.')}</p> |
|
37 | <code>${_('Account')}: ${totp_name}</code><br/> | |
|
38 | <div class="qr-code-container"> | |||
36 | <img alt="qr-code" src="data:image/png;base64, ${qr}"/> |
|
39 | <img alt="qr-code" src="data:image/png;base64, ${qr}"/> | |
37 |
|
||||
38 | <p>${_('Unable to scan?')} <a id="toggleLink">${_('Click here')}</a></p> |
|
|||
39 | <div id="secretDiv" class="hidden"> |
|
|||
40 | <p>${_('Copy and use this code to manually set up an authenticator app')}</p> |
|
|||
41 | <input type="text" class="input-monospace" value="${key}" id="secret_totp" name="secret_totp" style="width: 400px"/> |
|
|||
42 | <i class="tooltip icon-clipboard clipboard-action" data-clipboard-text="${key}" title="${_('Copy the secret key')}"></i> |
|
|||
43 | </div> |
|
40 | </div> | |
44 |
|
41 | |||
45 | <div id="verify_2fa"> |
|
42 | <div id="alternativeCode" style="margin: -10px 0 5px 0">${_('Unable to scan?')} <a id="toggleLink">${_('Click here')}</a></div> | |
|
43 | ||||
|
44 | ## Secret alternative code | |||
|
45 | <div id="secretDiv" style="display: none"> | |||
46 |
|
46 | |||
47 |
<div |
|
47 | <div style="padding: 10px 0"> | |
48 | <div class="field"> |
|
48 | <strong style="padding: 4px 0">${_('Copy and use this code to manually set up an authenticator app')}</strong> | |
49 | <p> |
|
49 | <code>${key}</code><i class="tooltip icon-clipboard clipboard-action" data-clipboard-text="${key}" title="${_('Copy the secret key')}" ></i><br/> | |
50 | <div class="label"> |
|
50 | <code>${_('type')}: time-based</code> | |
51 | <label for="totp" class="form-label text-dark font-weight-bold" style="text-align: left;">${_('Verify the code from the app')}:</label> |
|
|||
52 |
|
|
51 | </div> | |
53 | </p> |
|
52 | ||
54 |
|
|
53 | </div> | |
55 | <div> |
|
54 | ||
56 | <div class="input-group"> |
|
55 | <label for="totp">${_('Verify the code from the app')}:</label> | |
57 |
|
|
56 | ${h.text('totp', class_='form-control', )} | |
58 |
|
|
57 | <div id="formErrors"> | |
59 |
|
|
58 | % if 'totp' in errors: | |
60 |
|
|
59 | <span class="error-message">${errors.get('totp')}</span> | |
61 |
|
|
60 | <br /> | |
62 |
|
|
61 | % endif | |
63 | </div> |
|
62 | % if 'secret_totp' in errors: | |
64 | <div class="input-group-append"> |
|
63 | <span class="error-message">SECRET:${errors.get('secret_totp')}</span> | |
65 | ${h.submit('verify_2fa',_('Verify'),class_="btn btn-primary", style='width: 40%;')} |
|
64 | <br /> | |
66 |
|
|
65 | % endif | |
67 |
|
|
66 | </div> | |
68 | </div> |
|
67 | ${h.hidden('secret_totp', key)} | |
69 | </p> |
|
68 | ${h.submit('verify_2fa',_('Verify'), class_="btn sign-in")} | |
70 | </div> |
|
69 | ||
71 | </div> |
|
|||
72 | </div> |
|
|||
73 | ${h.end_form()} |
|
70 | ${h.end_form()} | |
74 | </div> |
|
71 | </div> | |
|
72 | ||||
75 | </div> |
|
73 | </div> | |
|
74 | ||||
76 | </div> |
|
75 | </div> | |
77 |
|
76 | |||
78 | <script> |
|
77 | <script type="text/javascript"> | |
|
78 | ||||
|
79 | $(document).ready(function() { | |||
79 |
|
80 | |||
80 | document.getElementById('toggleLink').addEventListener('click', function() { |
|
81 | $( "#toggleLink" ).on("click", function() { | |
81 | let hiddenField = document.getElementById('secretDiv'); |
|
82 | $( "#secretDiv" ).toggle(); | |
82 | if (hiddenField.classList.contains('hidden')) { |
|
83 | $( "#alternativeCode").hide(); | |
83 | hiddenField.classList.remove('hidden'); |
|
84 | $('#totp').focus(); | |
84 | } |
|
|||
85 | }); |
|
85 | }); | |
86 |
|
86 | |||
|
87 | $('#totp').focus(); | |||
|
88 | }) | |||
|
89 | ||||
87 | </script> |
|
90 | </script> |
@@ -8,7 +8,7 b'' | |||||
8 | </%def> |
|
8 | </%def> | |
9 | <style>body{background-color:#eeeeee;}</style> |
|
9 | <style>body{background-color:#eeeeee;}</style> | |
10 |
|
10 | |||
11 | <div class="loginbox"> |
|
11 | <div class="loginbox" style="width: 600px"> | |
12 | <div class="header-account"> |
|
12 | <div class="header-account"> | |
13 | <div id="header-inner" class="title"> |
|
13 | <div id="header-inner" class="title"> | |
14 | <div id="logo"> |
|
14 | <div id="logo"> |
General Comments 0
You need to be logged in to leave comments.
Login now