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",
"user": {
"created": 1614782270,
"description": "",
"disabled": false,
"email": "max.musterman@gmail.de",
"email_checked": true,
@ -69,6 +70,7 @@ In case of success returns:
- `user`: contain user object
- `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
- `email`: email of user
- `email_checked`: true if user has successfully clicked on activation link in email
@ -96,7 +98,9 @@ with:
{
"email":"max.musterman@gmail.de",
"first_name":"Max",
"last_name":"Musterman" ,
"last_name":"Musterman",
"username": "Maxilein",
"description": "Tischler",
"emailType": 2,
"group_id": 1,
"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
- 2: default, if user has registered directly
- 5: if user was registered by an admin
@ -167,6 +173,8 @@ with:
"update": {
"User.first_name": "Max",
"User.last_name" : "Musterman",
"User.username" : "Maxilein",
"User.description" : "Tischler",
"User.disabled": 0,
"User.language": "de",
"User.password": "1234"
@ -237,6 +245,8 @@ with:
"user.pubkeyhex",
"user.first_name",
"user.last_name",
"user.username",
"user.description",
"user.disabled",
"user.email_checked",
"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.first_name`: first 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
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
@ -298,6 +310,7 @@ In case of success returns:
"info":[],
"user": {
"created": 1614782270,
"description": "Tischler"
"disabled": false,
"email": "max.musterman@gmail.de",
"email_checked": true,

View File

@ -4,6 +4,7 @@ CREATE TABLE `users` (
`first_name` varchar(150) NOT NULL,
`last_name` varchar(255) DEFAULT '',
`username` varchar(255) DEFAULT '',
`description` text DEFAULT '',
`password` bigint unsigned DEFAULT '0',
`pubkey` binary(32) 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 last_name;
std::string password;
std::string username;
std::string description;
bool login_after_register = false;
int emailType;
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("last_name").convert(last_name);
paramJsonObject->get("emailType").convert(emailType);
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()) {
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)) {
paramJsonObject->get("password").convert(password);
}
if (!paramJsonObject->isNull("login_after_register")) {
paramJsonObject->get("login_after_register").convert(login_after_register);
}
}
catch (Poco::Exception& ex) {
return stateError("json exception", ex.displayText());
@ -85,6 +96,15 @@ Poco::JSON::Object* JsonCreateUser::handle(Poco::Dynamic::Var params)
group_was_not_set = true;
}
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();
Session* session = nullptr;

View File

@ -121,6 +121,12 @@ Poco::JSON::Object* JsonGetUserInfos::handle(Poco::Dynamic::Var params)
else if (parameterString == "user.last_name") {
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") {
jsonUser.set("disabled", user_model->isDisabled());
}

View File

@ -86,6 +86,32 @@ Poco::JSON::Object* JsonUpdateUserInfos::handle(Poco::Dynamic::Var params)
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) {
if (value.isBoolean()) {
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)
{
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");
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
//!
//! 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)
: ModelBase(tuple.get<0>()),
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)
{
@ -82,12 +83,12 @@ namespace model {
if (mPasswordHashed) {
insert << "INSERT INTO users (email, first_name, last_name, username, password, email_hash, language, group_id) VALUES(?,?,?,?,?,?,?,?);",
use(mEmail), use(mFirstName), use(mLastName), use(mUsername), bind(mPasswordHashed), use(mEmailHash), use(mLanguageKey), use(mGroupId);
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), use(mDescription), bind(mPasswordHashed), use(mEmailHash), use(mLanguageKey), use(mGroupId);
}
else {
insert << "INSERT INTO users (email, first_name, last_name, username, email_hash, language, group_id) VALUES(?,?,?,?,?,?,?);",
use(mEmail), use(mFirstName), use(mLastName), use(mUsername), use(mEmailHash), use(mLanguageKey), use(mGroupId);
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(mDescription), use(mEmailHash), use(mLanguageKey), use(mGroupId);
}
return insert;
@ -100,11 +101,11 @@ namespace model {
_fieldName = getTableName() + std::string(".id");
}
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()
<< " LEFT JOIN user_roles ON " << getTableName() << ".id = user_roles.user_id "
<< " 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(mLanguageKey), into(mDisabled), into(mGroupId), into(mRole);
@ -116,7 +117,7 @@ namespace model {
{
Poco::Data::Statement select(session);
// 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 ?";
@ -132,7 +133,7 @@ namespace model {
}
// 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 ?";
if (conditionType == MYSQL_CONDITION_AND) {
for (int i = 1; i < fieldNames.size(); i++) {
@ -244,8 +245,8 @@ namespace model {
auto session = cm->getConnection(CONNECTION_MYSQL_LOGIN_SERVER);
Poco::Data::Statement update(session);
update << "UPDATE users SET first_name = ?, last_name = ?, username = ?, disabled = ?, language = ? where id = ?;",
use(mFirstName), use(mLastName), use(mUsername), use(mDisabled), use(mLanguageKey), use(mID);
update << "UPDATE users SET first_name = ?, last_name = ?, username = ?, description = ?, disabled = ?, language = ? where id = ?;",
use(mFirstName), use(mLastName), use(mUsername), use(mDescription), use(mDisabled), use(mLanguageKey), use(mID);
try {
@ -305,6 +306,8 @@ namespace model {
ss << mUsername << 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 << "public key: " << (char*)*pubkeyHex << std::endl;
ss << "private key: " << (char*)*privkeyHex << std::endl;
@ -343,6 +346,8 @@ namespace model {
ss << "<b>" << mUsername << "</b><br>";
ss << "<b>" << mFirstName << " " << mLastName << " <" << mEmail << "></b>" << "<br>";
ss << "username: " << mUsername << "<br>";
ss << "details: " << mDescription << "<br>";
ss << "public key: " << (char*)*pubkeyHex << "<br>";
ss << "email hash: " << (char*)*email_hash << "<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("email", mEmail);
userObj.set("username", mUsername);
userObj.set("description", mDescription);
//userObj.set("state", userStateToString(mState));
auto createTimeStamp = mCreated.timestamp();

View File

@ -20,6 +20,8 @@ namespace model {
USER_FIELD_EMAIL,
USER_FIELDS_FIRST_NAME,
USER_FIELDS_LAST_NAME,
USER_FIELDS_USERNAME,
USER_FIELDS_DESCRIPTION,
USER_FIELDS_PASSWORD,
USER_FIELDS_PUBLIC_KEY,
USER_FIELDS_PRIVATE_KEY,
@ -28,7 +30,7 @@ namespace model {
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
{
@ -57,6 +59,7 @@ namespace model {
inline const std::string getFirstName() const { SHARED_LOCK; return mFirstName; }
inline const std::string getLastName() const { SHARED_LOCK; return mLastName; }
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 const Poco::UInt64 getPasswordHashed() const { SHARED_LOCK; return mPasswordHashed; }
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 setLastName(const std::string& last_name) { UNIQUE_LOCK; mLastName = last_name; }
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; }
void setPublicKey(const unsigned char* publicKey);
//! \brief set encrypted private key
@ -105,7 +109,7 @@ namespace model {
std::string mFirstName;
std::string mLastName;
std::string mUsername;
std::string mDescription;
Poco::UInt64 mPasswordHashed;
Poco::Nullable<Poco::Data::BLOB> mPublicKey;