mirror of
https://github.com/Icinga/icingaweb2.git
synced 2025-09-24 10:27:46 +02:00
Add: qr code for new secrets
This commit is contained in:
parent
3b31e63810
commit
b6b4315337
@ -6,7 +6,6 @@ use Exception;
|
|||||||
use Icinga\Application\Logger;
|
use Icinga\Application\Logger;
|
||||||
use Icinga\Authentication\Auth;
|
use Icinga\Authentication\Auth;
|
||||||
use Icinga\Authentication\Totp;
|
use Icinga\Authentication\Totp;
|
||||||
use Icinga\Data\Filter\Filter;
|
|
||||||
use Icinga\Forms\PreferenceForm;
|
use Icinga\Forms\PreferenceForm;
|
||||||
use Icinga\User\Preferences;
|
use Icinga\User\Preferences;
|
||||||
use Icinga\Web\Form;
|
use Icinga\Web\Form;
|
||||||
@ -135,10 +134,12 @@ class TotpForm extends PreferenceForm
|
|||||||
'Please enter the verification code from your TOTP application to verify the new secret.'
|
'Please enter the verification code from your TOTP application to verify the new secret.'
|
||||||
),
|
),
|
||||||
'class' => 'autofocus content-centered',
|
'class' => 'autofocus content-centered',
|
||||||
'style' => 'width: 200px;',
|
'style' => 'width: 120px;',
|
||||||
'autocomplete' => 'off',
|
'autocomplete' => 'off',
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
$this->addElement(
|
$this->addElement(
|
||||||
'submit',
|
'submit',
|
||||||
'btn_verify_totp',
|
'btn_verify_totp',
|
||||||
@ -159,6 +160,26 @@ class TotpForm extends PreferenceForm
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$this->addElement(
|
||||||
|
'hidden',
|
||||||
|
'qr_code_image',
|
||||||
|
[
|
||||||
|
'required' => false,
|
||||||
|
'ignore' => false,
|
||||||
|
'autoInsertNotEmptyValidator' => false,
|
||||||
|
'decorators' => [
|
||||||
|
[
|
||||||
|
'HtmlTag', [
|
||||||
|
'tag' => 'img',
|
||||||
|
'src' => $this->totp->createQRCode(),
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
$this->addDisplayGroup(
|
$this->addDisplayGroup(
|
||||||
['totp_verification_code', 'btn_verify_totp'],
|
['totp_verification_code', 'btn_verify_totp'],
|
||||||
'verify_buttons',
|
'verify_buttons',
|
||||||
@ -167,7 +188,10 @@ class TotpForm extends PreferenceForm
|
|||||||
'FormElements',
|
'FormElements',
|
||||||
[
|
[
|
||||||
'HtmlTag',
|
'HtmlTag',
|
||||||
['tag' => 'div', 'class' => 'control-group form-controls']
|
[
|
||||||
|
'tag' => 'div',
|
||||||
|
'class' => 'control-group form-controls'
|
||||||
|
]
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
@ -2,6 +2,10 @@
|
|||||||
|
|
||||||
namespace Icinga\Authentication;
|
namespace Icinga\Authentication;
|
||||||
|
|
||||||
|
use chillerlan\QRCode\Common\EccLevel;
|
||||||
|
use chillerlan\QRCode\Data\QRMatrix;
|
||||||
|
use chillerlan\QRCode\QRCode;
|
||||||
|
use chillerlan\QRCode\QROptions;
|
||||||
use Icinga\Clock\PsrClock;
|
use Icinga\Clock\PsrClock;
|
||||||
use Icinga\Common\Database;
|
use Icinga\Common\Database;
|
||||||
use Icinga\Exception\ConfigurationError;
|
use Icinga\Exception\ConfigurationError;
|
||||||
@ -47,7 +51,6 @@ class Totp
|
|||||||
*/
|
*/
|
||||||
const COLUMN_MODIFIED_TIME = 'mtime';
|
const COLUMN_MODIFIED_TIME = 'mtime';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* State indicating that a secret check is required
|
* State indicating that a secret check is required
|
||||||
*/
|
*/
|
||||||
@ -61,6 +64,14 @@ class Totp
|
|||||||
*/
|
*/
|
||||||
const STATE_APPROVED_TEMPORARY_SECRET = 'approve_temporary_secret';
|
const STATE_APPROVED_TEMPORARY_SECRET = 'approve_temporary_secret';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The label for the TOTP application
|
||||||
|
*
|
||||||
|
* This label is used when generating the TOTP secret and QR code.
|
||||||
|
* It helps identify the application in the user's TOTP app.
|
||||||
|
*/
|
||||||
|
const TOTP_LABEL = 'IcingaWeb2';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The username for which the TOTP is configured
|
* The username for which the TOTP is configured
|
||||||
*
|
*
|
||||||
@ -355,6 +366,44 @@ class Totp
|
|||||||
return $this->totpObject->now();
|
return $this->totpObject->now();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a QR code for the TOTP secret.
|
||||||
|
* This method generates a QR code that can be scanned by TOTP apps to set up the user's secret.
|
||||||
|
*
|
||||||
|
* @return string The rendered QR code as a string
|
||||||
|
*/
|
||||||
|
public function createQRCode(): ?string
|
||||||
|
{
|
||||||
|
if ($this->temporaryTotpObject === null) {
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$urlOTPAUTH = sprintf(
|
||||||
|
'otpauth://totp/%1$s:%2$s?secret=%3$s&issuer=%1$s',
|
||||||
|
urlencode(self::TOTP_LABEL),
|
||||||
|
urlencode($this->username),
|
||||||
|
urlencode($this->temporarySecret),
|
||||||
|
);
|
||||||
|
$options = new QROptions();
|
||||||
|
|
||||||
|
$options->scale = 5;
|
||||||
|
|
||||||
|
// $options->svgLogo = __DIR__.'/github.svg'; // logo from: https://github.com/simple-icons/simple-icons
|
||||||
|
// $options->svgLogoScale = 0.25;
|
||||||
|
// $options->svgLogoCssClass = 'dark';
|
||||||
|
//
|
||||||
|
// $options->addLogoSpace = true;
|
||||||
|
// $options->logoSpaceWidth = 19;
|
||||||
|
// $options->logoSpaceHeight = 19;
|
||||||
|
// $options->logoSpaceStartX = 25;
|
||||||
|
// $options->logoSpaceStartY = 25;
|
||||||
|
// $options->eccLevel = EccLevel::H;
|
||||||
|
|
||||||
|
return (new QRCode($options))->render($urlOTPAUTH);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the TOTP secret for the current user.
|
* Returns the TOTP secret for the current user.
|
||||||
* This method retrieves the secret used for generating TOTP codes.
|
* This method retrieves the secret used for generating TOTP codes.
|
||||||
@ -464,20 +513,40 @@ class Totp
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
$this->temporaryTotpObject = $this->temporarySecret !== null
|
$this->temporaryTotpObject = $this->temporarySecret !== null
|
||||||
? extTOTP::createFromSecret($this->temporarySecret, $this->clock)
|
? $this->createTotpObject($this->temporarySecret)
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
$this->totpObject = $this->secret !== null
|
$this->totpObject = $this->secret !== null
|
||||||
? extTOTP::createFromSecret($this->secret, $this->clock)
|
? $this->createTotpObject($this->secret)
|
||||||
: null;
|
: null;
|
||||||
} else {
|
} else {
|
||||||
$this->temporaryTotpObject = extTOTP::generate($this->clock);
|
$this->temporaryTotpObject = $this->createTotpObject();
|
||||||
$this->temporarySecret = $this->temporaryTotpObject->getSecret();
|
$this->temporarySecret = $this->temporaryTotpObject->getSecret();
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a TOTP object with the given secret.
|
||||||
|
* If no secret is provided, a new TOTP object is generated.
|
||||||
|
* This method sets the label and issuer for the TOTP object.
|
||||||
|
*
|
||||||
|
* @param string|null $secret The TOTP secret to use, or null to generate a new one
|
||||||
|
* @return extTOTP The created TOTP object
|
||||||
|
*/
|
||||||
|
private function createTotpObject(string $secret = null): extTOTP
|
||||||
|
{
|
||||||
|
$totpObject = ($secret === null)
|
||||||
|
? extTOTP::generate($this->clock)
|
||||||
|
: extTOTP::createFromSecret($secret, $this->clock);
|
||||||
|
|
||||||
|
$totpObject->setLabel(self::TOTP_LABEL);
|
||||||
|
$totpObject->setIssuer($this->username);
|
||||||
|
|
||||||
|
return $totpObject;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Makes the temporary TOTP object permanent.
|
* Makes the temporary TOTP object permanent.
|
||||||
* This method updates the main TOTP object and clears the temporary state.
|
* This method updates the main TOTP object and clears the temporary state.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user