add username and description to api (+ doc)

This commit is contained in:
einhornimmond 2021-06-02 13:05:38 +02:00
parent b7ba462a5d
commit 9e47ae36a4
9 changed files with 99 additions and 15 deletions

View File

@ -50,6 +50,7 @@ In case of success returns:
"state":"success", "state":"success",
"user": { "user": {
"created": 1614782270, "created": 1614782270,
"description": "",
"disabled": false, "disabled": false,
"email": "max.musterman@gmail.de", "email": "max.musterman@gmail.de",
"email_checked": true, "email_checked": true,
@ -69,6 +70,7 @@ In case of success returns:
- `user`: contain user object - `user`: contain user object
- `created`: timestamp on which account was created - `created`: timestamp on which account was created
- `description`: description of user for other user
- `disabled`: true if account was disabled, if disabled no login or coin transfer is possible - `disabled`: true if account was disabled, if disabled no login or coin transfer is possible
- `email`: email of user - `email`: email of user
- `email_checked`: true if user has successfully clicked on activation link in email - `email_checked`: true if user has successfully clicked on activation link in email
@ -96,7 +98,9 @@ with:
{ {
"email":"max.musterman@gmail.de", "email":"max.musterman@gmail.de",
"first_name":"Max", "first_name":"Max",
"last_name":"Musterman" , "last_name":"Musterman",
"username": "Maxilein",
"description": "Tischler",
"emailType": 2, "emailType": 2,
"group_id": 1, "group_id": 1,
"password":"123abcDE&", "password":"123abcDE&",
@ -104,6 +108,8 @@ with:
} }
``` ```
- `username`: (optional), mindestens 4 Zeichen, return error if already used
- `description`: (optional), mindestens 4 Zeichen
- `emailType`: control email-text sended with email verification code - `emailType`: control email-text sended with email verification code
- 2: default, if user has registered directly - 2: default, if user has registered directly
- 5: if user was registered by an admin - 5: if user was registered by an admin
@ -167,6 +173,8 @@ with:
"update": { "update": {
"User.first_name": "Max", "User.first_name": "Max",
"User.last_name" : "Musterman", "User.last_name" : "Musterman",
"User.username" : "Maxilein",
"User.description" : "Tischler",
"User.disabled": 0, "User.disabled": 0,
"User.language": "de", "User.language": "de",
"User.password": "1234" "User.password": "1234"
@ -237,6 +245,8 @@ with:
"user.pubkeyhex", "user.pubkeyhex",
"user.first_name", "user.first_name",
"user.last_name", "user.last_name",
"user.username",
"user.description",
"user.disabled", "user.disabled",
"user.email_checked", "user.email_checked",
"user.language" "user.language"
@ -271,6 +281,8 @@ Return only the fields which are defined in request
- `user.pubkeyhex`: public key of user in hex-format - `user.pubkeyhex`: public key of user in hex-format
- `user.first_name`: first name of user - `user.first_name`: first name of user
- `user.last_name`: last name of user - `user.last_name`: last name of user
- `user.username`: username of user (min 4 Character, unique per group)
- `user.description`: profil text for user
- `user.disabled`: User will be disabled if he wants a account delete but has transactions. Until transactions are saved in real blockchain, we need this data because the public key - `user.disabled`: User will be disabled if he wants a account delete but has transactions. Until transactions are saved in real blockchain, we need this data because the public key
is in db only saved in state_users so if we delete this entry, validating all transactions is no longer possible. Disabled User cannot login and cannot receive transactions. is in db only saved in state_users so if we delete this entry, validating all transactions is no longer possible. Disabled User cannot login and cannot receive transactions.
- `email_checked`: If user has clicked on link in verification email (register), can only transfer gradidos if email_checked is 1 - `email_checked`: If user has clicked on link in verification email (register), can only transfer gradidos if email_checked is 1
@ -298,6 +310,7 @@ In case of success returns:
"info":[], "info":[],
"user": { "user": {
"created": 1614782270, "created": 1614782270,
"description": "Tischler"
"disabled": false, "disabled": false,
"email": "max.musterman@gmail.de", "email": "max.musterman@gmail.de",
"email_checked": true, "email_checked": true,

View File

@ -4,6 +4,7 @@ CREATE TABLE `users` (
`first_name` varchar(150) NOT NULL, `first_name` varchar(150) NOT NULL,
`last_name` varchar(255) DEFAULT '', `last_name` varchar(255) DEFAULT '',
`username` varchar(255) DEFAULT '', `username` varchar(255) DEFAULT '',
`description` text DEFAULT '',
`password` bigint unsigned DEFAULT '0', `password` bigint unsigned DEFAULT '0',
`pubkey` binary(32) DEFAULT NULL, `pubkey` binary(32) DEFAULT NULL,
`privkey` binary(80) DEFAULT NULL, `privkey` binary(80) DEFAULT NULL,

View File

@ -15,6 +15,8 @@ Poco::JSON::Object* JsonCreateUser::handle(Poco::Dynamic::Var params)
std::string first_name; std::string first_name;
std::string last_name; std::string last_name;
std::string password; std::string password;
std::string username;
std::string description;
bool login_after_register = false; bool login_after_register = false;
int emailType; int emailType;
int group_id = 1; int group_id = 1;
@ -36,18 +38,27 @@ Poco::JSON::Object* JsonCreateUser::handle(Poco::Dynamic::Var params)
paramJsonObject->get("first_name").convert(first_name); paramJsonObject->get("first_name").convert(first_name);
paramJsonObject->get("last_name").convert(last_name); paramJsonObject->get("last_name").convert(last_name);
paramJsonObject->get("emailType").convert(emailType); paramJsonObject->get("emailType").convert(emailType);
auto group_id_obj = paramJsonObject->get("group_id"); auto group_id_obj = paramJsonObject->get("group_id");
auto username_obj = paramJsonObject->get("username");
auto description_obj = paramJsonObject->get("description");
if(!group_id_obj.isEmpty()) { if(!group_id_obj.isEmpty()) {
group_id_obj.convert(group_id); group_id_obj.convert(group_id);
} }
if (!username_obj.isEmpty()) {
username_obj.convert(username);
}
if (!description_obj.isEmpty()) {
description_obj.convert(description);
}
if ((ServerConfig::g_AllowUnsecureFlags & ServerConfig::UNSECURE_PASSWORD_REQUESTS)) { if ((ServerConfig::g_AllowUnsecureFlags & ServerConfig::UNSECURE_PASSWORD_REQUESTS)) {
paramJsonObject->get("password").convert(password); paramJsonObject->get("password").convert(password);
} }
if (!paramJsonObject->isNull("login_after_register")) { if (!paramJsonObject->isNull("login_after_register")) {
paramJsonObject->get("login_after_register").convert(login_after_register); paramJsonObject->get("login_after_register").convert(login_after_register);
} }
} }
catch (Poco::Exception& ex) { catch (Poco::Exception& ex) {
return stateError("json exception", ex.displayText()); return stateError("json exception", ex.displayText());
@ -85,6 +96,15 @@ Poco::JSON::Object* JsonCreateUser::handle(Poco::Dynamic::Var params)
group_was_not_set = true; group_was_not_set = true;
} }
user = controller::User::create(email, first_name, last_name, group_id); user = controller::User::create(email, first_name, last_name, group_id);
if (username.size() > 3) {
if (user->isUsernameAlreadyUsed(username)) {
return stateError("username already in use");
}
user->getModel()->setUsername(username);
}
if (description.size() > 3) {
user->getModel()->setDescription(description);
}
auto userModel = user->getModel(); auto userModel = user->getModel();
Session* session = nullptr; Session* session = nullptr;

View File

@ -121,6 +121,12 @@ Poco::JSON::Object* JsonGetUserInfos::handle(Poco::Dynamic::Var params)
else if (parameterString == "user.last_name") { else if (parameterString == "user.last_name") {
jsonUser.set("last_name", user_model->getLastName()); jsonUser.set("last_name", user_model->getLastName());
} }
else if (parameterString == "user.username") {
jsonUser.set("username", user_model->getUsername());
}
else if (parameterString == "user.description") {
jsonUser.set("description", user_model->getDescription());
}
else if (parameterString == "user.disabled") { else if (parameterString == "user.disabled") {
jsonUser.set("disabled", user_model->isDisabled()); jsonUser.set("disabled", user_model->isDisabled());
} }

View File

@ -86,6 +86,32 @@ Poco::JSON::Object* JsonUpdateUserInfos::handle(Poco::Dynamic::Var params)
extractet_values++; extractet_values++;
} }
} }
else if ("User.username" == name && value.size() > 3) {
if (!value.isString()) {
jsonErrorsArray.add("User.username isn't a string");
}
else {
auto new_username = value.toString();
if (user_model->getUsername() != new_username) {
if (user->isUsernameAlreadyUsed(new_username)) {
jsonErrorsArray.add("username already used");
}
else {
user_model->setUsername(new_username);
extractet_values++;
}
}
}
}
else if ("User.description" == name && value.size() > 3) {
if (!value.isString()) {
jsonErrorsArray.add("description isn't a string");
}
else {
user_model->setDescription(value.toString());
extractet_values++;
}
}
else if ("User.disabled" == name) { else if ("User.disabled" == name) {
if (value.isBoolean()) { if (value.isBoolean()) {
bool disabled; bool disabled;

View File

@ -105,6 +105,12 @@ namespace controller {
} }
bool User::isUsernameAlreadyUsed(const std::string& username)
{
auto db = getModel();
return db->loadFromDB({ "username", "group_id" }, username, db->getGroupId(), model::table::MYSQL_CONDITION_AND) > 0;
}
int User::load(const unsigned char* pubkey_array) int User::load(const unsigned char* pubkey_array)
{ {
Poco::Data::BLOB pubkey(pubkey_array, 32); Poco::Data::BLOB pubkey(pubkey_array, 32);

View File

@ -47,6 +47,8 @@ namespace controller {
static std::vector<User*> search(const std::string& searchString, const std::string& accountState = "all"); static std::vector<User*> search(const std::string& searchString, const std::string& accountState = "all");
bool isUsernameAlreadyUsed(const std::string& username);
//! \brief go through whole db and search users with email_checked = false and schedule resend 7 days after email_opt_in created date //! \brief go through whole db and search users with email_checked = false and schedule resend 7 days after email_opt_in created date
//! //!
//! Should be only called by server start, later it aren't necessary, because register function schedule resend tasks by himself. //! Should be only called by server start, later it aren't necessary, because register function schedule resend tasks by himself.

View File

@ -29,7 +29,8 @@ namespace model {
User::User(UserTuple tuple) User::User(UserTuple tuple)
: ModelBase(tuple.get<0>()), : ModelBase(tuple.get<0>()),
mFirstName(tuple.get<1>()), mLastName(tuple.get<2>()), mEmail(tuple.get<3>()), mUsername(tuple.get<4>()), mFirstName(tuple.get<1>()), mLastName(tuple.get<2>()), mEmail(tuple.get<3>()), mUsername(tuple.get<4>()),
mPublicKey(tuple.get<5>()), mCreated(tuple.get<6>()), mEmailChecked(tuple.get<7>()), mDisabled(tuple.get<8>()), mGroupId(tuple.get<9>()), mDescription(tuple.get<5>()),
mPublicKey(tuple.get<6>()), mCreated(tuple.get<7>()), mEmailChecked(tuple.get<8>()), mDisabled(tuple.get<9>()), mGroupId(tuple.get<10>()),
mPasswordHashed(0), mLanguageKey("de"), mRole(ROLE_NOT_LOADED) mPasswordHashed(0), mLanguageKey("de"), mRole(ROLE_NOT_LOADED)
{ {
@ -82,12 +83,12 @@ namespace model {
if (mPasswordHashed) { if (mPasswordHashed) {
insert << "INSERT INTO users (email, first_name, last_name, username, password, email_hash, language, group_id) VALUES(?,?,?,?,?,?,?,?);", insert << "INSERT INTO users (email, first_name, last_name, username, description, password, email_hash, language, group_id) VALUES(?,?,?,?,?,?,?,?);",
use(mEmail), use(mFirstName), use(mLastName), use(mUsername), bind(mPasswordHashed), use(mEmailHash), use(mLanguageKey), use(mGroupId); use(mEmail), use(mFirstName), use(mLastName), use(mUsername), use(mDescription), bind(mPasswordHashed), use(mEmailHash), use(mLanguageKey), use(mGroupId);
} }
else { else {
insert << "INSERT INTO users (email, first_name, last_name, username, email_hash, language, group_id) VALUES(?,?,?,?,?,?,?);", insert << "INSERT INTO users (email, first_name, last_name, username, description, email_hash, language, group_id) VALUES(?,?,?,?,?,?,?);",
use(mEmail), use(mFirstName), use(mLastName), use(mUsername), use(mEmailHash), use(mLanguageKey), use(mGroupId); use(mEmail), use(mFirstName), use(mLastName), use(mUsername), use(mDescription), use(mEmailHash), use(mLanguageKey), use(mGroupId);
} }
return insert; return insert;
@ -100,11 +101,11 @@ namespace model {
_fieldName = getTableName() + std::string(".id"); _fieldName = getTableName() + std::string(".id");
} }
Poco::Data::Statement select(session); Poco::Data::Statement select(session);
select << "SELECT " << getTableName() << ".id, email, first_name, last_name, username, password, pubkey, privkey, email_hash, created, email_checked, language, disabled, group_id, user_roles.role_id " select << "SELECT " << getTableName() << ".id, email, first_name, last_name, username, description, password, pubkey, privkey, email_hash, created, email_checked, language, disabled, group_id, user_roles.role_id "
<< " FROM " << getTableName() << " FROM " << getTableName()
<< " LEFT JOIN user_roles ON " << getTableName() << ".id = user_roles.user_id " << " LEFT JOIN user_roles ON " << getTableName() << ".id = user_roles.user_id "
<< " WHERE " << _fieldName << " = ?" , << " WHERE " << _fieldName << " = ?" ,
into(mID), into(mEmail), into(mFirstName), into(mLastName), into(mUsername), into(mPasswordHashed), into(mID), into(mEmail), into(mFirstName), into(mLastName), into(mUsername), into(mDescription), into(mPasswordHashed),
into(mPublicKey), into(mPrivateKey), into(mEmailHash), into(mCreated), into(mEmailChecked), into(mPublicKey), into(mPrivateKey), into(mEmailHash), into(mCreated), into(mEmailChecked),
into(mLanguageKey), into(mDisabled), into(mGroupId), into(mRole); into(mLanguageKey), into(mDisabled), into(mGroupId), into(mRole);
@ -116,7 +117,7 @@ namespace model {
{ {
Poco::Data::Statement select(session); Poco::Data::Statement select(session);
// typedef Poco::Tuple<std::string, std::string, std::string, Poco::Nullable<Poco::Data::BLOB>, int> UserTuple; // typedef Poco::Tuple<std::string, std::string, std::string, Poco::Nullable<Poco::Data::BLOB>, int> UserTuple;
select << "SELECT id, first_name, last_name, email, username, pubkey, created, email_checked, disabled, group_id FROM " << getTableName() select << "SELECT id, first_name, last_name, email, username, description, pubkey, created, email_checked, disabled, group_id FROM " << getTableName()
<< " where " << fieldName << " LIKE ?"; << " where " << fieldName << " LIKE ?";
@ -132,7 +133,7 @@ namespace model {
} }
// typedef Poco::Tuple<std::string, std::string, std::string, Poco::Nullable<Poco::Data::BLOB>, int> UserTuple; // typedef Poco::Tuple<std::string, std::string, std::string, Poco::Nullable<Poco::Data::BLOB>, int> UserTuple;
select << "SELECT id, first_name, last_name, email, username, pubkey, created, email_checked, disabled, group_id FROM " << getTableName() select << "SELECT id, first_name, last_name, email, username, description, pubkey, created, email_checked, disabled, group_id FROM " << getTableName()
<< " where " << fieldNames[0] << " LIKE ?"; << " where " << fieldNames[0] << " LIKE ?";
if (conditionType == MYSQL_CONDITION_AND) { if (conditionType == MYSQL_CONDITION_AND) {
for (int i = 1; i < fieldNames.size(); i++) { for (int i = 1; i < fieldNames.size(); i++) {
@ -244,8 +245,8 @@ namespace model {
auto session = cm->getConnection(CONNECTION_MYSQL_LOGIN_SERVER); auto session = cm->getConnection(CONNECTION_MYSQL_LOGIN_SERVER);
Poco::Data::Statement update(session); Poco::Data::Statement update(session);
update << "UPDATE users SET first_name = ?, last_name = ?, username = ?, disabled = ?, language = ? where id = ?;", update << "UPDATE users SET first_name = ?, last_name = ?, username = ?, description = ?, disabled = ?, language = ? where id = ?;",
use(mFirstName), use(mLastName), use(mUsername), use(mDisabled), use(mLanguageKey), use(mID); use(mFirstName), use(mLastName), use(mUsername), use(mDescription), use(mDisabled), use(mLanguageKey), use(mID);
try { try {
@ -305,6 +306,8 @@ namespace model {
ss << mUsername << std::endl; ss << mUsername << std::endl;
ss << mFirstName << " " << mLastName << " <" << mEmail << ">" << std::endl; ss << mFirstName << " " << mLastName << " <" << mEmail << ">" << std::endl;
ss << "username: " << mUsername << std::endl;
ss << "details: " << mDescription << std::endl;
ss << "password hash: " << mPasswordHashed << std::endl; ss << "password hash: " << mPasswordHashed << std::endl;
ss << "public key: " << (char*)*pubkeyHex << std::endl; ss << "public key: " << (char*)*pubkeyHex << std::endl;
ss << "private key: " << (char*)*privkeyHex << std::endl; ss << "private key: " << (char*)*privkeyHex << std::endl;
@ -343,6 +346,8 @@ namespace model {
ss << "<b>" << mUsername << "</b><br>"; ss << "<b>" << mUsername << "</b><br>";
ss << "<b>" << mFirstName << " " << mLastName << " <" << mEmail << "></b>" << "<br>"; ss << "<b>" << mFirstName << " " << mLastName << " <" << mEmail << "></b>" << "<br>";
ss << "username: " << mUsername << "<br>";
ss << "details: " << mDescription << "<br>";
ss << "public key: " << (char*)*pubkeyHex << "<br>"; ss << "public key: " << (char*)*pubkeyHex << "<br>";
ss << "email hash: " << (char*)*email_hash << "<br>"; ss << "email hash: " << (char*)*email_hash << "<br>";
ss << "created: " << Poco::DateTimeFormatter::format(mCreated, "%f.%m.%Y %H:%M:%S") << "<br>"; ss << "created: " << Poco::DateTimeFormatter::format(mCreated, "%f.%m.%Y %H:%M:%S") << "<br>";
@ -415,6 +420,7 @@ namespace model {
userObj.set("last_name", mLastName); userObj.set("last_name", mLastName);
userObj.set("email", mEmail); userObj.set("email", mEmail);
userObj.set("username", mUsername); userObj.set("username", mUsername);
userObj.set("description", mDescription);
//userObj.set("state", userStateToString(mState)); //userObj.set("state", userStateToString(mState));
auto createTimeStamp = mCreated.timestamp(); auto createTimeStamp = mCreated.timestamp();

View File

@ -20,6 +20,8 @@ namespace model {
USER_FIELD_EMAIL, USER_FIELD_EMAIL,
USER_FIELDS_FIRST_NAME, USER_FIELDS_FIRST_NAME,
USER_FIELDS_LAST_NAME, USER_FIELDS_LAST_NAME,
USER_FIELDS_USERNAME,
USER_FIELDS_DESCRIPTION,
USER_FIELDS_PASSWORD, USER_FIELDS_PASSWORD,
USER_FIELDS_PUBLIC_KEY, USER_FIELDS_PUBLIC_KEY,
USER_FIELDS_PRIVATE_KEY, USER_FIELDS_PRIVATE_KEY,
@ -28,7 +30,7 @@ namespace model {
USER_FIELDS_LANGUAGE USER_FIELDS_LANGUAGE
}; };
typedef Poco::Tuple<int, std::string, std::string, std::string, std::string, Poco::Nullable<Poco::Data::BLOB>, Poco::DateTime, int, int, int> UserTuple; typedef Poco::Tuple<int, std::string, std::string, std::string, std::string, std::string, Poco::Nullable<Poco::Data::BLOB>, Poco::DateTime, int, int, int> UserTuple;
class User : public ModelBase class User : public ModelBase
{ {
@ -57,6 +59,7 @@ namespace model {
inline const std::string getFirstName() const { SHARED_LOCK; return mFirstName; } inline const std::string getFirstName() const { SHARED_LOCK; return mFirstName; }
inline const std::string getLastName() const { SHARED_LOCK; return mLastName; } inline const std::string getLastName() const { SHARED_LOCK; return mLastName; }
inline const std::string getUsername() const { SHARED_LOCK; return mUsername; } inline const std::string getUsername() const { SHARED_LOCK; return mUsername; }
inline const std::string getDescription() const { SHARED_LOCK; return mDescription; }
inline std::string getNameWithEmailHtml() const { SHARED_LOCK; return mFirstName + "&nbsp;" + mLastName + "&nbsp;&lt;" + mEmail + "&gt;"; } inline std::string getNameWithEmailHtml() const { SHARED_LOCK; return mFirstName + "&nbsp;" + mLastName + "&nbsp;&lt;" + mEmail + "&gt;"; }
inline const Poco::UInt64 getPasswordHashed() const { SHARED_LOCK; return mPasswordHashed; } inline const Poco::UInt64 getPasswordHashed() const { SHARED_LOCK; return mPasswordHashed; }
inline int getGroupId() const { SHARED_LOCK; return mGroupId; } inline int getGroupId() const { SHARED_LOCK; return mGroupId; }
@ -80,6 +83,7 @@ namespace model {
inline void setFirstName(const std::string& first_name) { UNIQUE_LOCK; mFirstName = first_name; } inline void setFirstName(const std::string& first_name) { UNIQUE_LOCK; mFirstName = first_name; }
inline void setLastName(const std::string& last_name) { UNIQUE_LOCK; mLastName = last_name; } inline void setLastName(const std::string& last_name) { UNIQUE_LOCK; mLastName = last_name; }
inline void setUsername(const std::string& username) { UNIQUE_LOCK; mUsername = username; } inline void setUsername(const std::string& username) { UNIQUE_LOCK; mUsername = username; }
inline void setDescription(const std::string& description) { UNIQUE_LOCK; mDescription = description; }
inline void setPasswordHashed(const Poco::UInt64& passwordHashed) { UNIQUE_LOCK; mPasswordHashed = passwordHashed; } inline void setPasswordHashed(const Poco::UInt64& passwordHashed) { UNIQUE_LOCK; mPasswordHashed = passwordHashed; }
void setPublicKey(const unsigned char* publicKey); void setPublicKey(const unsigned char* publicKey);
//! \brief set encrypted private key //! \brief set encrypted private key
@ -105,7 +109,7 @@ namespace model {
std::string mFirstName; std::string mFirstName;
std::string mLastName; std::string mLastName;
std::string mUsername; std::string mUsername;
std::string mDescription;
Poco::UInt64 mPasswordHashed; Poco::UInt64 mPasswordHashed;
Poco::Nullable<Poco::Data::BLOB> mPublicKey; Poco::Nullable<Poco::Data::BLOB> mPublicKey;