package org.jivesoftware.openfire.plugin.ofmeet.jetty; import java.util.*; import org.eclipse.jetty.security.DefaultUserIdentity; import org.eclipse.jetty.server.UserIdentity; import org.jivesoftware.openfire.XMPPServer; import org.jivesoftware.openfire.auth.*; import org.jivesoftware.openfire.plugin.ofmeet.TokenManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.security.auth.Subject; import java.security.Principal; /** * A Jetty LoginService implementation, that makes available an access token for a user that's logged in through Jetty. * The token, managed by the singleton {@link TokenManager} instance, is valid until the user is logged out. * * @author Guus der Kinderen, guus.der.kinderen@gmail.com */ public class OfMeetLoginService extends AbstractLoginService { private static final Logger Log = LoggerFactory.getLogger( OfMeetLoginService.class ); public static final Map<String, AuthToken> authTokens = new HashMap<String, AuthToken>(); private final TokenManager tokenManager = TokenManager.getInstance(); /** * AuthFactory supports both a bare username, as well as user@domain. However, UserManager only accepts the bare * username. If the provided value includes a domain, return only the node-part (after verifying that it's actually * a user of our domain). * * @param username A authentication ID, either as 'user@domain' or 'user' (not null, not empty) * @return the 'user' part (never null). */ public static String asUserNameOfDomain( String username ) { final String[] parts = username.split( "@", 2 ); if ( parts.length > 1 ) { if ( XMPPServer.getInstance().getServerInfo().getXMPPDomain().equals( parts[ 1 ] ) ) { username = parts[ 0 ]; } else { return null; } } return username; } /** * Login a user. * * @param username The user name * @param credentials The users credentials * @return A UserIdentity if the credentials matched, otherwise null */ @Override public UserIdentity login( String userName, Object credentials ) { String username = asUserNameOfDomain( userName ); if (username == null) { return null; } try { final AuthToken authToken = AuthFactory.authenticate( username, (String) credentials ); final Principal principal = new KnownUser( authToken.getUsername(), userName); final String[] roles = new String[]{"ofmeet"}; final Subject subject = new Subject(); subject.getPrincipals().add( principal ); subject.setReadOnly(); final UserIdentity identity = new DefaultUserIdentity( subject, principal, roles ); tokenManager.registerIfAbsent( principal ); Log.info( "Login succeeded for local user " + username ); authTokens.put(username, authToken); return identity; } catch ( UnauthorizedException ex ) { // Wrong password provided by user. Log.info( "Login failed for " + username, ex ); return null; } catch ( Exception ex ) { // System failure! Configuration should be checked server-sided. Log.error( "An internal problem caused authentication to fail for "+ username, ex ); return null; } } /** * Validate that a UserIdentity previously created by a call {@link #login(String, Object)} is still valid. * * @param identity The user to validate * @return true if authentication has not been revoked for the user. */ @Override public boolean validate( UserIdentity identity ) { return tokenManager.containsToken( identity.getUserPrincipal() ); } /** * Revoke authentication for the user * * @param identity the user to invalidate. */ @Override public synchronized void logout( UserIdentity identity ) { tokenManager.revoke( identity.getUserPrincipal() ); } }