Merge pull request #1585 from master3395/v2.5.5-dev

Add regenerateTwoFASecret functionality and UI support
This commit is contained in:
Master3395
2025-10-17 01:27:38 +02:00
committed by GitHub
4 changed files with 132 additions and 0 deletions

View File

@@ -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;

View File

@@ -319,6 +319,14 @@
</div>
<small class="text-muted mt-1 d-block">{% trans "Enter this key in your authenticator app if you cannot scan QR codes." %}</small>
</div>
<!-- Regenerate Secret Button -->
<div class="mt-3" ng-show="twofa">
<button type="button" class="btn btn-warning" ng-click="regenerateSecret()" title="{% trans 'Generate a new 2FA secret key' %}">
<i class="fa fa-refresh"></i> {% trans "Regenerate Secret" %}
</button>
<small class="text-muted d-block mt-1">{% trans "Warning: This will generate a new secret key. You'll need to update your authenticator app." %}</small>
</div>
</div>
</div>

View File

@@ -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'),
]

View File

@@ -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)