LCOV - code coverage report
Current view: top level - extra/webserver/webserver/tools - SPWebToolsAuth.cc (source / functions) Hit Total Coverage
Test: coverage.info Lines: 88 99 88.9 %
Date: 2024-05-12 00:16:13 Functions: 4 4 100.0 %

          Line data    Source code
       1             : /**
       2             :  Copyright (c) 2024 Stappler LLC <admin@stappler.dev>
       3             : 
       4             :  Permission is hereby granted, free of charge, to any person obtaining a copy
       5             :  of this software and associated documentation files (the "Software"), to deal
       6             :  in the Software without restriction, including without limitation the rights
       7             :  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       8             :  copies of the Software, and to permit persons to whom the Software is
       9             :  furnished to do so, subject to the following conditions:
      10             : 
      11             :  The above copyright notice and this permission notice shall be included in
      12             :  all copies or substantial portions of the Software.
      13             : 
      14             :  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      15             :  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      16             :  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      17             :  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      18             :  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      19             :  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
      20             :  THE SOFTWARE.
      21             :  **/
      22             : 
      23             : #include "SPWebTools.h"
      24             : #include "SPWebRoot.h"
      25             : #include "SPWebSession.h"
      26             : #include "SPDbUser.h"
      27             : 
      28             : namespace STAPPLER_VERSIONIZED stappler::web::tools {
      29             : 
      30          50 : static bool reportAuthHandlerError(Request &rctx, StringView text, Status status, Value && = Value()) {
      31          50 :         rctx.addError("Auth", text);
      32          50 :         if (status != DECLINED) {
      33          25 :                 rctx.setStatus(status);
      34             :         }
      35          50 :         return false;
      36             : }
      37             : 
      38         225 : bool AuthHandler::isRequestPermitted(Request &rctx) {
      39         225 :         if (_subPath != "/login" && _subPath != "/update" && _subPath != "/cancel" && _subPath != "/setup" && _subPath != "/basic" && _subPath != "/touch") {
      40           0 :                 return reportAuthHandlerError(rctx, "Invalid handler action", HTTP_NOT_FOUND);
      41             :         }
      42         225 :         if (_subPath != "/login" && _subPath != "/setup" && _subPath != "/basic" && rctx.getUser() == nullptr) {
      43          25 :                 return reportAuthHandlerError(rctx, "You are not logged in", HTTP_FORBIDDEN);
      44             :         }
      45         200 :         _allow = AllowMethod::Get;
      46         200 :         _transaction = rctx.acquireDbTransaction();
      47         200 :         return rctx.getInfo().method == RequestMethod::Get;
      48             : }
      49             : 
      50         200 : Status AuthHandler::onTranslateName(Request &rctx) {
      51         200 :         if (_subPath == "/basic") {
      52          50 :                 if (rctx.getAuthorizedUser()) {
      53          50 :                         auto r = rctx.getInfo().queryData.getString("redirect");
      54          50 :                         return rctx.redirectTo(String(r));
      55          50 :                 }
      56           0 :                 return HTTP_UNAUTHORIZED;
      57             :         }
      58         150 :         return DataHandler::onTranslateName(rctx);
      59             : }
      60             : 
      61         150 : bool AuthHandler::processDataHandler(Request &rctx, Value &result, Value &input) {
      62         150 :         auto &queryData = rctx.getInfo().queryData;
      63             : 
      64         150 :         if (!_transaction) {
      65           0 :                 return reportAuthHandlerError(rctx, "Database connection failed", HTTP_INTERNAL_SERVER_ERROR);
      66             :         }
      67             : 
      68         150 :         if (_subPath == "/login") {
      69          50 :                 auto &name = queryData.getString("name");
      70          50 :                 auto &passwd = queryData.getString("passwd");
      71          50 :                 if (name.empty() || passwd.empty()) {
      72           0 :                         return reportAuthHandlerError(rctx, "Name or password is not specified", DECLINED, Value{ pair("Doc", Value("You should specify 'name' and 'passwd' variables in request")) });
      73             :                 }
      74             : 
      75          50 :                 TimeInterval maxAge = TimeInterval::seconds(queryData.getInteger("maxAge"));
      76          50 :                 if (!maxAge || maxAge > config::AUTH_MAX_TIME) {
      77          50 :                         maxAge = config::AUTH_MAX_TIME;
      78             :                 }
      79             : 
      80          50 :                 auto user = db::User::get(_transaction, name, passwd);
      81          50 :                 if (!user) {
      82          25 :                         return reportAuthHandlerError(rctx, "Invalid username or password", DECLINED);
      83             :                 }
      84             : 
      85          25 :                 auto &opts = getOptions();
      86          25 :                 bool isAuthorized = false;
      87          25 :                 if (!opts.getBool("AdminOnly") || user->isAdmin()) {
      88          25 :                         isAuthorized = true;
      89             :                 }
      90             : 
      91          25 :                 if (isAuthorized) {
      92          25 :                         Session *session = _request.authorizeUser(user, maxAge);
      93          25 :                         if (session) {
      94          25 :                                 auto &token = session->getSessionToken();
      95          25 :                                 result.setString(base64url::encode<Interface>(CoderSource(token.data(), token.size())), "token");
      96          25 :                                 result.setInteger(maxAge.toSeconds(), "maxAge");
      97          25 :                                 result.setInteger(user->getObjectId(), "userId");
      98          25 :                                 result.setString(user->getName(), "userName");
      99          25 :                                 if (queryData.getBool("userdata")) {
     100          25 :                                         auto &val = result.emplace("userData");
     101         125 :                                         for (auto &it : *user) {
     102         100 :                                                 val.setValue(it.second, it.first);
     103             :                                         }
     104          25 :                                         val.erase("password");
     105             :                                 }
     106          25 :                                 return true;
     107             :                         }
     108             :                 }
     109             : 
     110           0 :                 return reportAuthHandlerError(rctx, "Fail to create session", DECLINED);
     111         100 :         } else if (_subPath == "/touch") {
     112          25 :                 if (auto u = rctx.getAuthorizedUser()) {
     113          25 :                         result.setInteger(u->getObjectId(), "userId");
     114          25 :                         result.setString(u->getName(), "userName");
     115          25 :                         return true;
     116             :                 }
     117           0 :                 return false;
     118          75 :         } else if (_subPath == "/update") {
     119          25 :                 if (auto session = rctx.getSession()) {
     120          25 :                         db::User *user = session->getUser();
     121          25 :                         if (!user) {
     122          25 :                                 return false;
     123             :                         }
     124          25 :                         TimeInterval maxAge = TimeInterval::seconds(rctx.getInfo().queryData.getInteger("maxAge"));
     125          25 :                         if (!maxAge || maxAge > config::AUTH_MAX_TIME) {
     126          25 :                                 maxAge = config::AUTH_MAX_TIME;
     127             :                         }
     128             : 
     129          25 :                         if (session->touch(maxAge)) {
     130          25 :                                 auto &token = session->getSessionToken();
     131          25 :                                 result.setString(base64url::encode<Interface>(CoderSource(token.data(), token.size())), "token");
     132          25 :                                 result.setInteger(maxAge.toSeconds(), "maxAge");
     133          25 :                                 result.setInteger(user->getObjectId(), "userId");
     134          25 :                                 result.setString(user->getName(), "userName");
     135          25 :                                 if (queryData.getBool("userdata")) {
     136          25 :                                         auto &val = result.emplace("userData");
     137         125 :                                         for (auto &it : *user) {
     138         100 :                                                 val.setValue(it.second, it.first);
     139             :                                         }
     140          25 :                                         val.erase("password");
     141             :                                 }
     142          25 :                                 return true;
     143             :                         }
     144             :                 }
     145             : 
     146           0 :                 return false;
     147          50 :         } else if (_subPath == "/cancel") {
     148          25 :                 if (auto session = rctx.getSession()) {
     149          25 :                         return session->cancel();
     150             :                 }
     151           0 :                 return false;
     152          25 :         } else if (_subPath == "/setup") {
     153          25 :                 auto &name = queryData.getString("name");
     154          25 :                 auto &passwd = queryData.getString("passwd");
     155             : 
     156          25 :                 if (name.empty() || passwd.empty()) {
     157           0 :                         return reportAuthHandlerError(rctx, "Fail to create session", DECLINED, Value{ pair("Doc", Value("You should specify 'name' and 'passwd' variables in request")) });
     158             :                 }
     159             : 
     160          25 :                 auto user = db::User::setup(_transaction, name, passwd);
     161          25 :                 if (user) {
     162          25 :                         Value &u = result.emplace("user");
     163         125 :                         for (auto &it : *user) {
     164         100 :                                 u.setValue(it.second, it.first);
     165             :                         }
     166          25 :                         return true;
     167             :                 } else {
     168           0 :                         return reportAuthHandlerError(rctx, "Setup failed", DECLINED);
     169             :                 }
     170             :         }
     171           0 :         return false;
     172             : }
     173             : 
     174             : }

Generated by: LCOV version 1.14