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)