From 9e47ae36a4f9f1649fe81f760b95ced5d38c38b6 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Wed, 2 Jun 2021 13:05:38 +0200 Subject: [PATCH] add username and description to api (+ doc) --- docu/login_server.api.md | 15 +++++++++- login_server/skeema/gradido_login/users.sql | 1 + .../src/cpp/JSONInterface/JsonCreateUser.cpp | 22 ++++++++++++++- .../cpp/JSONInterface/JsonGetUserInfos.cpp | 6 ++++ .../cpp/JSONInterface/JsonUpdateUserInfos.cpp | 26 +++++++++++++++++ login_server/src/cpp/controller/User.cpp | 6 ++++ login_server/src/cpp/controller/User.h | 2 ++ login_server/src/cpp/model/table/User.cpp | 28 +++++++++++-------- login_server/src/cpp/model/table/User.h | 8 ++++-- 9 files changed, 99 insertions(+), 15 deletions(-) diff --git a/docu/login_server.api.md b/docu/login_server.api.md index 70f19cdde..6ec1b36fe 100644 --- a/docu/login_server.api.md +++ b/docu/login_server.api.md @@ -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, diff --git a/login_server/skeema/gradido_login/users.sql b/login_server/skeema/gradido_login/users.sql index 9de1bad49..bedb5c2d4 100644 --- a/login_server/skeema/gradido_login/users.sql +++ b/login_server/skeema/gradido_login/users.sql @@ -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, diff --git a/login_server/src/cpp/JSONInterface/JsonCreateUser.cpp b/login_server/src/cpp/JSONInterface/JsonCreateUser.cpp index 13ca5122f..64e112c27 100644 --- a/login_server/src/cpp/JSONInterface/JsonCreateUser.cpp +++ b/login_server/src/cpp/JSONInterface/JsonCreateUser.cpp @@ -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; diff --git a/login_server/src/cpp/JSONInterface/JsonGetUserInfos.cpp b/login_server/src/cpp/JSONInterface/JsonGetUserInfos.cpp index 2b1f229de..3bdafd283 100644 --- a/login_server/src/cpp/JSONInterface/JsonGetUserInfos.cpp +++ b/login_server/src/cpp/JSONInterface/JsonGetUserInfos.cpp @@ -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()); } diff --git a/login_server/src/cpp/JSONInterface/JsonUpdateUserInfos.cpp b/login_server/src/cpp/JSONInterface/JsonUpdateUserInfos.cpp index 40ec44b16..7db31df33 100644 --- a/login_server/src/cpp/JSONInterface/JsonUpdateUserInfos.cpp +++ b/login_server/src/cpp/JSONInterface/JsonUpdateUserInfos.cpp @@ -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; diff --git a/login_server/src/cpp/controller/User.cpp b/login_server/src/cpp/controller/User.cpp index a7cecd92a..271d0feae 100644 --- a/login_server/src/cpp/controller/User.cpp +++ b/login_server/src/cpp/controller/User.cpp @@ -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); diff --git a/login_server/src/cpp/controller/User.h b/login_server/src/cpp/controller/User.h index 649fc1742..6063f87be 100644 --- a/login_server/src/cpp/controller/User.h +++ b/login_server/src/cpp/controller/User.h @@ -47,6 +47,8 @@ namespace controller { static std::vector 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. diff --git a/login_server/src/cpp/model/table/User.cpp b/login_server/src/cpp/model/table/User.cpp index 0a69ac324..24f1371ee 100644 --- a/login_server/src/cpp/model/table/User.cpp +++ b/login_server/src/cpp/model/table/User.cpp @@ -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, 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, 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 << "" << mUsername << "
"; ss << "" << mFirstName << " " << mLastName << " <" << mEmail << ">" << "
"; + ss << "username: " << mUsername << "
"; + ss << "details: " << mDescription << "
"; ss << "public key: " << (char*)*pubkeyHex << "
"; ss << "email hash: " << (char*)*email_hash << "
"; ss << "created: " << Poco::DateTimeFormatter::format(mCreated, "%f.%m.%Y %H:%M:%S") << "
"; @@ -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(); diff --git a/login_server/src/cpp/model/table/User.h b/login_server/src/cpp/model/table/User.h index 876803cf8..dd51d7498 100644 --- a/login_server/src/cpp/model/table/User.h +++ b/login_server/src/cpp/model/table/User.h @@ -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, Poco::DateTime, int, int, int> UserTuple; + typedef Poco::Tuple, 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 + " " + mLastName + " <" + mEmail + ">"; } 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 mPublicKey;