mirror of
https://github.com/IT4Change/gradido.git
synced 2025-12-13 07:45:54 +00:00
update code to work together for setting new password and update encrypted privkey and password hash in db
This commit is contained in:
parent
e7624382ae
commit
c44184f823
@ -128,6 +128,34 @@ AuthenticatedEncryption::ResultType AuthenticatedEncryption::decrypt(const Memor
|
|||||||
return AUTH_DECRYPT_OK;
|
return AUTH_DECRYPT_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AuthenticatedEncryption::ResultType AuthenticatedEncryption::decrypt(const std::vector<unsigned char>& encryptedMessage, MemoryBin** message) const
|
||||||
|
{
|
||||||
|
assert(message);
|
||||||
|
std::shared_lock<std::shared_mutex> _lock(mWorkingMutex);
|
||||||
|
|
||||||
|
if (!mEncryptionKey) {
|
||||||
|
return AUTH_NO_KEY;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t decryptSize = encryptedMessage.size() - crypto_secretbox_MACBYTES;
|
||||||
|
//unsigned char* decryptBuffer = (unsigned char*)malloc(decryptSize);
|
||||||
|
auto mm = MemoryManager::getInstance();
|
||||||
|
//ObfusArray* decryptedData = new ObfusArray(decryptSize);
|
||||||
|
auto decryptedData = mm->getFreeMemory(decryptSize);
|
||||||
|
unsigned char nonce[crypto_secretbox_NONCEBYTES];
|
||||||
|
// we use a hardcoded value for nonce
|
||||||
|
// TODO: use a dynamic value, save it along with the other parameters
|
||||||
|
memset(nonce, 31, crypto_secretbox_NONCEBYTES);
|
||||||
|
|
||||||
|
if (crypto_secretbox_open_easy(*decryptedData, encryptedMessage.data(), encryptedMessage.size(), nonce, *mEncryptionKey)) {
|
||||||
|
mm->releaseMemory(decryptedData);
|
||||||
|
return AUTH_DECRYPT_MESSAGE_FAILED;
|
||||||
|
}
|
||||||
|
*message = decryptedData;
|
||||||
|
|
||||||
|
return AUTH_DECRYPT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
const char* AuthenticatedEncryption::getErrorMessage(ResultType type)
|
const char* AuthenticatedEncryption::getErrorMessage(ResultType type)
|
||||||
{
|
{
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
|||||||
@ -5,6 +5,7 @@
|
|||||||
#include "../SingletonManager/MemoryManager.h"
|
#include "../SingletonManager/MemoryManager.h"
|
||||||
|
|
||||||
#include <shared_mutex>
|
#include <shared_mutex>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
*
|
*
|
||||||
@ -59,6 +60,11 @@ public:
|
|||||||
ResultType encrypt(const MemoryBin* message, MemoryBin** encryptedMessage) const;
|
ResultType encrypt(const MemoryBin* message, MemoryBin** encryptedMessage) const;
|
||||||
|
|
||||||
ResultType decrypt(const MemoryBin* encryptedMessage, MemoryBin** message) const;
|
ResultType decrypt(const MemoryBin* encryptedMessage, MemoryBin** message) const;
|
||||||
|
//! \brief same as the other decrypt only in other format
|
||||||
|
//! \param encryptedMessage format from Poco Binary Data from DB, like returned from model/table/user for encrypted private key
|
||||||
|
//!
|
||||||
|
//! double code, I don't know how to prevent without unnecessary copy of encryptedMessage
|
||||||
|
ResultType decrypt(const std::vector<unsigned char>& encryptedMessage, MemoryBin** message) const;
|
||||||
|
|
||||||
static const char* getErrorMessage(ResultType type);
|
static const char* getErrorMessage(ResultType type);
|
||||||
|
|
||||||
|
|||||||
@ -8,10 +8,12 @@
|
|||||||
|
|
||||||
#include "Passphrase.h"
|
#include "Passphrase.h"
|
||||||
|
|
||||||
KeyPairEd25519::KeyPairEd25519(MemoryBin* privateKey, const unsigned char* publicKey)
|
KeyPairEd25519::KeyPairEd25519(MemoryBin* privateKey)
|
||||||
: mSodiumSecret(privateKey)
|
: mSodiumSecret(privateKey)
|
||||||
{
|
{
|
||||||
memcpy(mSodiumPublic, publicKey, crypto_sign_PUBLICKEYBYTES);
|
//memcpy(mSodiumPublic, publicKey, crypto_sign_PUBLICKEYBYTES);
|
||||||
|
// read pubkey from private key, so we are sure it is the correct pubkey for the private key
|
||||||
|
crypto_sign_ed25519_sk_to_pk(*privateKey, mSodiumPublic);
|
||||||
}
|
}
|
||||||
|
|
||||||
KeyPairEd25519::KeyPairEd25519(const unsigned char* publicKey)
|
KeyPairEd25519::KeyPairEd25519(const unsigned char* publicKey)
|
||||||
|
|||||||
@ -21,7 +21,7 @@ class KeyPairEd25519 : public IKeyPair
|
|||||||
public:
|
public:
|
||||||
//! \param privateKey: take ownership, release after object destruction
|
//! \param privateKey: take ownership, release after object destruction
|
||||||
//! \param publicKey: copy
|
//! \param publicKey: copy
|
||||||
KeyPairEd25519(MemoryBin* privateKey, const unsigned char* publicKey);
|
KeyPairEd25519(MemoryBin* privateKey);
|
||||||
KeyPairEd25519(const unsigned char* publicKey);
|
KeyPairEd25519(const unsigned char* publicKey);
|
||||||
|
|
||||||
~KeyPairEd25519();
|
~KeyPairEd25519();
|
||||||
@ -38,10 +38,16 @@ public:
|
|||||||
inline bool isTheSame(const KeyPairEd25519& b) const {
|
inline bool isTheSame(const KeyPairEd25519& b) const {
|
||||||
return 0 == sodium_memcmp(mSodiumPublic, b.mSodiumPublic, crypto_sign_PUBLICKEYBYTES);
|
return 0 == sodium_memcmp(mSodiumPublic, b.mSodiumPublic, crypto_sign_PUBLICKEYBYTES);
|
||||||
}
|
}
|
||||||
|
inline bool isTheSame(const unsigned char* pubkey) const {
|
||||||
|
return 0 == sodium_memcmp(mSodiumPublic, pubkey, crypto_sign_PUBLICKEYBYTES);
|
||||||
|
}
|
||||||
|
|
||||||
inline bool operator == (const KeyPairEd25519& b) const { return isTheSame(b); }
|
inline bool operator == (const KeyPairEd25519& b) const { return isTheSame(b); }
|
||||||
inline bool operator != (const KeyPairEd25519& b) const { return !isTheSame(b); }
|
inline bool operator != (const KeyPairEd25519& b) const { return !isTheSame(b); }
|
||||||
|
|
||||||
|
inline bool operator == (const unsigned char* b) const { return isTheSame(b); }
|
||||||
|
inline bool operator != (const unsigned char* b) const { return !isTheSame(b); }
|
||||||
|
|
||||||
inline bool hasPrivateKey() const { return mSodiumSecret != nullptr; }
|
inline bool hasPrivateKey() const { return mSodiumSecret != nullptr; }
|
||||||
|
|
||||||
//! \brief only way to get a private key.. encrypted
|
//! \brief only way to get a private key.. encrypted
|
||||||
|
|||||||
@ -207,7 +207,7 @@ void AdminUserPasswordReset::handleRequest(Poco::Net::HTTPServerRequest& request
|
|||||||
responseStream << "</li>\n";
|
responseStream << "</li>\n";
|
||||||
responseStream << "\t\t\t<li>Private Key verschlüsselt: ";
|
responseStream << "\t\t\t<li>Private Key verschlüsselt: ";
|
||||||
#line 92 "F:\\Gradido\\gradido_login_server\\src\\cpsp\\adminUserPasswordReset.cpsp"
|
#line 92 "F:\\Gradido\\gradido_login_server\\src\\cpsp\\adminUserPasswordReset.cpsp"
|
||||||
responseStream << ( std::to_string(userModel->existPrivateKeyCrypted()) );
|
responseStream << ( std::to_string(userModel->hasPrivateKeyEncrypted()) );
|
||||||
responseStream << "</li>\n";
|
responseStream << "</li>\n";
|
||||||
responseStream << "\t\t\t<li>Passwort gesetzt: ";
|
responseStream << "\t\t\t<li>Passwort gesetzt: ";
|
||||||
#line 93 "F:\\Gradido\\gradido_login_server\\src\\cpsp\\adminUserPasswordReset.cpsp"
|
#line 93 "F:\\Gradido\\gradido_login_server\\src\\cpsp\\adminUserPasswordReset.cpsp"
|
||||||
|
|||||||
@ -3,10 +3,12 @@
|
|||||||
#include "sodium.h"
|
#include "sodium.h"
|
||||||
|
|
||||||
#include "../SingletonManager/SessionManager.h"
|
#include "../SingletonManager/SessionManager.h"
|
||||||
|
#include "../lib/DataTypeConverter.h"
|
||||||
|
|
||||||
|
|
||||||
namespace controller {
|
namespace controller {
|
||||||
User::User(model::table::User* dbModel)
|
User::User(model::table::User* dbModel)
|
||||||
: mPassword(nullptr)
|
: mPassword(nullptr), mGradidoKeyPair(nullptr)
|
||||||
{
|
{
|
||||||
mDBModel = dbModel;
|
mDBModel = dbModel;
|
||||||
}
|
}
|
||||||
@ -15,6 +17,11 @@ namespace controller {
|
|||||||
{
|
{
|
||||||
if (mPassword) {
|
if (mPassword) {
|
||||||
delete mPassword;
|
delete mPassword;
|
||||||
|
mPassword = nullptr;
|
||||||
|
}
|
||||||
|
if (mGradidoKeyPair) {
|
||||||
|
delete mGradidoKeyPair;
|
||||||
|
mGradidoKeyPair = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,4 +122,45 @@ namespace controller {
|
|||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int User::setPassword(AuthenticatedEncryption* passwd)
|
||||||
|
{
|
||||||
|
std::unique_lock<std::shared_mutex> _lock(mSharedMutex);
|
||||||
|
auto model = getModel();
|
||||||
|
const static char* function_name = "controller::User::setPassword";
|
||||||
|
|
||||||
|
if (mPassword)
|
||||||
|
{
|
||||||
|
if (mPassword == passwd) return 0;
|
||||||
|
// if password exist but gradido key pair not, try to load key pair
|
||||||
|
if ((!mGradidoKeyPair || !mGradidoKeyPair->hasPrivateKey()) && model->hasPrivateKeyEncrypted()) {
|
||||||
|
//if (!mGradidoKeyPair) mGradidoKeyPair = new KeyPairEd25519;
|
||||||
|
MemoryBin* clear_private_key = nullptr;
|
||||||
|
if (AuthenticatedEncryption::AUTH_DECRYPT_OK == mPassword->decrypt(model->getPrivateKeyEncrypted(), &clear_private_key)) {
|
||||||
|
if (mGradidoKeyPair) delete mGradidoKeyPair;
|
||||||
|
mGradidoKeyPair = new KeyPairEd25519(clear_private_key);
|
||||||
|
|
||||||
|
// check if saved pubkey and from private key extracted pubkey match
|
||||||
|
if (*mGradidoKeyPair != model->getPublicKey()) {
|
||||||
|
delete mGradidoKeyPair;
|
||||||
|
mGradidoKeyPair = nullptr;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delete passwd;
|
||||||
|
}
|
||||||
|
// replace old password with new
|
||||||
|
mPassword = passwd;
|
||||||
|
|
||||||
|
// set new encrypted password and hash
|
||||||
|
model->setPasswordHashed(mPassword->getKeyHashed());
|
||||||
|
auto encryptedPrivateKey = mGradidoKeyPair->getCryptedPrivKey(mPassword);
|
||||||
|
model->setPrivateKey(encryptedPrivateKey);
|
||||||
|
MemoryManager::getInstance()->releaseMemory(encryptedPrivateKey);
|
||||||
|
|
||||||
|
// save changes to db
|
||||||
|
return model->updatePrivkeyAndPasswordHash();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -2,7 +2,8 @@
|
|||||||
#define GRADIDO_LOGIN_SERVER_CONTROLLER_USER_INCLUDE
|
#define GRADIDO_LOGIN_SERVER_CONTROLLER_USER_INCLUDE
|
||||||
|
|
||||||
#include "../model/table/User.h"
|
#include "../model/table/User.h"
|
||||||
#include "../Crypto/AuthenticatedEncryption.h"
|
//#include "../Crypto/AuthenticatedEncryption.h"
|
||||||
|
#include "../Crypto/KeyPairEd25519.h"
|
||||||
|
|
||||||
#include <shared_mutex>
|
#include <shared_mutex>
|
||||||
|
|
||||||
@ -42,13 +43,12 @@ namespace controller {
|
|||||||
|
|
||||||
// ***********************************************************************************
|
// ***********************************************************************************
|
||||||
// password related
|
// password related
|
||||||
//! \brief
|
//! \brief set authenticated encryption and save hash in db, should also re encrypt private key if exist
|
||||||
//! \param passwd take owner ship
|
//! \param passwd take owner ship
|
||||||
inline void setPassword(AuthenticatedEncryption* passwd) {
|
//! \return 0 = new and current passwords are the same
|
||||||
std::unique_lock<std::shared_mutex> _lock(mSharedMutex);
|
//! \return 1 = password changed, private key re-encrypted and saved into db
|
||||||
if (mPassword) delete passwd;
|
//! \return -1 = stored pubkey and private key didn't match
|
||||||
mPassword = passwd;
|
int setPassword(AuthenticatedEncryption* passwd);
|
||||||
}
|
|
||||||
|
|
||||||
inline const AuthenticatedEncryption* getPassword() {
|
inline const AuthenticatedEncryption* getPassword() {
|
||||||
std::shared_lock<std::shared_mutex> _lock(mSharedMutex);
|
std::shared_lock<std::shared_mutex> _lock(mSharedMutex);
|
||||||
@ -60,6 +60,7 @@ namespace controller {
|
|||||||
std::string mPublicHex;
|
std::string mPublicHex;
|
||||||
|
|
||||||
AuthenticatedEncryption* mPassword;
|
AuthenticatedEncryption* mPassword;
|
||||||
|
KeyPairEd25519* mGradidoKeyPair;
|
||||||
|
|
||||||
mutable std::shared_mutex mSharedMutex;
|
mutable std::shared_mutex mSharedMutex;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -426,8 +426,8 @@ User::User(Poco::AutoPtr<controller::User> ctrl_user)
|
|||||||
mPublicHex = std::string((char*)(*hexStringTemp));
|
mPublicHex = std::string((char*)(*hexStringTemp));
|
||||||
mm->releaseMemory(hexStringTemp);
|
mm->releaseMemory(hexStringTemp);
|
||||||
}
|
}
|
||||||
if (model->existPrivateKeyCrypted()) {
|
if (model->hasPrivateKeyEncrypted()) {
|
||||||
auto privKeyVetor = model->getPrivateKeyCrypted();
|
auto privKeyVetor = model->getPrivateKeyEncrypted();
|
||||||
mPrivateKey = mm->getFreeMemory(privKeyVetor.size());
|
mPrivateKey = mm->getFreeMemory(privKeyVetor.size());
|
||||||
memcpy(*mPrivateKey, privKeyVetor.data(), privKeyVetor.size());
|
memcpy(*mPrivateKey, privKeyVetor.data(), privKeyVetor.size());
|
||||||
}
|
}
|
||||||
|
|||||||
@ -143,7 +143,10 @@ namespace model {
|
|||||||
size_t User::updatePrivkey()
|
size_t User::updatePrivkey()
|
||||||
{
|
{
|
||||||
lock();
|
lock();
|
||||||
if (mPrivateKey.isNull()) return 0;
|
if (mPrivateKey.isNull()) {
|
||||||
|
unlock();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
auto result = updateIntoDB("privkey", mPrivateKey.value());
|
auto result = updateIntoDB("privkey", mPrivateKey.value());
|
||||||
unlock();
|
unlock();
|
||||||
return result;
|
return result;
|
||||||
@ -151,12 +154,45 @@ namespace model {
|
|||||||
size_t User::updatePublickey()
|
size_t User::updatePublickey()
|
||||||
{
|
{
|
||||||
lock();
|
lock();
|
||||||
if (mPublicKey.isNull()) return 0;
|
if (mPublicKey.isNull()) {
|
||||||
|
unlock();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
auto result = updateIntoDB("pubkey", mPublicKey.value());
|
auto result = updateIntoDB("pubkey", mPublicKey.value());
|
||||||
unlock();
|
unlock();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t User::updatePrivkeyAndPasswordHash()
|
||||||
|
{
|
||||||
|
lock();
|
||||||
|
if (mPrivateKey.isNull() || !mPasswordHashed || !mID) {
|
||||||
|
unlock();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
auto cm = ConnectionManager::getInstance();
|
||||||
|
auto session = cm->getConnection(CONNECTION_MYSQL_LOGIN_SERVER);
|
||||||
|
|
||||||
|
Poco::Data::Statement update(session);
|
||||||
|
|
||||||
|
update << "UPDATE users SET password = ?, privkey = ? where id = ?;",
|
||||||
|
bind(mPasswordHashed), use(mPrivateKey), use(mID);
|
||||||
|
|
||||||
|
|
||||||
|
size_t resultCount = 0;
|
||||||
|
try {
|
||||||
|
return update.execute();
|
||||||
|
}
|
||||||
|
catch (Poco::Exception& ex) {
|
||||||
|
lock("User::updatePrivkeyAndPasswordHash");
|
||||||
|
addError(new ParamError(getTableName(), "mysql error by insert", ex.displayText().data()));
|
||||||
|
addError(new ParamError(getTableName(), "data set: ", toString().data()));
|
||||||
|
unlock();
|
||||||
|
}
|
||||||
|
//printf("data valid: %s\n", toString().data());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
std::string mEmail;
|
std::string mEmail;
|
||||||
|
|||||||
@ -45,7 +45,7 @@ namespace model {
|
|||||||
// specific db operation
|
// specific db operation
|
||||||
size_t updatePrivkey();
|
size_t updatePrivkey();
|
||||||
size_t updatePublickey();
|
size_t updatePublickey();
|
||||||
|
size_t updatePrivkeyAndPasswordHash();
|
||||||
|
|
||||||
// default getter unlocked
|
// default getter unlocked
|
||||||
inline const std::string& getEmail() const { return mEmail; }
|
inline const std::string& getEmail() const { return mEmail; }
|
||||||
@ -56,8 +56,8 @@ namespace model {
|
|||||||
inline const unsigned char* getPublicKey() const { if (mPublicKey.isNull()) return nullptr; return mPublicKey.value().content().data(); }
|
inline const unsigned char* getPublicKey() const { if (mPublicKey.isNull()) return nullptr; return mPublicKey.value().content().data(); }
|
||||||
std::string getPublicKeyHex() const;
|
std::string getPublicKeyHex() const;
|
||||||
|
|
||||||
inline bool existPrivateKeyCrypted() const { return !mPrivateKey.isNull(); }
|
inline bool hasPrivateKeyEncrypted() const { return !mPrivateKey.isNull(); }
|
||||||
inline const std::vector<unsigned char>& getPrivateKeyCrypted() const { return mPrivateKey.value().content(); }
|
inline const std::vector<unsigned char>& getPrivateKeyEncrypted() const { return mPrivateKey.value().content(); }
|
||||||
inline bool isEmailChecked() const { return mEmailChecked; }
|
inline bool isEmailChecked() const { return mEmailChecked; }
|
||||||
inline const std::string& getLanguageKey() const { return mLanguageKey; }
|
inline const std::string& getLanguageKey() const { return mLanguageKey; }
|
||||||
|
|
||||||
|
|||||||
@ -4,6 +4,8 @@
|
|||||||
#include "../SingletonManager/SingletonTaskObserver.h"
|
#include "../SingletonManager/SingletonTaskObserver.h"
|
||||||
#include "../SingletonManager/ErrorManager.h"
|
#include "../SingletonManager/ErrorManager.h"
|
||||||
|
|
||||||
|
#include "../lib/Profiler.h"
|
||||||
|
|
||||||
AuthenticatedEncryptionCreateKeyTask::AuthenticatedEncryptionCreateKeyTask(Poco::AutoPtr<controller::User> user, const std::string& passwd)
|
AuthenticatedEncryptionCreateKeyTask::AuthenticatedEncryptionCreateKeyTask(Poco::AutoPtr<controller::User> user, const std::string& passwd)
|
||||||
: UniLib::controller::CPUTask(ServerConfig::g_CryptoCPUScheduler), mUser(user), mPassword(passwd)
|
: UniLib::controller::CPUTask(ServerConfig::g_CryptoCPUScheduler), mUser(user), mPassword(passwd)
|
||||||
{
|
{
|
||||||
@ -20,6 +22,7 @@ int AuthenticatedEncryptionCreateKeyTask::run()
|
|||||||
auto em = ErrorManager::getInstance();
|
auto em = ErrorManager::getInstance();
|
||||||
const static char* function_name = "AuthenticatedEncryptionCreateKeyTask::run";
|
const static char* function_name = "AuthenticatedEncryptionCreateKeyTask::run";
|
||||||
auto authenticated_encryption = new AuthenticatedEncryption;
|
auto authenticated_encryption = new AuthenticatedEncryption;
|
||||||
|
Profiler timeUsed;
|
||||||
if (AuthenticatedEncryption::AUTH_ENCRYPT_OK != authenticated_encryption->createKey(mUser->getModel()->getEmail(), mPassword)) {
|
if (AuthenticatedEncryption::AUTH_ENCRYPT_OK != authenticated_encryption->createKey(mUser->getModel()->getEmail(), mPassword)) {
|
||||||
em->addError(new Error(function_name, "error creating key"));
|
em->addError(new Error(function_name, "error creating key"));
|
||||||
em->addError(new ParamError(function_name, "for email", mUser->getModel()->getEmail()));
|
em->addError(new ParamError(function_name, "for email", mUser->getModel()->getEmail()));
|
||||||
@ -27,7 +30,10 @@ int AuthenticatedEncryptionCreateKeyTask::run()
|
|||||||
em->sendErrorsAsEmail();
|
em->sendErrorsAsEmail();
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
printf("create password time: %s\n", timeUsed.string().data());
|
||||||
|
timeUsed.reset();
|
||||||
mUser->setPassword(authenticated_encryption);
|
mUser->setPassword(authenticated_encryption);
|
||||||
|
printf("set password time: %s\n", timeUsed.string().data());
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -89,7 +89,7 @@ enum PageState
|
|||||||
<li><%= userModel->getEmail() %></li>
|
<li><%= userModel->getEmail() %></li>
|
||||||
<li>Public Key: <%= userModel->getPublicKeyHex() %></li>
|
<li>Public Key: <%= userModel->getPublicKeyHex() %></li>
|
||||||
<li>E-Mail überprüft: <%= std::to_string(userModel->isEmailChecked()) %></li>
|
<li>E-Mail überprüft: <%= std::to_string(userModel->isEmailChecked()) %></li>
|
||||||
<li>Private Key verschlüsselt: <%= std::to_string(userModel->existPrivateKeyCrypted()) %></li>
|
<li>Private Key verschlüsselt: <%= std::to_string(userModel->hasPrivateKeyEncrypted()) %></li>
|
||||||
<li>Passwort gesetzt: <%= std::to_string(userModel->getPasswordHashed() != 0) %></li>
|
<li>Passwort gesetzt: <%= std::to_string(userModel->getPasswordHashed() != 0) %></li>
|
||||||
</ul>
|
</ul>
|
||||||
<% } %>
|
<% } %>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user