From 6aed317a1f714cba3d7b2063ebcd06b01032163d Mon Sep 17 00:00:00 2001 From: Master3395 Date: Fri, 17 Oct 2025 01:06:04 +0200 Subject: [PATCH] Add regenerateTwoFASecret functionality and UI support - Introduced a new endpoint to manually regenerate the 2FA secret for users. - Updated views to handle 2FA secret regeneration, including security checks and logging. - Enhanced the user interface with a button to regenerate the 2FA secret, along with appropriate alerts and confirmations. - Updated JavaScript to manage the regeneration process and display the new secret key and QR code provisioning URI. https://github.com/usmannasir/cyberpanel/issues/1577 --- .../static/userManagment/userManagment.js | 42 ++++++++++ .../templates/userManagment/modifyUser.html | 8 ++ userManagment/urls.py | 1 + userManagment/views.py | 81 +++++++++++++++++++ 4 files changed, 132 insertions(+) diff --git a/userManagment/static/userManagment/userManagment.js b/userManagment/static/userManagment/userManagment.js index f898b411e..fdf0e7878 100644 --- a/userManagment/static/userManagment/userManagment.js +++ b/userManagment/static/userManagment/userManagment.js @@ -180,6 +180,48 @@ app.controller('modifyUser', function ($scope, $http) { } }; + $scope.regenerateSecret = function() { + if (!$scope.accountUsername) { + alert('Please select a user first.'); + return; + } + + if (!confirm('Are you sure you want to regenerate the 2FA secret? This will generate a new secret key and you will need to update your authenticator app.')) { + return; + } + + var url = "/users/regenerateTwoFASecret"; + var data = { + accountUsername: $scope.accountUsername + }; + var config = { + headers: { + 'X-CSRFToken': getCookie('csrftoken') + } + }; + + $http.post(url, data, config).then(function(response) { + if (response.data.status === 1) { + // Update the secret key and formatted version + $scope.secretKey = response.data.secretKey; + $scope.formattedSecretKey = response.data.secretKey.match(/.{1,4}/g).join(' '); + + // Update the QR code with new provisioning URI + qrCode.set({ + value: response.data.otpauth + }); + + // Show success message + alert('2FA secret has been successfully regenerated! Please update your authenticator app with the new QR code or secret key.'); + } else { + alert('Error regenerating 2FA secret: ' + response.data.error_message); + } + }, function(error) { + console.error('Error regenerating 2FA secret:', error); + alert('Failed to regenerate 2FA secret. Please try again.'); + }); + }; + // WebAuthn Functions $scope.loadWebAuthnData = function() { if (!$scope.accountUsername) return; diff --git a/userManagment/templates/userManagment/modifyUser.html b/userManagment/templates/userManagment/modifyUser.html index 559717644..f9b470033 100644 --- a/userManagment/templates/userManagment/modifyUser.html +++ b/userManagment/templates/userManagment/modifyUser.html @@ -319,6 +319,14 @@ {% trans "Enter this key in your authenticator app if you cannot scan QR codes." %} + + +
+ + {% trans "Warning: This will generate a new secret key. You'll need to update your authenticator app." %} +
diff --git a/userManagment/urls.py b/userManagment/urls.py index 6f2660a9d..04ba37a08 100644 --- a/userManagment/urls.py +++ b/userManagment/urls.py @@ -40,4 +40,5 @@ urlpatterns = [ path('migrateUser', homeDirectoryViews.migrateUser, name='migrateUser'), path('userMigration', views.userMigration, name='userMigration'), path('disable2FA', views.disable2FA, name='disable2FA'), + path('regenerateTwoFASecret', views.regenerateTwoFASecret, name='regenerateTwoFASecret'), ] diff --git a/userManagment/views.py b/userManagment/views.py index 3ff4ba6e0..a43cf5632 100644 --- a/userManagment/views.py +++ b/userManagment/views.py @@ -504,11 +504,22 @@ def saveModifications(request): user.lastName = lastName user.email = email user.type = 0 + + # Check if 2FA is being enabled (transition from 0 to 1) + was_2fa_disabled = user.twoFA == 0 user.twoFA = twofa # If 2FA is being disabled, clear the secret key if twofa == 0: user.secretKey = 'None' + # If 2FA is being enabled (transition from disabled to enabled), always generate a new secret + elif twofa == 1 and was_2fa_disabled: + import pyotp + user.secretKey = pyotp.random_base32() + + # Log the secret regeneration for security audit + from plogical.CyberCPLogFileWriter import CyberCPLogFileWriter as logging + logging.writeToFile(f'2FA secret auto-regenerated for user: {accountUsername} by admin: {val}') if securityLevel == 'LOW': user.securityLevel = secMiddleware.LOW @@ -1136,3 +1147,73 @@ def disable2FA(request): data_ret = secure_error_response(e, 'Failed to disable 2FA') json_data = json.dumps(data_ret) return HttpResponse(json_data) + + +def regenerateTwoFASecret(request): + """ + Manually regenerate 2FA secret for a specific user + """ + try: + val = request.session['userID'] + currentACL = ACLManager.loadedACL(val) + + if currentACL['admin'] != 1: + data_ret = {'status': 0, 'error_message': 'Unauthorized access. Admin privileges required.'} + json_data = json.dumps(data_ret) + return HttpResponse(json_data) + + if request.method == 'POST': + data = json.loads(request.body) + accountUsername = data.get('accountUsername') + + if not accountUsername: + data_ret = {'status': 0, 'error_message': 'Username is required.'} + json_data = json.dumps(data_ret) + return HttpResponse(json_data) + + try: + user = Administrator.objects.get(userName=accountUsername) + + # Check if user has 2FA enabled + if not user.twoFA: + data_ret = {'status': 0, 'error_message': '2FA is not enabled for this user.'} + json_data = json.dumps(data_ret) + return HttpResponse(json_data) + + # Generate new secret key + import pyotp + new_secret = pyotp.random_base32() + user.secretKey = new_secret + user.save() + + # Generate new QR code provisioning URI + otpauth = pyotp.totp.TOTP(new_secret).provisioning_uri(user.email, issuer_name="CyberPanel") + + # Log the secret regeneration for security audit + from plogical.CyberCPLogFileWriter import CyberCPLogFileWriter as logging + logging.writeToFile(f'2FA secret manually regenerated for user: {accountUsername} by admin: {val}') + + data_ret = { + 'status': 1, + 'error_message': '2FA secret successfully regenerated.', + 'message': f'Two-factor authentication secret has been regenerated for user {accountUsername}.', + 'secretKey': new_secret, + 'otpauth': otpauth + } + json_data = json.dumps(data_ret) + return HttpResponse(json_data) + + except Administrator.DoesNotExist: + data_ret = {'status': 0, 'error_message': 'User not found.'} + json_data = json.dumps(data_ret) + return HttpResponse(json_data) + + data_ret = {'status': 0, 'error_message': 'Invalid request method.'} + json_data = json.dumps(data_ret) + return HttpResponse(json_data) + + except Exception as e: + secure_log_error(e, 'regenerateTwoFASecret', request.session.get('userID', 'Unknown')) + data_ret = secure_error_response(e, 'Failed to regenerate 2FA secret') + json_data = json.dumps(data_ret) + return HttpResponse(json_data)