flex.messaging.services.AuthenticationService Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package flex.messaging.services;
import java.io.UnsupportedEncodingException;
import java.security.Principal;
import java.util.concurrent.CopyOnWriteArrayList;
import flex.messaging.FlexContext;
import flex.messaging.FlexSession;
import flex.messaging.MessageBroker;
import flex.messaging.MessageException;
import flex.messaging.client.FlexClient;
import flex.messaging.config.ConfigMap;
import flex.messaging.endpoints.Endpoint;
import flex.messaging.log.Log;
import flex.messaging.log.LogCategories;
import flex.messaging.messages.CommandMessage;
import flex.messaging.messages.Message;
import flex.messaging.security.LoginManager;
import flex.messaging.security.SecurityException;
import flex.messaging.util.Base64;
/**
* Core service that is automatically created and registered with a MessageBroker.
* It handles login and logout commands from clients.
* The service implementation is internal, but customer code may look up the service by id
* using MessageBroker#getService(String)
, and register as an AuthenticationListener for
* AuthenticationEvents. An authentication event is dispatched following successful and after
* successful logout.
*/
public class AuthenticationService extends AbstractService
{
private static final int INVALID_CREDENTIALS_ERROR = 10064;
/**
* The well-known id that the AuthenticationService is bound to the MessageBroker under.
*/
public static final String ID = "authentication-service";
/**
* @exclude
*/
public AuthenticationService()
{
this(false);
}
/**
* @exclude
*/
public AuthenticationService(boolean enableManagement)
{
// this service can never be managed
super(false);
super.setId(ID);
}
/**
* Internal thread-safe storage for AuthenticationListeners.
*/
private final CopyOnWriteArrayList authenticationListeners = new CopyOnWriteArrayList();
/**
* Registers an AuthenticationListener to receive AuthenticationEvents.
*
* @param listener The AuthenticationListener to register.
*/
public void addAuthenticationListener(final AuthenticationListener listener)
{
authenticationListeners.addIfAbsent(listener);
}
/**
* Unregisters an AuthenticationListener.
*
* @param listener The AuthenticationListener to unregister.
*/
public void removeAuthenticationListener(final AuthenticationListener listener)
{
authenticationListeners.remove(listener);
}
// This service's id should never be changed
/**
* @exclude
*/
public void setId(String id)
{
// No-op
}
// This service should not be visible to the client
/**
* @exclude
*/
public ConfigMap describeService(Endpoint endpoint)
{
return null;
}
/**
* @exclude
*/
public Object serviceMessage(Message message)
{
return null;
}
/**
* @exclude
*/
public Object serviceCommand(CommandMessage msg)
{
LoginManager lm = getMessageBroker().getLoginManager();
switch (msg.getOperation())
{
case CommandMessage.LOGIN_OPERATION:
if (msg.getBody() instanceof String)
{
String encoded = (String)msg.getBody();
Object charsetHeader = msg.getHeader(CommandMessage.CREDENTIALS_CHARSET_HEADER);
if (charsetHeader instanceof String)
decodeAndLoginWithCharset(encoded, lm, (String)charsetHeader);
else
decodeAndLoginWithCharset(encoded, lm, null);
}
break;
case CommandMessage.LOGOUT_OPERATION:
// Generate event first, to capture refs to Principal/FlexSession/etc.
AuthenticationEvent logoutEvent = buildAuthenticationEvent(null, null); // null username and creds.
lm.logout();
// Success - notify listeners.
for (AuthenticationListener listener : authenticationListeners)
{
try
{
listener.logoutSucceeded(logoutEvent);
}
catch (Throwable t)
{
if (Log.isError())
Log.getLogger(LogCategories.SECURITY).error("AuthenticationListener {0} threw an exception handling a logout event.", new Object[] {listener}, t);
}
}
break;
default:
throw new MessageException("Service Does Not Support Command Type " + msg.getOperation());
}
return "success";
}
/**
* @exclude
*/
@Override
public void stop()
{
super.stop();
authenticationListeners.clear();
}
/**
* @exclude
*/
public void decodeAndLogin(String encoded, LoginManager lm)
{
decodeAndLoginWithCharset(encoded, lm, null);
}
/**
* @exclude
*/
private void decodeAndLoginWithCharset(String encoded, LoginManager lm, String charset)
{
String username = null;
String password = null;
Base64.Decoder decoder = new Base64.Decoder();
decoder.decode(encoded);
String decoded = "";
// Charset-aware decoding of the credentials bytes
if (charset != null)
{
try
{
decoded = new String(decoder.drain(), charset);
}
catch (UnsupportedEncodingException ex)
{
}
}
else
{
decoded = new String(decoder.drain());
}
int colon = decoded.indexOf(":");
if (colon > 0 && colon < decoded.length() - 1)
{
username = decoded.substring(0, colon);
password = decoded.substring(colon + 1);
}
if (username != null && password != null)
{
lm.login(username, password);
// Success - notify listeners.
AuthenticationEvent loginEvent = buildAuthenticationEvent(username, password);
for (AuthenticationListener listener : authenticationListeners)
{
try
{
listener.loginSucceeded(loginEvent);
}
catch (Throwable t)
{
if (Log.isError())
Log.getLogger(LogCategories.SECURITY).error("AuthenticationListener {0} threw an exception handling a login event.", new Object[] {listener}, t);
}
}
}
else
{
SecurityException se = new SecurityException();
se.setCode(SecurityException.CLIENT_AUTHENTICATION_CODE);
se.setMessage(INVALID_CREDENTIALS_ERROR);
throw se;
}
}
/**
* @exclude
*/
protected void setupServiceControl(MessageBroker broker)
{
// not doing anything
}
/**
* Utility method to build an AuthenticationEvent based on the current
* thread-local state for the remote user.
*
* @return An AuthenticationEvent that captures references to the server state for the remote user.
*/
private AuthenticationEvent buildAuthenticationEvent(String username, Object credentials)
{
Principal principal = FlexContext.getUserPrincipal();
FlexClient flexClient = FlexContext.getFlexClient();
FlexSession flexSession = FlexContext.getFlexSession();
return new AuthenticationEvent(this, username, credentials, principal, flexSession, flexClient);
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy