You are viewing a plain text version of this content. The canonical link for it is here.
Posted to user@guacamole.apache.org by Konrad Schroder <pe...@uw.edu> on 2023/05/25 19:37:55 UTC

Terminating active connections from auth provider extension

Hello all,

We operate a shared instrument facility. We would like to provide remote 
access to some of our instruments to users to allow them to monitor the 
progress of their experiments, without also allowing them to monitor the 
progress of other users' experiments.  Our system knows who is signed 
into a given instrument at any time, and produces "coral tokens" that 
signify a user's right to connect to a given instrument.  We also need 
the system to be able to revoke that access, closing any active 
Guacamole sessions, when the user's instrument session ends and their 
coral token is destroyed.

I've implemented an auth extension that grants remote access by coral 
token: that part is working fine. What I don't see is how the auth 
extension can kick users out. My best guess so far has been to store the 
AuthenticatedUser and UserContext structures and use them to look up the 
active connections later; but when I use the stored UserContext to close 
the active connections, there are none.

My auth provider, reduced to only the parts that store and retrieve 
those structures and the method that revokes authorization, looks like this:

public class CoralAuthenticationProvider extends 
AbstractAuthenticationProvider {

// [...]

private class TokenInfo { String m_token = null; // This is the coral 
token, not guacamole token UserContext m_userContext = null; 
CoralAuthenticatedUser m_authenticatedUser = *null*;

[...]

}

// A map from "coral token" to TokenInfo

*private static* Map<String, TokenInfo> s_tokenInfo = new 
HashMap<String, TokenInfo>();

// CoralAuthenticatedUser is a clone of SimpleAuthenticatedUser except 
for the "coraltoken" field. // (I'd extend SimpleAuthenticatedUser, but 
it's a private class in an inaccessible scope.)

private class CoralAuthenticatedUser extends AbstractAuthenticatedUser { 
// [... all of SimpleAuthenticatedUser ...]

private String coraltoken;

public void setCoralToken(String token) {

this.coraltoken = token;

}

public String getCoralToken() {

return this.coraltoken;

}

}

/*

* Authenticate the user, and store the AuthenticatedUser in s_tokenInfo

* so we can look it up by coral token later. * Also store the coral 
token in the AuthenticatedUser for later consumption by getUserContext().

*/

@Override

public AuthenticatedUser authenticateUser(Credentials credentials) {

// [Actually authenticate the user based on "coral token"; then,]

TokenInfo ti = new TokenInfo();

ti.m_token = [the coral token];

GuacamoleConfigs configs = [the configs available based on the coral token];

CoralAuthenticatedUser sau = new CoralAuthenticatedUser(credentials, 
configs);

sau.setCoralToken(ti.m_token);

ti.m_authenticatedUser = sau;

s_tokenInfo.put(ti.m_token, ti);

return sau; }

/* * Pull the coral token out of the AuthenticatedUser and use it to 
store the UserContext * in s_tokenInfo as well. */ @Override

public UserContext getUserContext(AuthenticatedUser authenticatedUser)

throws GuacamoleException {

if (!(authenticatedUser instanceof CoralAuthenticatedUser))

return null;

CoralAuthenticatedUser cau = (CoralAuthenticatedUser)authenticatedUser;

// Return as unauthorized if not authorized to retrieve configs

if (cau.configs == null)

return null;

// Return user context restricted to authorized configs

UserContext userContext = new SimpleUserContext(this, 
authenticatedUser.getIdentifier(), cau.configs, true); // Retrieve the 
TokenInfo and store the UserContext in it too

TokenInfo ti = s_tokenInfo.get(cau.getCoralToken());

ti.m_userContext = userContext;

ti.m_authenticatedUser = cau;

return userContext;

}

/* * Revoke authorization using the data stored in the previous two 
methods. * This should de-authenticate the user, remove all active 
contexts * and terminate all active sessions. */ public static void 
revokeToken(TokenInfo ti) throws GuacamoleException {

Directory<ActiveConnection> dac = 
ti.m_userContext.getActiveConnectionDirectory();

for (String connectionId : dac.getIdentifiers()) {

ActiveConnection ac = dac.get(connectionId);

System.out.println("Disconnect connection " + connectionId);

GuacamoleWriter gw = ac.getTunnel().acquireWriter();

gw.writeInstruction(new GuacamoleInstruction("disconnect", new 
LinkedList<String>()));

ac.getTunnel().releaseWriter();

System.out.println("Close tunnel for connection " + connectionId);

ac.getTunnel().close();

}

System.out.println("Invalidate AU for user token " + ti.m_token);

ti.m_authenticatedUser.invalidate();

System.out.println("Invalidate UC for user token " + ti.m_token);

ti.m_userContext.invalidate();

}

// [ ... ]

}

When revokeToken() is called, the "Invalidate..." lines print but the 
"Disconnect connection/Close tunnel" lines don't: there are no active 
connections attached to the UserContext, despite there being, in fact, 
active connections. Do I have a simple implementation error, or is this 
just the wrong way to go about this?

If I did manage to get a valid list of active connections, is this the 
right way to terminate them, or should I be doing something else?

Thanks,

-- 
-----------------------------------------------------------------------
Konrad Schroder             Box 352143 -or- 115D Fluke Hall, Mason Road
Software Developer and Sys Admin               University of Washington
College of Engineering                          Seattle, WA, 98195, USA


---------------------------------------------------------------------
To unsubscribe, e-mail: user-unsubscribe@guacamole.apache.org
For additional commands, e-mail: user-help@guacamole.apache.org