Show More
@@ -34,7 +34,7 b' from pyramid.renderers import render' | |||
|
34 | 34 | from pyramid.response import Response |
|
35 | 35 | from pyramid.httpexceptions import HTTPFound |
|
36 | 36 | |
|
37 | ||
|
37 | import rhodecode | |
|
38 | 38 | from rhodecode.apps._base import BaseAppView |
|
39 | 39 | from rhodecode.authentication.base import authenticate, HTTP_TYPE |
|
40 | 40 | from rhodecode.authentication.plugins import auth_rhodecode |
@@ -510,9 +510,10 b' class LoginView(BaseAppView):' | |||
|
510 | 510 | # only then we should persist it |
|
511 | 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 | 517 | qr.add_data(pyotp.totp.TOTP(secret).provisioning_uri(name=totp_name)) |
|
517 | 518 | qr.make(fit=True) |
|
518 | 519 | img = qr.make_image(fill_color='black', back_color='white') |
@@ -919,7 +919,7 b' class User(Base, BaseModel):' | |||
|
919 | 919 | return '' |
|
920 | 920 | |
|
921 | 921 | def get_secret_2fa(self) -> str: |
|
922 |
secret_2fa = self.user_data |
|
|
922 | secret_2fa = self.user_data.get('secret_2fa') | |
|
923 | 923 | if secret_2fa: |
|
924 | 924 | strict_mode = ConfigGet().get_bool('rhodecode.encrypted_values.strict', missing=True) |
|
925 | 925 | return safe_str( |
@@ -6,9 +6,11 b'' | |||
|
6 | 6 | · ${h.branding(c.rhodecode_name)} |
|
7 | 7 | %endif |
|
8 | 8 | </%def> |
|
9 | ||
|
9 | 10 | <style>body{background-color:#eeeeee;}</style> |
|
10 | 11 | |
|
11 | <div class="loginbox"> | |
|
12 | <div class="loginbox" style="width: 600px"> | |
|
13 | ||
|
12 | 14 | <div class="header-account"> |
|
13 | 15 | <div id="header-inner" class="title"> |
|
14 | 16 | <div id="logo"> |
@@ -22,66 +24,67 b'' | |||
|
22 | 24 | </div> |
|
23 | 25 | |
|
24 | 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 | 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 | 33 | ${h.secure_form(h.route_path('setup_2fa'), request=request, id='totp_form')} |
|
32 | <div class="sign-in-title"> | |
|
33 | <h1>${_('Scan the QR code')}: "${totp_name}"</h1> | |
|
34 | <strong>${_('Use an authenticator app to scan.')}</strong><br/> | |
|
35 | ||
|
36 | ## QR CODE | |
|
37 | <code>${_('Account')}: ${totp_name}</code><br/> | |
|
38 | <div class="qr-code-container"> | |
|
39 | <img alt="qr-code" src="data:image/png;base64, ${qr}"/> | |
|
34 | 40 | </div> |
|
35 | <p>${_('Use an authenticator app to scan.')}</p> | |
|
36 | <img alt="qr-code" src="data:image/png;base64, ${qr}"/> | |
|
41 | ||
|
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"> | |
|
37 | 46 | |
|
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> | |
|
47 | <div style="padding: 10px 0"> | |
|
48 | <strong style="padding: 4px 0">${_('Copy and use this code to manually set up an authenticator app')}</strong> | |
|
49 | <code>${key}</code><i class="tooltip icon-clipboard clipboard-action" data-clipboard-text="${key}" title="${_('Copy the secret key')}" ></i><br/> | |
|
50 | <code>${_('type')}: time-based</code> | |
|
51 | </div> | |
|
52 | ||
|
43 | 53 | </div> |
|
44 | 54 | |
|
45 | <div id="verify_2fa"> | |
|
55 | <label for="totp">${_('Verify the code from the app')}:</label> | |
|
56 | ${h.text('totp', class_='form-control', )} | |
|
57 | <div id="formErrors"> | |
|
58 | % if 'totp' in errors: | |
|
59 | <span class="error-message">${errors.get('totp')}</span> | |
|
60 | <br /> | |
|
61 | % endif | |
|
62 | % if 'secret_totp' in errors: | |
|
63 | <span class="error-message">SECRET:${errors.get('secret_totp')}</span> | |
|
64 | <br /> | |
|
65 | % endif | |
|
66 | </div> | |
|
67 | ${h.hidden('secret_totp', key)} | |
|
68 | ${h.submit('verify_2fa',_('Verify'), class_="btn sign-in")} | |
|
46 | 69 | |
|
47 | <div class="form mt-4"> | |
|
48 | <div class="field"> | |
|
49 | <p> | |
|
50 | <div class="label"> | |
|
51 | <label for="totp" class="form-label text-dark font-weight-bold" style="text-align: left;">${_('Verify the code from the app')}:</label> | |
|
52 | </div> | |
|
53 | </p> | |
|
54 | <p> | |
|
55 | <div> | |
|
56 | <div class="input-group"> | |
|
57 | ${h.text('totp', class_='form-control', style='width: 40%;')} | |
|
58 | <div id="formErrors"> | |
|
59 | % if 'totp' in errors: | |
|
60 | <span class="error-message">${errors.get('totp')}</span> | |
|
61 | <br /> | |
|
62 | % endif | |
|
63 | </div> | |
|
64 | <div class="input-group-append"> | |
|
65 | ${h.submit('verify_2fa',_('Verify'),class_="btn btn-primary", style='width: 40%;')} | |
|
66 | </div> | |
|
67 | </div> | |
|
68 | </div> | |
|
69 | </p> | |
|
70 | </div> | |
|
71 | </div> | |
|
72 | </div> | |
|
73 | 70 | ${h.end_form()} |
|
74 | 71 | </div> |
|
72 | ||
|
75 | 73 | </div> |
|
74 | ||
|
76 | 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 | let hiddenField = document.getElementById('secretDiv'); | |
|
82 | if (hiddenField.classList.contains('hidden')) { | |
|
83 | hiddenField.classList.remove('hidden'); | |
|
84 | } | |
|
85 | }); | |
|
81 | $( "#toggleLink" ).on("click", function() { | |
|
82 | $( "#secretDiv" ).toggle(); | |
|
83 | $( "#alternativeCode").hide(); | |
|
84 | $('#totp').focus(); | |
|
85 | }); | |
|
86 | ||
|
87 | $('#totp').focus(); | |
|
88 | }) | |
|
86 | 89 | |
|
87 | 90 | </script> |
General Comments 0
You need to be logged in to leave comments.
Login now