Merge pull request #63 from gradido/update_doc

Update doc
This commit is contained in:
Ulf Gebhardt 2021-03-23 17:48:38 +01:00 committed by GitHub
commit 0886979d09
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 301 additions and 34 deletions

View File

@ -197,7 +197,7 @@ class StateBalancesController extends AppController
} }
uasort($transactions, array($this, 'sortTransactions')); uasort($transactions, array($this, 'sortTransactions'));
$this->set('transactions', $transactions); $this->set('transactions', $transactions);
$this->set('transactionExecutingCount', $session->read('Transaction.executing')); $this->set('transactionExecutingCount', $session->read('Transactions.executing'));
$this->set('balance', $session->read('StateUser.balance')); $this->set('balance', $session->read('StateUser.balance'));
$this->set('timeUsed', microtime(true) - $startTime); $this->set('timeUsed', microtime(true) - $startTime);
$this->set('gdtSum', $gdtSum); $this->set('gdtSum', $gdtSum);
@ -370,7 +370,7 @@ class StateBalancesController extends AppController
return $this->returnJson([ return $this->returnJson([
'state' => 'success', 'state' => 'success',
'transactions' => $transactions, 'transactions' => $transactions,
'transactionExecutingCount' => $session->read('Transaction.executing'), 'transactionExecutingCount' => $session->read('Transactions.executing'),
'count' => count($transactions), 'count' => count($transactions),
'gdtSum' => $gdtSum, 'gdtSum' => $gdtSum,
'timeUsed' => microtime(true) - $startTime 'timeUsed' => microtime(true) - $startTime

View File

@ -196,7 +196,7 @@ class StateUserTransactionsController extends AppController
return $this->returnJson([ return $this->returnJson([
'state' => 'success', 'state' => 'success',
'transactions' => $transactions, 'transactions' => $transactions,
'transactionExecutingCount' => $session->read('Transaction.executing'), 'transactionExecutingCount' => $session->read('Transactions.executing'),
'count' => $all_user_transactions_count, 'count' => $all_user_transactions_count,
'timeUsed' => microtime(true) - $startTime 'timeUsed' => microtime(true) - $startTime
]); ]);

View File

@ -323,7 +323,7 @@ class TransactionCreationsController extends AppController
$this->set('firstDayLastMonth', $firstDayLastMonth); $this->set('firstDayLastMonth', $firstDayLastMonth);
$this->set('activeUser', $user); $this->set('activeUser', $user);
$this->set('creationForm', $creationForm); $this->set('creationForm', $creationForm);
$this->set('transactionExecutingCount', $session->read('Transaction.executing')); $this->set('transactionExecutingCount', $session->read('Transactions.executing'));
$this->set('timeUsed', microtime(true) - $startTime); $this->set('timeUsed', microtime(true) - $startTime);
$this->set('countUsers', $countUsers); $this->set('countUsers', $countUsers);
$this->set('limit', $limit); $this->set('limit', $limit);

View File

@ -15,8 +15,10 @@ mit:
```ini ```ini
unsercure.allow_cors_all = 1 unsercure.allow_cors_all = 1
``` ```
Wird bei allen JSON-Requests zum Header: Access-Control-Allow-Origin:* Wird bei allen JSON-Requests zum Header hinzugefügt:
hinzugefügt. - Access-Control-Allow-Origin:*
- Access-Control-Allow-Headers: "Access-Control-Allow-Headers, Origin,Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers"
In diesen Beispielen gehe ich jetzt davon aus, das du das gesamte Gradido Projekt mit Docker gebaut hast und auf dem lokalen Rechner laufen lässt. In diesen Beispielen gehe ich jetzt davon aus, das du das gesamte Gradido Projekt mit Docker gebaut hast und auf dem lokalen Rechner laufen lässt.
@ -81,4 +83,130 @@ data: {"session_id": -127182}
Wenn alles okay ist erhältst du: Wenn alles okay ist erhältst du:
```json ```json
{"state":"success"} {"state":"success"}
``` ```
## Update User Data
Update first name, last name, user language and enable/disable user
Language currently supported de and en
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 we wenn delete this entry, validating all transactions not longer possible.
Disabled User cannot login and cannot receive transactions.
In update Object only one of the sets needs to be there.
Update password can only be used if in Login-Server config:
```ini
unsecure.allow_passwort_via_json_request = 1
```
POST http://localhost/login_api/updateUserInfos
```json
{"session_id": -127182, "email": "max.musterman@gmail.de", "update": {
"User.first_name": "Max",
"User.last_name" : "Musterman",
"User.disabled": 0,
"User.language": "de"
"User.password": "1234"
}
}
```
also valid
```json
{"session_id": -127182, "email": "max.musterman@gmail.de", "update": {
"User.last_name" : "Musterman"
}
}
```
It returns if everything is okay
```json
{"state":"success", "valid_values": 4, "errors":[]}
```
- valid_values: should contain count of entrys in update if no error occured (User.password will not be counted)
- errors: contain on error string for every entry in update, which type isn't like expected
- password:
- "new password is the same as old password": no change taking place
- "password changed, coludn"t load private key for re-encryption": password was successfully changed, is at the moment only a warning as long as user_backups are unencrypted, safe to ignore
- "stored pubkey and private key didn't match": error by re-encryption keys, no changes saved
- "User.password isn't valid": if password validation failed, followed by reasons why (additional array in array)
example:
```json
{"errors":[
"User.password isn't valid",[
"Passwort: Dein Passwort ist zu kurz!\n",
"Passwort: Bitte gebe ein gültiges Password ein mit mindestens 8 Zeichen, Groß- und Kleinbuchstaben, mindestens einer Zahl und einem Sonderzeichen (@$!%*?&+-_) ein!\n"
]
],
"state":"success",
"valid_values":0
}
```
## Retrieve User Data
Retrieve different user data, in ask only one field is needed, or every possible combination
from the available fields
Normal User can only retrieve data for himself, admins (login-server admin) can retrieve data from every user
Email is also the email address of user from which data are asked
POST http://localhost/login_api/getUserInfos
```json
{"session_id": -127182, "email": "max.musterman@gmail.de", "ask": [
"EmailVerificationCode.Register",
"loginServer.path",
"user.pubkeyhex",
"user.first_name",
"user.last_name",
"user.disabled",
"user.email_checked",
]
}
```
returns if no error occured:
```json
{"state": "success", "userData": {
"EmailVerificationCode.Register": "2718271129122",
"pubkeyhex": "131c7f68dd94b2be4c913400ff7ff4cdc03ac2bda99c2d29edcacb3b065c67e6",
"first_name": "Max",
"last_name": "Musterman",
"disabled": 0,
"email_checked": 1
}, "server": {
"loginServer.path": "http://localhost/account"
},
"errors": []
}
```
Return only the fields which are defined in ask
- EmailVerificationCode.Register: return the email verification code for check email (create one if none exist), work only if logged in user is admin and the email isn't from him
- loginServer.path: the redirect path to login-server, for example for login with login-server html frontend
- user.pubkeyhex: public key of user in hex-format
- user.first_name: first name of user
- user.last_name: last name of 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 we wenn delete this entry, validating all transactions not 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
- errors: array of strings if error occure
## Login by Email Verification Code
Used for replace http://localhost/account/checkEmail
Can be used to set check_email to 1 (will be done automaticly if called with valid email verification code of type register or registerDirect)
Can be used for password reset (additional step required: call update user info with new password)
GET http://localhost/login_api/loginViaEmailVerificationCode?emailVerificationCode=382738273892983
return
```json
{"state":"success", "email_verification_code_type":"resetPassword","info":[],"session_id":1853761475}
```
- email_verification_code_type
- resetPassword: for password resets, will be deleted immediately, is a only one use code
- registerDirect: code generated by register for check email
- register: code generated by auto-register via elopage for check email
- info can contain additional info strings
- user hasn't password: if user hasn't set a password yet (for example if he was registered via elopage)
- email already activated: if email was already checked
- session_id: session_id for new session

View File

@ -71,15 +71,13 @@ Poco::JSON::Object* JsonGetLogin::handle(Poco::Dynamic::Var params)
em->addError(new Error("JsonGetLogin::handle", "generic exception calling userModel->getJson: ")); em->addError(new Error("JsonGetLogin::handle", "generic exception calling userModel->getJson: "));
em->sendErrorsAsEmail(); em->sendErrorsAsEmail();
} }
// use both variants for compatibility reasons, but final version is Transactions.pending
result->set("Transaction.pending", session->getProcessingTransactionCount());
result->set("Transactions.pending", session->getProcessingTransactionCount()); result->set("Transactions.pending", session->getProcessingTransactionCount());
auto executing = observer->getTaskCount(userModel->getEmail(), TASK_OBSERVER_SIGN_TRANSACTION); auto executing = observer->getTaskCount(userModel->getEmail(), TASK_OBSERVER_SIGN_TRANSACTION);
if (executing < 0) { if (executing < 0) {
executing = 0; executing = 0;
} }
// use both variants for compatibility reasons, but final version is Transactions.executing
result->set("Transaction.executing", executing);
result->set("Transactions.executing", executing); result->set("Transactions.executing", executing);
//printf("pending: %d\n", session->getProcessingTransactionCount()); //printf("pending: %d\n", session->getProcessingTransactionCount());
//std::string user_string = userModel->toString(); //std::string user_string = userModel->toString();

View File

@ -7,6 +7,26 @@
#include "../ServerConfig.h" #include "../ServerConfig.h"
Poco::UInt64 JsonGetUserInfos::readOrCreateEmailVerificationCode(int user_id, model::table::EmailOptInType type)
{
try {
auto emailVerificationCode = controller::EmailVerificationCode::load(user_id, type);
if (!emailVerificationCode) {
emailVerificationCode = controller::EmailVerificationCode::create(user_id, type);
UniLib::controller::TaskPtr insert = new model::table::ModelInsertTask(emailVerificationCode->getModel(), false);
insert->scheduleTask(insert);
}
return emailVerificationCode->getModel()->getCode();
}
catch (Poco::Exception& ex) {
ErrorList errors;
//printf("exception: %s\n", ex.displayText().data());
errors.addError(new ParamError("JsonGetUserInfos::readOrCreateEmailVerificationCode", "exception: ", ex.displayText()));
errors.sendErrorsAsEmail();
}
return 0;
}
Poco::JSON::Object* JsonGetUserInfos::handle(Poco::Dynamic::Var params) Poco::JSON::Object* JsonGetUserInfos::handle(Poco::Dynamic::Var params)
{ {
/* /*
@ -54,11 +74,21 @@ Poco::JSON::Object* JsonGetUserInfos::handle(Poco::Dynamic::Var params)
return customStateError("not found", "session not found"); return customStateError("not found", "session not found");
} }
auto session_user = session->getNewUser();
auto session_user_model = session_user->getModel();
bool isAdmin = false;
if (model::table::ROLE_ADMIN == session_user_model->getRole()) {
isAdmin = true;
}
if (session_user_model->getEmail() != email && !isAdmin) {
return customStateError("not same", "email don't belong to logged in user");
}
auto user = controller::User::create(); auto user = controller::User::create();
if (1 != user->load(email)) { if (1 != user->load(email)) {
return customStateError("not found", "user not found"); return customStateError("not found", "user not found");
} }
auto userModel = user->getModel(); auto user_model = user->getModel();
Poco::JSON::Object* result = new Poco::JSON::Object; Poco::JSON::Object* result = new Poco::JSON::Object;
@ -72,42 +102,32 @@ Poco::JSON::Object* JsonGetUserInfos::handle(Poco::Dynamic::Var params)
std::string parameterString; std::string parameterString;
try { try {
parameter.convert(parameterString); parameter.convert(parameterString);
if (parameterString == "EmailVerificationCode.Register") { if (parameterString == "EmailVerificationCode.Register" && isAdmin && session_user_model->getEmail() != user_model->getEmail()) {
try { auto code = readOrCreateEmailVerificationCode(user_model->getID(), model::table::EMAIL_OPT_IN_REGISTER_DIRECT);
auto emailVerificationCode = controller::EmailVerificationCode::load( if (code) {
userModel->getID(), model::table::EMAIL_OPT_IN_REGISTER jsonUser.set("EmailVerificationCode.Register", std::to_string(code));
);
if (!emailVerificationCode) {
emailVerificationCode = controller::EmailVerificationCode::create(userModel->getID(), model::table::EMAIL_OPT_IN_REGISTER);
UniLib::controller::TaskPtr insert = new model::table::ModelInsertTask(emailVerificationCode->getModel(), false);
insert->scheduleTask(insert);
}
jsonUser.set("EmailVerificationCode.Register", std::to_string(emailVerificationCode->getModel()->getCode()));
}
catch (Poco::Exception& ex) {
printf("exception: %s\n", ex.displayText().data());
} }
} }
else if (parameterString == "loginServer.path") { else if (parameterString == "loginServer.path") {
jsonServer.set("loginServer.path", ServerConfig::g_serverPath); jsonServer.set("loginServer.path", ServerConfig::g_serverPath);
} }
else if (parameterString == "user.pubkeyhex") { else if (parameterString == "user.pubkeyhex") {
jsonUser.set("pubkeyhex", userModel->getPublicKeyHex()); jsonUser.set("pubkeyhex", user_model->getPublicKeyHex());
} }
else if (parameterString == "user.first_name") { else if (parameterString == "user.first_name") {
jsonUser.set("first_name", userModel->getFirstName()); jsonUser.set("first_name", user_model->getFirstName());
} }
else if (parameterString == "user.last_name") { else if (parameterString == "user.last_name") {
jsonUser.set("last_name", userModel->getLastName()); jsonUser.set("last_name", user_model->getLastName());
} }
else if (parameterString == "user.disabled") { else if (parameterString == "user.disabled") {
jsonUser.set("disabled", userModel->isDisabled()); jsonUser.set("disabled", user_model->isDisabled());
} }
else if (parameterString == "user.email_checked") { else if (parameterString == "user.email_checked") {
jsonUser.set("email_checked", userModel->isEmailChecked()); jsonUser.set("email_checked", user_model->isEmailChecked());
} }
else if (parameterString == "user.identHash") { else if (parameterString == "user.identHash") {
auto email = userModel->getEmail(); auto email = user_model->getEmail();
jsonUser.set("identHash", DRMakeStringHash(email.data(), email.size())); jsonUser.set("identHash", DRMakeStringHash(email.data(), email.size()));
} }
} }

View File

@ -2,7 +2,7 @@
#define __JSON_INTERFACE_JSON_GET_USER_INFOS_ #define __JSON_INTERFACE_JSON_GET_USER_INFOS_
#include "JsonRequestHandler.h" #include "JsonRequestHandler.h"
#include "../model/table/EmailOptIn.h"
/*! /*!
* @author Dario Rekowski * @author Dario Rekowski
* @date 2020-03-21 * @date 2020-03-21
@ -17,7 +17,7 @@ public:
Poco::JSON::Object* handle(Poco::Dynamic::Var params); Poco::JSON::Object* handle(Poco::Dynamic::Var params);
protected: protected:
Poco::UInt64 readOrCreateEmailVerificationCode(int user_id, model::table::EmailOptInType type);
}; };

View File

@ -0,0 +1,70 @@
#include "JsonLoginViaEmailVerificationCode.h"
#include "JsonUnsecureLogin.h"
#include "../SingletonManager/SessionManager.h"
#include "../SingletonManager/SingletonTaskObserver.h"
#include "../SingletonManager/ErrorManager.h"
#include "../controller/User.h"
#include "../lib/DataTypeConverter.h"
#include "Poco/URI.h"
#include "Poco/JSON/Array.h"
Poco::JSON::Object* JsonLoginViaEmailVerificationCode::handle(Poco::Dynamic::Var params)
{
auto sm = SessionManager::getInstance();
/*
email verification code
*/
// incoming
unsigned long long code = 0;
if (params.isVector()) {
const Poco::URI::QueryParameters queryParams = params.extract<Poco::URI::QueryParameters>();
std::string codeString;
for (auto it = queryParams.begin(); it != queryParams.end(); it++) {
if (it->first == "emailVerificationCode") {
codeString = it->second;
break;
}
}
if (codeString == "") {
return stateError("emailVerificationCode not found");
}
if (DataTypeConverter::NUMBER_PARSE_OKAY != DataTypeConverter::strToInt(codeString, code)) {
return stateError("couldn't parse emailVerificationCode");
}
}
auto session = sm->findByEmailVerificationCode(code);
if (!session) {
session = sm->getNewSession();
if (!session->loadFromEmailVerificationCode(code)) {
return stateError("couldn't login with emailVerificationCode");
}
}
session->setClientIp(mClientIP);
auto result = new Poco::JSON::Object;
result->set("state", "success");
result->set("session_id", session->getHandle());
result->set("email_verification_code_type", model::table::EmailOptIn::typeToString(session->getEmailVerificationType()));
Poco::JSON::Array info;
if (!session->getNewUser()->getModel()->getPasswordHashed()) {
info.add("user hasn't password");
}
auto update_email_verification_result = session->updateEmailVerification(code);
if (1 == update_email_verification_result) {
info.add("email already activated");
}
result->set("info", info);
return result;
}

View File

@ -0,0 +1,18 @@
#ifndef __JSON_INTERFACE_JSON_LOGIN_VIA_EMAIL_VERIFICATION_CODE_
#define __JSON_INTERFACE_JSON_LOGIN_VIA_EMAIL_VERIFICATION_CODE_
#include "JsonRequestHandler.h"
class JsonLoginViaEmailVerificationCode : public JsonRequestHandler
{
public:
JsonLoginViaEmailVerificationCode(Poco::Net::IPAddress ip) : mClientIP(ip) {}
Poco::JSON::Object* handle(Poco::Dynamic::Var params);
protected:
Poco::Net::IPAddress mClientIP;
};
#endif // __JSON_INTERFACE_JSON_LOGIN_VIA_EMAIL_VERIFICATION_CODE_

View File

@ -10,6 +10,7 @@
#include "JsonTransaction.h" #include "JsonTransaction.h"
#include "JsonGetRunningUserTasks.h" #include "JsonGetRunningUserTasks.h"
#include "JsonGetUsers.h" #include "JsonGetUsers.h"
#include "JsonLoginViaEmailVerificationCode.h"
#include "JsonAdminEmailVerificationResend.h" #include "JsonAdminEmailVerificationResend.h"
#include "JsonGetUserInfos.h" #include "JsonGetUserInfos.h"
#include "JsonUpdateUserInfos.h" #include "JsonUpdateUserInfos.h"
@ -67,6 +68,9 @@ Poco::Net::HTTPRequestHandler* JsonRequestHandlerFactory::createRequestHandler(c
else if (url_first_part == "/unsecureLogin" && (ServerConfig::g_AllowUnsecureFlags & ServerConfig::UNSECURE_PASSWORD_REQUESTS)) { else if (url_first_part == "/unsecureLogin" && (ServerConfig::g_AllowUnsecureFlags & ServerConfig::UNSECURE_PASSWORD_REQUESTS)) {
return new JsonUnsecureLogin(client_host); return new JsonUnsecureLogin(client_host);
} }
else if (url_first_part == "/loginViaEmailVerificationCode") {
return new JsonLoginViaEmailVerificationCode(client_host);
}
else if (url_first_part == "/logout") { else if (url_first_part == "/logout") {
return new JsonLogout(client_host); return new JsonLogout(client_host);
} }

View File

@ -2,6 +2,7 @@
#include "../SingletonManager/SessionManager.h" #include "../SingletonManager/SessionManager.h"
#include "../SingletonManager/LanguageManager.h" #include "../SingletonManager/LanguageManager.h"
#include "../tasks/AuthenticatedEncryptionCreateKeyTask.h"
Poco::JSON::Object* JsonUpdateUserInfos::handle(Poco::Dynamic::Var params) Poco::JSON::Object* JsonUpdateUserInfos::handle(Poco::Dynamic::Var params)
{ {
@ -117,6 +118,34 @@ Poco::JSON::Object* JsonUpdateUserInfos::handle(Poco::Dynamic::Var params)
} }
} }
} }
else if ("User.password" == name && (ServerConfig::g_AllowUnsecureFlags & ServerConfig::UNSECURE_PASSWORD_REQUESTS) == ServerConfig::UNSECURE_PASSWORD_REQUESTS) {
if (!value.isString()) {
jsonErrorsArray.add("User.password isn't string");
}
else {
ErrorList errors;
if (!sm->checkPwdValidation(value.toString(), &errors)) {
jsonErrorsArray.add("User.password isn't valid");
jsonErrorsArray.add(errors.getErrorsArray());
}
else {
auto result_new_password = user->setNewPassword(value.toString());
switch (result_new_password) {
// 0 = new and current passwords are the same
case 0: jsonErrorsArray.add("new password is the same as old password"); break;
// 1 = password changed, private key re-encrypted and saved into db
//case 1: extractet_values++; break;
// 2 = password changed, only hash stored in db, couldn't load private key for re-encryption
case 2: jsonErrorsArray.add("password changed, couldn't load private key for re-encryption"); break;
// -1 = stored pubkey and private key didn't match
case -1: jsonErrorsArray.add("stored pubkey and private key didn't match"); break;
}
}
}
}
} }
catch (Poco::Exception& ex) { catch (Poco::Exception& ex) {
jsonErrorsArray.add("update parameter invalid"); jsonErrorsArray.add("update parameter invalid");