diff --git a/CommandList.cpp b/CommandList.cpp index 35816e3..9ec1e2e 100644 --- a/CommandList.cpp +++ b/CommandList.cpp @@ -56,8 +56,15 @@ namespace core { } int CommandList::processCommand(coreutils::ZString &request, TCPSession &session) { - // for(Command *command : commands) - // session.out << command->getName() << std::endl; + // The intent was always "typing help lists the available + // commands" - this was left as a no-op stub (the commented-out + // body even called a Command::getName() that was never added to + // the base class) so every registered CommandList - including + // the top-level one behind the "help" command itself - silently + // did nothing. The map's own key IS the registered name (see + // add()), so no getName() is needed at all - just walk it directly. + for (auto &pair : commands) + session.out << pair.first << std::string("\r\n"); return true; } diff --git a/ConsoleServer.cpp b/ConsoleServer.cpp index 2a49815..7c6fdb0 100644 --- a/ConsoleServer.cpp +++ b/ConsoleServer.cpp @@ -5,7 +5,9 @@ namespace core { - ConsoleServer::ConsoleServer(EPoll &ePoll, IPAddress address, TLSInfo *tlsInfo) : TCPServer(ePoll, address, tlsInfo, " ", 10, "Console Server") { + ConsoleServer::ConsoleServer(EPoll &ePoll, IPAddress address, TLSInfo *tlsInfo, + std::function credentialChecker) + : TCPServer(ePoll, address, tlsInfo, " ", 10, "Console Server"), credentialChecker(credentialChecker) { coreutils::Log(this); } @@ -18,4 +20,8 @@ namespace core { return new ConsoleSession(ePoll, this); } + bool ConsoleServer::checkCredentials(std::string user, std::string password) { + return credentialChecker ? credentialChecker(user, password) : true; + } + } diff --git a/ConsoleServer.h b/ConsoleServer.h index 55f8e25..1b7a1e2 100644 --- a/ConsoleServer.h +++ b/ConsoleServer.h @@ -6,6 +6,8 @@ # include "EPoll.h" # include "LogListener.h" # include "TLSInfo.h" +# include +# include namespace core { @@ -21,10 +23,16 @@ namespace core { public: // - // + // credentialChecker, when provided, is consulted by + // ConsoleSession's login flow (see its own doc comment) instead + // of the previous unconditional accept-anything behavior. Left + // defaulting to nullptr so any other, unrelated use of + // ConsoleServer that never cared about authentication is + // unaffected. // - ConsoleServer(EPoll &ePoll, IPAddress address, TLSInfo *tlsInfo); + ConsoleServer(EPoll &ePoll, IPAddress address, TLSInfo *tlsInfo, + std::function credentialChecker = nullptr); // // @@ -34,6 +42,11 @@ namespace core { TCPSession * getSocketAccept(EPoll &ePoll) override; + bool checkCredentials(std::string user, std::string password); + + private: + std::function credentialChecker; + }; } diff --git a/ConsoleSession.cpp b/ConsoleSession.cpp index ae4ef71..8604452 100644 --- a/ConsoleSession.cpp +++ b/ConsoleSession.cpp @@ -1,4 +1,5 @@ #include "ConsoleSession.h" +#include "ConsoleServer.h" #include "TCPServer.h" #include "Log.h" @@ -27,6 +28,10 @@ namespace core { break; case WAIT_USER_PROFILE: + // Previously discarded entirely - nothing captured what was + // actually typed here, so a real credential check further + // down could never have anything to compare against. + enteredUser = data.str(); status = PASSWORD; protocol(blank); break; @@ -37,8 +42,21 @@ namespace core { break; case WAIT_PASSWORD: - status = PROMPT; - protocol(blank); + // The actual check: previously this state unconditionally + // advanced to PROMPT regardless of what (if anything) was + // entered at either step - any username/password, including + // blank, reached the command prompt. checkCredentials() + // returns true unconditionally when the owning ConsoleServer + // was built without a credentialChecker, preserving that + // exact behavior for any other use of this class. + if (((ConsoleServer *)server)->checkCredentials(enteredUser, data.str())) { + status = PROMPT; + protocol(blank); + } else { + out << "Login failed." << "\r\n"; + status = DONE; + protocol(blank); + } break; case PROMPT: @@ -68,6 +86,11 @@ namespace core { } } + void ConsoleSession::onConnected() { + coreutils::ZString blank(""); + protocol(blank); + } + void ConsoleSession::writeLog(std::string data) { // saveCursor(); // setCursorLocation(16, 1); diff --git a/ConsoleSession.h b/ConsoleSession.h index b1369ff..b8c0ad0 100644 --- a/ConsoleSession.h +++ b/ConsoleSession.h @@ -16,6 +16,14 @@ namespace core { /// Extend the protocol() method in order to define the behavior and /// protocol interaction for this socket which is a console session. /// + /// The User:/Password: exchange is a real login when the owning + /// ConsoleServer was constructed with a credentialChecker (see its + /// own doc comment) - previously the entered username was never + /// even stored and the password was never compared against + /// anything, so any input (including blank) reached the command + /// prompt. A ConsoleServer built without a credentialChecker still + /// accepts anything, matching that original behavior exactly. + /// class ConsoleSession : public TerminalSession { @@ -28,11 +36,27 @@ namespace core { protected: void protocol(coreutils::ZString &data) override; + // Previously absent entirely, so the state machine only ever + // advanced on the CLIENT's first line of input - that line was + // consumed just to drive WELCOME->LOGIN and was never used as + // real content, meaning every subsequent line was shifted one + // slot early (what the user typed as their *username* actually + // got compared as nothing, their *password* got captured as + // enteredUser, and their first real command got compared as the + // password). Harmless before real credential checking existed - + // any input reached the prompt regardless - but it broke login + // outright once checkCredentials() started actually comparing + // values. Kicking the state machine off here instead means + // "User: " is sent immediately on connect, before the client + // has sent anything at all. + void onConnected() override; + private: enum Status {WELCOME, LOGIN, WAIT_USER_PROFILE, PASSWORD, WAIT_PASSWORD, PROMPT, INPUT, PROCESS, DONE}; Status status = WELCOME; void doCommand(coreutils::ZString &request); coreutils::ZString command; + std::string enteredUser; };