summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNat Goodspeed <nat@lindenlab.com>2024-08-02 19:24:07 -0400
committerNat Goodspeed <nat@lindenlab.com>2024-08-02 19:24:07 -0400
commitaf947de6928daa70098edb8346effb9dc8146f47 (patch)
treecb7d34770012bb4e9baf6354645182c9c8451fd9
parent1913ea4ec2f46458e2971e6e02afacf4da670af2 (diff)
Add 'LLPanelLogin' 'login', 'savedLogins' operations.
'login' accepts optional 'username', 'slurl', 'grid'. 'savedLogins' returns the list of saved usernames in both display form and internal form. Make LLPanelLogin::getUserName() accept (const LLPointer<LLCredential>&). There's a whole separate discussion pending as to whether const LLPointer<T> should provide access to non-const T methods. Similarly, make LLCredential::getIdentifier() a const method. These two changes enable read-only access to credentials. Make LLPanelLogin methods capture and reuse LLGridManager::instance() as appropriate. Add require/login.lua and test_login.lua.
-rw-r--r--indra/newview/llpanellogin.cpp57
-rw-r--r--indra/newview/llpanellogin.h2
-rw-r--r--indra/newview/llpanelloginlistener.cpp178
-rw-r--r--indra/newview/llpanelloginlistener.h2
-rw-r--r--indra/newview/llsecapi.h2
-rw-r--r--indra/newview/scripts/lua/require/login.lua21
-rw-r--r--indra/newview/scripts/lua/test_login.lua10
7 files changed, 226 insertions, 46 deletions
diff --git a/indra/newview/llpanellogin.cpp b/indra/newview/llpanellogin.cpp
index 86f56f0949..a6dbcedc4b 100644
--- a/indra/newview/llpanellogin.cpp
+++ b/indra/newview/llpanellogin.cpp
@@ -81,15 +81,16 @@ BOOL LLPanelLogin::sCredentialSet = FALSE;
LLPointer<LLCredential> load_user_credentials(std::string &user_key)
{
- if (gSecAPIHandler->hasCredentialMap("login_list", LLGridManager::getInstance()->getGrid()))
+ std::string grid{ LLGridManager::instance().getGrid() };
+ if (gSecAPIHandler->hasCredentialMap("login_list", grid))
{
// user_key should be of "name Resident" format
- return gSecAPIHandler->loadFromCredentialMap("login_list", LLGridManager::getInstance()->getGrid(), user_key);
+ return gSecAPIHandler->loadFromCredentialMap("login_list", grid, user_key);
}
else
{
// legacy (or legacy^2, since it also tries to load from settings)
- return gSecAPIHandler->loadCredential(LLGridManager::getInstance()->getGrid());
+ return gSecAPIHandler->loadCredential(grid);
}
}
@@ -223,7 +224,8 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect,
sendChildToBack(getChildView("forgot_password_text"));
sendChildToBack(getChildView("sign_up_text"));
- std::string current_grid = LLGridManager::getInstance()->getGrid();
+ LLGridManager& gridmgr{ LLGridManager::instance() };
+ std::string current_grid = gridmgr.getGrid();
if (!mFirstLoginThisInstall)
{
LLComboBox* favorites_combo = getChild<LLComboBox>("start_location_combo");
@@ -237,7 +239,7 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect,
// Load all of the grids, sorted, and then add a bar and the current grid at the top
server_choice_combo->removeall();
- std::map<std::string, std::string> known_grids = LLGridManager::getInstance()->getKnownGrids();
+ std::map<std::string, std::string> known_grids = gridmgr.getKnownGrids();
for (std::map<std::string, std::string>::iterator grid_choice = known_grids.begin();
grid_choice != known_grids.end();
grid_choice++)
@@ -251,7 +253,7 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect,
server_choice_combo->sortByName();
LL_DEBUGS("AppInit") << "adding current " << current_grid << LL_ENDL;
- server_choice_combo->add(LLGridManager::getInstance()->getGridLabel(),
+ server_choice_combo->add(gridmgr.getGridLabel(),
current_grid,
ADD_TOP);
server_choice_combo->selectFirstItem();
@@ -760,6 +762,7 @@ void LLPanelLogin::onUpdateStartSLURL(const LLSLURL& new_start_slurl)
LL_DEBUGS("AppInit")<<new_start_slurl.asString()<<LL_ENDL;
LLComboBox* location_combo = sInstance->getChild<LLComboBox>("start_location_combo");
+ LLGridManager& gridmgr{ LLGridManager::instance() };
/*
* Determine whether or not the new_start_slurl modifies the grid.
*
@@ -774,17 +777,17 @@ void LLPanelLogin::onUpdateStartSLURL(const LLSLURL& new_start_slurl)
{
case LLSLURL::LOCATION:
{
- std::string slurl_grid = LLGridManager::getInstance()->getGrid(new_start_slurl.getGrid());
+ std::string slurl_grid = gridmgr.getGrid(new_start_slurl.getGrid());
if ( ! slurl_grid.empty() ) // is that a valid grid?
{
- if ( slurl_grid != LLGridManager::getInstance()->getGrid() ) // new grid?
+ if ( slurl_grid != gridmgr.getGrid() ) // new grid?
{
// the slurl changes the grid, so update everything to match
- LLGridManager::getInstance()->setGridChoice(slurl_grid);
+ gridmgr.setGridChoice(slurl_grid);
// update the grid selector to match the slurl
LLComboBox* server_combo = sInstance->getChild<LLComboBox>("server_combo");
- std::string server_label(LLGridManager::getInstance()->getGridLabel(slurl_grid));
+ std::string server_label(gridmgr.getGridLabel(slurl_grid));
server_combo->setSimple(server_label);
updateServer(); // to change the links and splash screen
@@ -831,8 +834,7 @@ void LLPanelLogin::autologinToLocation(const LLSLURL& slurl)
if ( LLPanelLogin::sInstance != NULL )
{
- void* unused_parameter = 0;
- LLPanelLogin::sInstance->onClickConnect(unused_parameter);
+ LLPanelLogin::sInstance->onClickConnect(false);
}
}
@@ -872,7 +874,8 @@ void LLPanelLogin::loadLoginPage()
{
if (!sInstance) return;
- LLURI login_page = LLURI(LLGridManager::getInstance()->getLoginPage());
+ LLGridManager& gridmgr{ LLGridManager::instance() };
+ LLURI login_page = LLURI(gridmgr.getLoginPage());
LLSD params(login_page.queryMap());
LL_DEBUGS("AppInit") << "login_page: " << login_page << LL_ENDL;
@@ -899,7 +902,7 @@ void LLPanelLogin::loadLoginPage()
params["channel"] = LLVersionInfo::instance().getChannel();
// Grid
- params["grid"] = LLGridManager::getInstance()->getGridId();
+ params["grid"] = gridmgr.getGridId();
// add OS info
params["os"] = LLOSInfo::instance().getOSStringSimple();
@@ -916,7 +919,7 @@ void LLPanelLogin::loadLoginPage()
login_page.path(),
params));
- gViewerWindow->setMenuBackgroundColor(false, !LLGridManager::getInstance()->isInProductionGrid());
+ gViewerWindow->setMenuBackgroundColor(false, !gridmgr.isInProductionGrid());
LLMediaCtrl* web_browser = sInstance->getChild<LLMediaCtrl>("login_html");
if (web_browser->getCurrentNavUrl() != login_uri.asString())
@@ -949,9 +952,10 @@ void LLPanelLogin::onClickConnect(bool commit_fields)
// the grid definitions may come from a user-supplied grids.xml, so they may not be good
LL_DEBUGS("AppInit")<<"grid "<<combo_val.asString()<<LL_ENDL;
+ LLGridManager& gridmgr{ LLGridManager::instance() };
try
{
- LLGridManager::getInstance()->setGridChoice(combo_val.asString());
+ gridmgr.setGridChoice(combo_val.asString());
}
catch (LLInvalidGridName ex)
{
@@ -984,7 +988,7 @@ void LLPanelLogin::onClickConnect(bool commit_fields)
std::string identifier_type;
cred->identifierType(identifier_type);
LLSD allowed_credential_types;
- LLGridManager::getInstance()->getLoginIdentifierTypes(allowed_credential_types);
+ gridmgr.getLoginIdentifierTypes(allowed_credential_types);
// check the typed in credential type against the credential types expected by the server.
for(LLSD::array_iterator i = allowed_credential_types.beginArray();
@@ -1133,6 +1137,7 @@ void LLPanelLogin::updateServer()
{
if (sInstance)
{
+ LLGridManager& gridmgr{ LLGridManager::instance() };
try
{
// if they've selected another grid, we should load the credentials
@@ -1152,7 +1157,7 @@ void LLPanelLogin::updateServer()
// populate dropbox and setFields
// Note: following call is related to initializeLoginInfo()
- LLPointer<LLCredential> credential = gSecAPIHandler->loadCredential(LLGridManager::getInstance()->getGrid());
+ LLPointer<LLCredential> credential = gSecAPIHandler->loadCredential(gridmgr.getGrid());
sInstance->populateUserList(credential);
// restore creds
@@ -1165,12 +1170,12 @@ void LLPanelLogin::updateServer()
{
// populate dropbox and setFields
// Note: following call is related to initializeLoginInfo()
- LLPointer<LLCredential> credential = gSecAPIHandler->loadCredential(LLGridManager::getInstance()->getGrid());
+ LLPointer<LLCredential> credential = gSecAPIHandler->loadCredential(gridmgr.getGrid());
sInstance->populateUserList(credential);
}
// update the login panel links
- bool system_grid = LLGridManager::getInstance()->isSystemGrid();
+ bool system_grid = gridmgr.isSystemGrid();
// Want to vanish not only create_new_account_btn, but also the
// title text over it, so turn on/off the whole layout_panel element.
@@ -1219,11 +1224,12 @@ void LLPanelLogin::populateUserList(LLPointer<LLCredential> credential)
getChild<LLUICtrl>("password_edit")->setValue(std::string());
mUsernameLength = 0;
mPasswordLength = 0;
+ std::string grid{ LLGridManager::instance().getGrid() };
- if (gSecAPIHandler->hasCredentialMap("login_list", LLGridManager::getInstance()->getGrid()))
+ if (gSecAPIHandler->hasCredentialMap("login_list", grid))
{
LLSecAPIHandler::credential_map_t credencials;
- gSecAPIHandler->loadCredentialMap("login_list", LLGridManager::getInstance()->getGrid(), credencials);
+ gSecAPIHandler->loadCredentialMap("login_list", grid, credencials);
LLSecAPIHandler::credential_map_t::iterator cr_iter = credencials.begin();
LLSecAPIHandler::credential_map_t::iterator cr_end = credencials.end();
@@ -1278,7 +1284,8 @@ void LLPanelLogin::onSelectServer()
LLComboBox* server_combo = getChild<LLComboBox>("server_combo");
LLSD server_combo_val = server_combo->getSelectedValue();
LL_INFOS("AppInit") << "grid "<<server_combo_val.asString()<< LL_ENDL;
- LLGridManager::getInstance()->setGridChoice(server_combo_val.asString());
+ auto& gridmgr{ LLGridManager::instance() };
+ gridmgr.setGridChoice(server_combo_val.asString());
addFavoritesToStartLocation();
/*
@@ -1308,7 +1315,7 @@ void LLPanelLogin::onSelectServer()
LLSLURL slurl(location); // generata a slurl from the location combo contents
if (location.empty()
|| (slurl.getType() == LLSLURL::LOCATION
- && slurl.getGrid() != LLGridManager::getInstance()->getGrid())
+ && slurl.getGrid() != gridmgr.getGrid())
)
{
// the grid specified by the location is not this one, so clear the combo
@@ -1338,7 +1345,7 @@ bool LLPanelLogin::getShowFavorites()
}
// static
-std::string LLPanelLogin::getUserName(LLPointer<LLCredential> &cred)
+std::string LLPanelLogin::getUserName(const LLPointer<LLCredential> &cred)
{
if (cred.isNull())
{
diff --git a/indra/newview/llpanellogin.h b/indra/newview/llpanellogin.h
index 7f4c2487bf..a7019ea2a3 100644
--- a/indra/newview/llpanellogin.h
+++ b/indra/newview/llpanellogin.h
@@ -88,7 +88,7 @@ public:
static bool getShowFavorites();
// extract name from cred in a format apropriate for username field
- static std::string getUserName(LLPointer<LLCredential> &cred);
+ static std::string getUserName(const LLPointer<LLCredential> &cred);
private:
friend class LLPanelLoginListener;
diff --git a/indra/newview/llpanelloginlistener.cpp b/indra/newview/llpanelloginlistener.cpp
index fb6f034b7f..a4418145d7 100644
--- a/indra/newview/llpanelloginlistener.cpp
+++ b/indra/newview/llpanelloginlistener.cpp
@@ -32,9 +32,19 @@
#include "llpanelloginlistener.h"
// STL headers
// std headers
+#include <iomanip>
+#include <memory>
// external library headers
// other Linden headers
+#include "llcombobox.h"
#include "llpanellogin.h"
+#include "llsdutil.h"
+#include "llsecapi.h"
+#include "llslurl.h"
+#include "llstartup.h"
+#include "lluictrl.h"
+#include "llviewernetwork.h"
+#include "stringize.h"
LLPanelLoginListener::LLPanelLoginListener(LLPanelLogin* instance):
LLEventAPI("LLPanelLogin", "Access to LLPanelLogin methods"),
@@ -43,9 +53,177 @@ LLPanelLoginListener::LLPanelLoginListener(LLPanelLogin* instance):
add("onClickConnect",
"Pretend user clicked the \"Log In\" button",
&LLPanelLoginListener::onClickConnect);
+ add("login",
+ "Login to Second Life with saved credentials.\n"
+ "Pass [\"username\"] to select credentials previously saved with that username.\n"
+ "Pass [\"slurl\"] to specify target location.\n"
+ "Pass [\"grid\"] to select one of the valid grids in grids.xml.",
+ &LLPanelLoginListener::login,
+ llsd::map("reply", LLSD::String()));
+ add("savedLogins",
+ "Return \"logins\" as a list of {} saved login names.\n"
+ "Pass [\"grid\"] to select one of the valid grids in grids.xml.",
+ &LLPanelLoginListener::savedLogins,
+ llsd::map("reply", LLSD::String()));
}
void LLPanelLoginListener::onClickConnect(const LLSD&) const
{
mPanel->onClickConnect(false);
}
+
+void LLPanelLoginListener::login(const LLSD& args) const
+{
+ // Although we require a "reply" key, don't instantiate a Response object:
+ // if things go well, response to our invoker will come later, once the
+ // viewer gets the response from the login server.
+ std::string username(args["username"]), slurlstr(args["slurl"]), grid(args["grid"]);
+ LL_DEBUGS("AppInit") << "Login with saved credentials";
+ if (! username.empty())
+ {
+ LL_CONT << " for user " << std::quoted(username);
+ }
+ if (! slurlstr.empty())
+ {
+ LL_CONT << " to location " << std::quoted(slurlstr);
+ }
+ if (! grid.empty())
+ {
+ LL_CONT << " on grid " << std::quoted(grid);
+ }
+ LL_CONT << LL_ENDL;
+
+ // change grid first, allowing slurl to override
+ auto& gridmgr{ LLGridManager::instance() };
+ if (! grid.empty())
+ {
+ // setGridChoice() can throw LLInvalidGridName -- but if so, let it
+ // propagate, trusting that LLEventAPI will catch it and send an
+ // appropriate reply.
+ auto server_combo = mPanel->getChild<LLComboBox>("server_combo");
+ server_combo->setSimple(gridmgr.getGridLabel(grid));
+ LLPanelLogin::updateServer();
+ }
+ if (! slurlstr.empty())
+ {
+ LLSLURL slurl(slurlstr);
+ if (! slurl.isSpatial())
+ {
+ // don't bother with LLTHROW() because we expect LLEventAPI to
+ // catch and report back to invoker
+ throw DispatchError(stringize("Invalid start SLURL ", std::quoted(slurlstr)));
+ }
+ // Bypass LLStartUp::setStartSLURL() because, after validating as
+ // above, it bounces right back to LLPanelLogin::onUpdateStartSLURL().
+ // It also sets the "NextLoginLocation" user setting, but as with grid,
+ // we don't yet know whether that's desirable for scripted login.
+ LLPanelLogin::onUpdateStartSLURL(slurl);
+ }
+ if (! username.empty())
+ {
+ // Transform (e.g.) "Nat Linden" to the internal form expected by
+ // loadFromCredentialMap(), e.g. "nat_linden"
+ LLStringUtil::toLower(username);
+ LLStringUtil::replaceChar(username, ' ', '_');
+ LLStringUtil::replaceChar(username, '.', '_');
+
+ // call gridmgr.getGrid() because our caller didn't necessarily pass
+ // ["grid"] -- or it might have been overridden by the SLURL
+ auto cred = gSecAPIHandler->loadFromCredentialMap("login_list",
+ gridmgr.getGrid(),
+ username);
+ LLPanelLogin::setFields(cred);
+ }
+
+ if (mPanel->getChild<LLUICtrl>("username_combo")->getValue().asString().empty() ||
+ mPanel->getChild<LLUICtrl>("password_edit") ->getValue().asString().empty())
+ {
+ // as above, let LLEventAPI report back to invoker
+ throw DispatchError(stringize("Unrecognized username ", std::quoted(username)));
+ }
+
+ // Here we're about to trigger actual login, which is all we can do in
+ // this method. All subsequent responses must come via the "login"
+ // LLEventPump.
+ LLEventPumps::instance().obtain("login").listen(
+ "Lua login",
+ [args]
+ (const LLBoundListener& conn, const LLSD& update)
+ {
+ LLSD response{ llsd::map("ok", false) };
+ if (update["change"] == "connect")
+ {
+ // success!
+ response["ok"] = true;
+ }
+ else if (update["change"] == "disconnect")
+ {
+ // Does this ever actually happen?
+ // LLLoginInstance::handleDisconnect() says it's a placeholder.
+ response["error"] = "login disconnect";
+ }
+ else if (update["change"] == "fail.login")
+ {
+ // why?
+ // LLLoginInstance::handleLoginFailure() has special code to
+ // handle "tos", "update" and "mfa_challenge". Do not respond
+ // automatically because these things are SUPPOSED to engage
+ // the user.
+ // Copy specific response fields because there's an enormous
+ // chunk of stuff that comes back on success.
+ LLSD data{ update["data"] };
+ const char* fields[] = {
+ "reason",
+ "error_code"
+ };
+ for (auto field : fields)
+ {
+ response[field] = data[field];
+ }
+ response["error"] = update["message"];
+ }
+ else
+ {
+ // Ignore all other "change" values: LLLogin sends multiple update
+ // events. Only a few of them (above) indicate completion.
+ return;
+ }
+ // For all the completion cases, disconnect from the "login"
+ // LLEventPump.
+ conn.disconnect();
+ // and at last, send response to the original invoker
+ sendReply(response, args);
+ });
+
+ // Turn the Login crank.
+ mPanel->onClickConnect(false);
+}
+
+LLSD LLPanelLoginListener::savedLogins(const LLSD& args) const
+{
+ LL_INFOS() << "called with " << args << LL_ENDL;
+ std::string grid = (args.has("grid")? args["grid"].asString()
+ : LLGridManager::instance().getGrid());
+ if (! gSecAPIHandler->hasCredentialMap("login_list", grid))
+ {
+ LL_INFOS() << "no creds for " << grid << LL_ENDL;
+ return llsd::map("error",
+ stringize("No saved logins for grid ", std::quoted(grid)));
+ }
+ LLSecAPIHandler::credential_map_t creds;
+ gSecAPIHandler->loadCredentialMap("login.list", grid, creds);
+ auto result = llsd::map(
+ "logins",
+ llsd::toArray(
+ creds,
+ [](const auto& pair)
+ {
+ return llsd::map(
+ "name",
+ LLPanelLogin::getUserName(pair.second),
+ "key",
+ pair.first);
+ }));
+ LL_INFOS() << "returning creds " << result << LL_ENDL;
+ return result;
+}
diff --git a/indra/newview/llpanelloginlistener.h b/indra/newview/llpanelloginlistener.h
index b552ccd5b0..f2f4d70ff9 100644
--- a/indra/newview/llpanelloginlistener.h
+++ b/indra/newview/llpanelloginlistener.h
@@ -40,6 +40,8 @@ public:
private:
void onClickConnect(const LLSD&) const;
+ void login(const LLSD&) const;
+ LLSD savedLogins(const LLSD&) const;
LLPanelLogin* mPanel;
};
diff --git a/indra/newview/llsecapi.h b/indra/newview/llsecapi.h
index 5cc78d09dc..367bfe1469 100644
--- a/indra/newview/llsecapi.h
+++ b/indra/newview/llsecapi.h
@@ -311,7 +311,7 @@ public:
mIdentifier = identifier;
mAuthenticator = authenticator;
}
- virtual LLSD getIdentifier() { return mIdentifier; }
+ virtual LLSD getIdentifier() const { return mIdentifier; }
virtual void identifierType(std::string& idType);
virtual LLSD getAuthenticator() { return mAuthenticator; }
virtual void authenticatorType(std::string& authType);
diff --git a/indra/newview/scripts/lua/require/login.lua b/indra/newview/scripts/lua/require/login.lua
index 0d8591cace..f4e65b1606 100644
--- a/indra/newview/scripts/lua/require/login.lua
+++ b/indra/newview/scripts/lua/require/login.lua
@@ -1,19 +1,12 @@
-local UI = require 'UI'
local leap = require 'leap'
+local startup = require 'startup'
+local mapargs = require 'mapargs'
-local function login(username, password)
- if username and password then
- local userpath = '//username_combo/Combo Text Entry'
- local passpath = '//password_edit'
- -- first clear anything presently in those text fields
- for _, path in pairs({userpath, passpath}) do
- UI.click(path)
- UI.keypress{keysym='Backsp', path=path}
- end
- UI.type{path=userpath, text=username}
- UI.type{path=passpath, text=password}
- end
- leap.send('LLPanelLogin', {op='onClickConnect'})
+local function login(...)
+ startup.wait('STATE_LOGIN_WAIT')
+ local args = mapargs('username,grid,slurl', ...)
+ args.op = 'login'
+ return leap.request('LLPanelLogin', args)
end
return login
diff --git a/indra/newview/scripts/lua/test_login.lua b/indra/newview/scripts/lua/test_login.lua
index a8c31807bc..f263940f79 100644
--- a/indra/newview/scripts/lua/test_login.lua
+++ b/indra/newview/scripts/lua/test_login.lua
@@ -1,7 +1,7 @@
-startup = require 'startup'
+inspect = require 'inspect'
login = require 'login'
-startup.wait('STATE_LOGIN_WAIT')
-login()
--- Fill in valid credentials as they would be entered on the login screen
--- login('My Username', 'password')
+print(inspect(login{
+ username='Nat Linden',
+-- grid='agni'
+ }))