@@ -111,6 +111,54 @@ class ExternalAuthTest : public TestappClientTest {
111111 provider->sendFrame (frame);
112112 }
113113
114+ std::unique_ptr<MemcachedConnection> loginOsbourne () {
115+ auto ret = getConnection ().clone ();
116+
117+ BinprotSaslAuthCommand saslAuthCommand;
118+ saslAuthCommand.setChallenge ({" \0 osbourne\0 password" , 18 });
119+ saslAuthCommand.setMechanism (" PLAIN" );
120+ ret->sendCommand (saslAuthCommand);
121+
122+ stepAuthProvider ();
123+
124+ // Now read out the response from the client
125+ BinprotResponse response;
126+ ret->recvResponse (response);
127+ if (!response.isSuccess ()) {
128+ return {};
129+ }
130+
131+ return ret;
132+ }
133+
134+ /* *
135+ * We've got multiple worker threads and if we try to disconnect a user
136+ * connected to one thread that thread may not be sheduled to run
137+ * immediately, so we may end up serving requests for another worker
138+ * thread.
139+ *
140+ * To work around this (and try to avoid spurious test failures) we'll
141+ * try to run the command a few times before we give up.
142+ *
143+ * @param content what we want the content of the users list to be
144+ */
145+ void waitForUserList (const std::string& content) {
146+ const auto start = std::chrono::steady_clock::now ();
147+ do {
148+ auto & conn = getAdminConnection ();
149+ auto resp = conn.execute (BinprotGetActiveUsersCommand{});
150+ if (resp.isSuccess () && content == resp.getDataString ()) {
151+ return ;
152+ }
153+ } while ((std::chrono::steady_clock::now () - start) <
154+ std::chrono::seconds (2 ));
155+
156+ auto & conn = getAdminConnection ();
157+ auto resp = conn.execute (BinprotGetActiveUsersCommand{});
158+ FAIL () << " Timed out trying to get expected content [" << content
159+ << " ] Current content is: " << resp.getDataString ();
160+ }
161+
114162 std::unique_ptr<MemcachedConnection> provider;
115163};
116164
@@ -255,3 +303,36 @@ TEST_P(ExternalAuthTest, TestReloadRbacDbDontNukeExternalUsers) {
255303 EXPECT_EQ (" external" , json[" osbourne" ][" domain" ])
256304 << response.getDataString ();
257305}
306+
307+ TEST_P (ExternalAuthTest, GetActiveUsers) {
308+ // Log in a few "local" users
309+ auto & conn = getConnection ();
310+ auto clone1 = conn.clone ();
311+ auto clone2 = conn.clone ();
312+ auto clone3 = conn.clone ();
313+ auto clone4 = conn.clone ();
314+
315+ clone1->authenticate (" smith" , " smithpassword" , " PLAIN" );
316+ clone2->authenticate (" smith" , " smithpassword" , " PLAIN" );
317+ clone3->authenticate (" jones" , " jonespassword" , " PLAIN" );
318+ clone4->authenticate (" @admin" , " password" , " PLAIN" );
319+
320+ // Log in 2 external ones
321+ auto osbourne1 = loginOsbourne ();
322+ EXPECT_TRUE (osbourne1);
323+
324+ auto osbourne2 = loginOsbourne ();
325+ EXPECT_TRUE (osbourne2);
326+
327+ waitForUserList (R"( ["osbourne"])" );
328+
329+ // Log out one of the external users
330+ osbourne1.reset ();
331+
332+ waitForUserList (R"( ["osbourne"])" );
333+
334+ // Log out the second external user
335+ osbourne2.reset ();
336+
337+ waitForUserList (R"( [])" );
338+ }
0 commit comments