diff --git a/Interfaces/API/intfPureModule.h b/Interfaces/API/intfPureModule.h index 3dded32d..8de5a5bf 100644 --- a/Interfaces/API/intfPureModule.h +++ b/Interfaces/API/intfPureModule.h @@ -185,7 +185,7 @@ using namespace TAPI; \**********************************************************************/ //used by Api call methods //USER -#define USER_DELETE_METHOD_ARGS_DECL_APICALL APICALLBOOM_TYPE_JWT_USER_DECL &APICALLBOOM_PARAM, TAPI::PKsByPath_t _pksByPath = {} +#define USER_DELETE_METHOD_ARGS_DECL_APICALL APICALLBOOM_TYPE_JWT_USER_DECL &APICALLBOOM_PARAM, TAPI::PKsByPath_t _pksByPath //= {} #define USER_DELETE_METHOD_ARGS_IMPL_APICALL APICALLBOOM_TYPE_JWT_USER_IMPL &APICALLBOOM_PARAM, TAPI::PKsByPath_t _pksByPath #define ORMDELETE_USER(_doc, ...) apiDELETE(USER_DELETE_METHOD_ARGS_DECL_APICALL) __VA_ARGS__; \ @@ -195,7 +195,7 @@ using namespace TAPI; #define IMPL_ORMDELETE_USER(_module) _module::apiDELETE(USER_DELETE_METHOD_ARGS_IMPL_APICALL) //API -#define API_DELETE_METHOD_ARGS_DECL_APICALL APICALLBOOM_TYPE_JWT_API_DECL &APICALLBOOM_PARAM, TAPI::PKsByPath_t _pksByPath = {} +#define API_DELETE_METHOD_ARGS_DECL_APICALL APICALLBOOM_TYPE_JWT_API_DECL &APICALLBOOM_PARAM, TAPI::PKsByPath_t _pksByPath //= {} #define API_DELETE_METHOD_ARGS_IMPL_APICALL APICALLBOOM_TYPE_JWT_API_IMPL &APICALLBOOM_PARAM, TAPI::PKsByPath_t _pksByPath #define ORMDELETE_API(_doc, ...) apiDELETE(API_DELETE_METHOD_ARGS_DECL_APICALL) __VA_ARGS__; \ @@ -205,7 +205,7 @@ using namespace TAPI; #define IMPL_ORMDELETE_API(_module) _module::apiDELETE(API_DELETE_METHOD_ARGS_IMPL_APICALL) //used by internal methods -#define DELETE_METHOD_ARGS_DECL_INTERNAL INTFAPICALLBOOM_DECL &APICALLBOOM_PARAM, TAPI::PKsByPath_t _pksByPath = {} +#define DELETE_METHOD_ARGS_DECL_INTERNAL INTFAPICALLBOOM_DECL &APICALLBOOM_PARAM, TAPI::PKsByPath_t _pksByPath //= {} #define DELETE_METHOD_ARGS_IMPL_INTERNAL INTFAPICALLBOOM_IMPL &APICALLBOOM_PARAM, TAPI::PKsByPath_t _pksByPath #define DELETE_METHOD_ARGS_CALL_VALUES APICALLBOOM_PARAM, _pksByPath diff --git a/Interfaces/DBM/QueryBuilders.cpp b/Interfaces/DBM/QueryBuilders.cpp index b946c7a5..b45e0202 100644 --- a/Interfaces/DBM/QueryBuilders.cpp +++ b/Interfaces/DBM/QueryBuilders.cpp @@ -2538,12 +2538,20 @@ ORMSelectQuery& ORMSelectQuery::addColsFromInlineJoinCols(Q_DECL_UNUSED const QL ORMSelectQuery& ORMSelectQuery::addCol(const clsColSpecs& _colSpecs) { //check duplicates + QString ColSpecToCompare; + if (_colSpecs.renameAs().isEmpty()) + ColSpecToCompare = _colSpecs.name(); + else + ColSpecToCompare = _colSpecs.renameAs(); + foreach (auto Col, this->Data->RequiredCols) { - if ((Col.name().isEmpty() == false) - && (_colSpecs.name().isEmpty() == false) - && (Col.name() == _colSpecs.name()) - && (Col.renameAs() == _colSpecs.renameAs()) - ) + QString ColToCompare; + if (Col.renameAs().isEmpty()) + ColToCompare = Col.name(); + else + ColToCompare = Col.renameAs(); + + if (ColSpecToCompare == ColToCompare) return *this; } diff --git a/Modules/Account/functionalTest/testAccount.hpp b/Modules/Account/functionalTest/testAccount.hpp index 3469854c..a0d67613 100644 --- a/Modules/Account/functionalTest/testAccount.hpp +++ b/Modules/Account/functionalTest/testAccount.hpp @@ -1732,6 +1732,27 @@ private slots: } } + void apitoken_edit_name_and_expiry() { + QT_TRY { + QVariant Result = this->callUserAPI( + RESTClientHelper::enuHTTPMethod::PATCH, + "Account/APITokens", + {}, + { + { "token", this->TokenJWT }, + { "name", "new name aa" }, + { "expireDate", "2345/06/07 12:34:56" }, + }); + + QVERIFY(Result.isValid()); + + this->TokenJWT = Result.toMap().value("token").toString(); + + } QT_CATCH (const std::exception &exp) { + QTest::qFail(exp.what(), __FILE__, __LINE__); + } + } + void apitoken_new_resume() { QT_TRY { QVariant Result = this->callUserAPI( diff --git a/Modules/Account/moduleSrc/ORM/APITokens.cpp b/Modules/Account/moduleSrc/ORM/APITokens.cpp index 55392b72..d95f77c8 100644 --- a/Modules/Account/moduleSrc/ORM/APITokens.cpp +++ b/Modules/Account/moduleSrc/ORM/APITokens.cpp @@ -217,6 +217,92 @@ QVariant IMPL_ORMGET_USER(APITokens) { return this->Select(GET_METHOD_ARGS_CALL_VALUES); } +QVariantMap IMPL_REST_UPDATE(APITokens, , ( + APICALLBOOM_TYPE_JWT_USER_DECL &APICALLBOOM_PARAM, + QString _token, + QString _name, + NULLABLE_TYPE(TAPI::DateTime_t) _expireDate +)) { + QVariantMap MethodResult; + + quint64 CurrentUserID = APICALLBOOM_PARAM.getActorID(); + + TAPI::JWT_t TokenJWTPayload; + QJWT::extractAndDecryptPayload(_token, TokenJWTPayload); + + if (CurrentUserID != TokenJWTPayload["own"].toDouble()) + throw exAuthorization("This API Token is not yours"); + + //---------------------------------------- + enuTokenActorType::Type TokenType = enuTokenActorType::USER; + if (TokenJWTPayload.contains("typ")) + TokenType = enuTokenActorType::toEnum(TokenJWTPayload["typ"].toString()); + if (TokenType != enuTokenActorType::API) + throw exHTTPForbidden("Only API Token allowed"); + + //---------------------------------------- + auto UpdateQuery = this->makeUpdateQuery(APICALLBOOM_PARAM) + .where({ tblAPITokens::Fields::aptToken, enuConditionOperator::Equal, _token }); + + int ToUpdateCount = 0; + + if (_name.isNull() == false) { + ++ToUpdateCount; + UpdateQuery.set(tblAPITokens::Fields::aptName, _name); + } + + QString NewToken; + + if (NULLABLE_HAS_VALUE(_expireDate)) { + TokenJWTPayload["exp"] = (NULLABLE_VALUE(_expireDate)).toSecsSinceEpoch(); + NewToken = QJWT::encryptAndSigned(TokenJWTPayload); + MethodResult.insert("token", NewToken); + + ++ToUpdateCount; + UpdateQuery + .set(tblAPITokens::Fields::aptToken, NewToken) + .set(tblAPITokens::Fields::aptExpiryDate, NULLABLE_VALUE(_expireDate)) + ; + } + + if (ToUpdateCount == 0) + throw exHTTPInternalServerError("Nothing to do"); + + try { + if (UpdateQuery.execute(CurrentUserID) == 0) + throw exHTTPInternalServerError("Error in updating"); + } catch (const std::exception &_exp) { + QString ExpStr = _exp.what(); + if (ExpStr.contains("Duplicate entry", Qt::CaseInsensitive)) + throw /*exHTTPConflict*/exHTTPBadRequest("This name has already been used"); + + throw; + } + + if (NewToken.isEmpty() == false) { + QString OldTokenMD5 = QCryptographicHash::hash(_token.toLatin1(), QCryptographicHash::Md5).toHex().constData(); + QString NewTokenMD5 = QCryptographicHash::hash(NewToken.toLatin1(), QCryptographicHash::Md5).toHex().constData(); + + QString qry = QString() + + "UPDATE " + tblTokenBin::Name + + " SET tkbTokenMD5=?" + + " WHERE tkbTokenMD5=?" + ; + + clsDACResult Result = TokenBin::instance().execQuery(APICALLBOOM_PARAM, + qry, + QVariantList({ + NewTokenMD5, + OldTokenMD5 + }) + ); + } + + MethodResult.insert("result", true); + + return MethodResult; +} + QVariant IMPL_REST_GET(APITokens, byService, ( APICALLBOOM_TYPE_JWT_USER_IMPL &APICALLBOOM_PARAM, QStringList _services, @@ -283,9 +369,14 @@ Targoman::API::AccountModule::stuRequestTokenResult IMPL_REST_POST(APITokens, re )) { // QString TokenMD5 = QCryptographicHash::hash(_token.toLatin1(), QCryptographicHash::Md5).toHex().constData(); + quint64 CurrentUserID = APICALLBOOM_PARAM.getActorID(); + TAPI::JWT_t TokenJWTPayload; QJWT::extractAndDecryptPayload(_token, TokenJWTPayload); + if (CurrentUserID != TokenJWTPayload["own"].toDouble()) + throw exAuthorization("This API Token is not yours"); + //---------------------------------------- enuTokenActorType::Type TokenType = enuTokenActorType::USER; if (TokenJWTPayload.contains("typ")) @@ -342,9 +433,14 @@ bool IMPL_REST_POST(APITokens, pause, ( APICALLBOOM_TYPE_JWT_USER_IMPL &APICALLBOOM_PARAM, const QString &_token )) { + quint64 CurrentUserID = APICALLBOOM_PARAM.getActorID(); + TAPI::JWT_t TokenJWTPayload; QJWT::extractAndDecryptPayload(_token, TokenJWTPayload); + if (CurrentUserID != TokenJWTPayload["own"].toDouble()) + throw exAuthorization("This API Token is not yours"); + //---------------------------------------- enuTokenActorType::Type TokenType = enuTokenActorType::USER; if (TokenJWTPayload.contains("typ")) @@ -369,9 +465,14 @@ bool IMPL_REST_POST(APITokens, resume, ( APICALLBOOM_TYPE_JWT_USER_IMPL &APICALLBOOM_PARAM, const QString &_token )) { + quint64 CurrentUserID = APICALLBOOM_PARAM.getActorID(); + TAPI::JWT_t TokenJWTPayload; QJWT::extractAndDecryptPayload(_token, TokenJWTPayload); + if (CurrentUserID != TokenJWTPayload["own"].toDouble()) + throw exAuthorization("This API Token is not yours"); + //---------------------------------------- enuTokenActorType::Type TokenType = enuTokenActorType::USER; if (TokenJWTPayload.contains("typ")) diff --git a/Modules/Account/moduleSrc/ORM/APITokens.h b/Modules/Account/moduleSrc/ORM/APITokens.h index 4ca814ee..9a8082b3 100644 --- a/Modules/Account/moduleSrc/ORM/APITokens.h +++ b/Modules/Account/moduleSrc/ORM/APITokens.h @@ -120,14 +120,14 @@ namespace tblAPITokens { const QList ORMFields = { //ColName Type validation Default UpBy Sort Filter Self Virt PK { Fields::aptID, ORM_PRIMARYKEY_64 }, - { Fields::aptToken, S(QString), QFV, QRequired, UPAdmin, true, true }, + { Fields::aptToken, S(QString), QFV, QRequired, UPOwner, true, true }, //aptTokenMD5 { Fields::aptName, S(QString), QFV.asciiAlNum().maxLenght(250), QRequired, UPOwner, true, true }, { Fields::apt_usrID, S(quint64), QFV.integer().minValue(1), QRequired, UPNone }, { Fields::aptLang, S(TAPI::ISO639_2_t), QFV, "en", UPOwner }, { Fields::aptValidateIP, S(bool), QFV, false, UPOwner }, - { Fields::aptExtraPrivileges, S(TAPI::PrivObject_t), QFV, QNull, UPAdmin, false, false }, - { Fields::aptExpiryDate, S(TAPI::DateTime_t), QFV, QNull, UPAdmin }, + { Fields::aptExtraPrivileges, S(TAPI::PrivObject_t), QFV, QNull, UPOwner, false, false }, + { Fields::aptExpiryDate, S(TAPI::DateTime_t), QFV, QNull, UPOwner }, { Fields::aptLastActivity, S(TAPI::DateTime_t), QFV, QInvalid, UPNone }, { Fields::aptAccessCount, S(quint32), QFV.integer().minValue(1), QInvalid, UPNone }, { Fields::aptPaused, S(bool), QFV, false, UPOwner }, @@ -301,6 +301,17 @@ private slots: // bool ORMUPDATE_USER("Update an APIToken") // bool ORMDELETE_USER("Delete an APIToken") + QVariantMap REST_UPDATE( + , + ( + APICALLBOOM_TYPE_JWT_USER_DECL &APICALLBOOM_PARAM, + QString _token, + QString _name = {}, + NULLABLE_TYPE(TAPI::DateTime_t) _expireDate = NULLABLE_NULL_VALUE + ), + "Update token name and expire date. Returns new token and other info" + ) + QVariant REST_GET( byService, ( diff --git a/Modules/Account/moduleSrc/ORM/User.cpp b/Modules/Account/moduleSrc/ORM/User.cpp index ff56b04b..c4b25403 100644 --- a/Modules/Account/moduleSrc/ORM/User.cpp +++ b/Modules/Account/moduleSrc/ORM/User.cpp @@ -137,8 +137,28 @@ bool IMPL_ORMUPDATE_USER(User) { return this->Update(UPDATE_METHOD_ARGS_CALL_VALUES); } -bool IMPL_ORMDELETE_USER(User) { - if (APICALLBOOM_PARAM.getActorID() != _pksByPath.toULongLong()) +//bool IMPL_ORMDELETE_USER(User) { +// if (APICALLBOOM_PARAM.getActorID() != _pksByPath.toULongLong()) +// Authorization::checkPriv(APICALLBOOM_PARAM, this->privOn(EHTTP_DELETE, this->moduleBaseName())); + +// return this->DeleteByPks(DELETE_METHOD_ARGS_CALL_VALUES); +//} + +bool IMPL_REST_DELETE(User, , ( + USER_DELETE_METHOD_ARGS_IMPL_APICALL, + TAPI::MD5_t _pass, + QString _salt +)) { + if (APICALLBOOM_PARAM.getActorID() == _pksByPath.toULongLong()) { + //check password if is set + this->callSP(APICALLBOOM_PARAM, + "spUser_CheckPassword", { + { "iUserID", APICALLBOOM_PARAM.getActorID() }, + { "iPass", _pass }, + { "iSalt", _salt }, + { "iThrowIfPassNotSet", 0 }, + }); + } else Authorization::checkPriv(APICALLBOOM_PARAM, this->privOn(EHTTP_DELETE, this->moduleBaseName())); return this->DeleteByPks(DELETE_METHOD_ARGS_CALL_VALUES); diff --git a/Modules/Account/moduleSrc/ORM/User.h b/Modules/Account/moduleSrc/ORM/User.h index 022052ef..e768b172 100644 --- a/Modules/Account/moduleSrc/ORM/User.h +++ b/Modules/Account/moduleSrc/ORM/User.h @@ -50,7 +50,17 @@ private slots: QVariant ORMGET_USER("Get user information") quint64 ORMCREATE_USER("Create a new user by an authorized user. Email or Mobile is required") bool ORMUPDATE_USER("Update User info by an authorized user") - bool ORMDELETE_USER("Delete a User by an authorized user") +// bool ORMDELETE_USER("Delete a User by an authorized user") + + bool REST_DELETE( + , + ( + USER_DELETE_METHOD_ARGS_DECL_APICALL, + TAPI::MD5_t _pass = {}, + QString _salt = {} + ), + "Delete an user" + ) TAPI::Base64Image_t REST_GET( photo,