blob: 2e11d783d69287119a12098a1d527f9e76bdb1e5 [file] [log] [blame]
/* $Id$
*
* 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 org.apache.etch.bindings.java.transport.filters;
import org.apache.etch.bindings.java.msg.Field;
import org.apache.etch.bindings.java.msg.Message;
import org.apache.etch.bindings.java.msg.Type;
import org.apache.etch.bindings.java.msg.ValueFactory;
import org.apache.etch.bindings.java.support.Validator_boolean;
import org.apache.etch.bindings.java.support.Validator_string;
import org.apache.etch.bindings.java.transport.TransportMessage;
import org.apache.etch.util.Log;
import org.apache.etch.util.Resources;
import org.apache.etch.util.URL;
import org.apache.etch.util.core.Who;
import org.apache.etch.util.core.io.Transport;
/**
* PwAuth is a message filter which watches for Session.UP and attempts to
* authenticate the user by sending a message to the server with the name and
* password in it. If the server responds, the session is notified with the
* user name. If the server responds negatively, the session is notified of the
* failure (using a PwAuthFail event). A transport control may also be sent to
* force a login or logout or to set the name or password. The name and password
* are initialized from the uri, or prompted for if not specified.
*/
public class PwAuth extends AbstractMessageFilter
{
/**
* client sessionQuery: return user name to use for authentication.
* transportQuery: return the current user name (on server it has been validated).
* client transportControl: set user name to use for authentication.
*/
public static final String USER = "PwAuth.user";
/**
* client sessionQuery: return password to use for authentication.
* transportQuery: return the current password (on server it has been validated).
* client transportControl: set password to use for authentication.
*/
public static final String PASSWORD = "PwAuth.password";
/**
* client transportControl: send authentication request
*/
private static final String AUTHENTICATE = "PwAuth.authenticate";
/**
* @param transport
* @param uri
* @param resources
* @throws Exception
*/
public PwAuth( TransportMessage transport, URL uri, Resources resources )
throws Exception
{
super( transport, uri, resources );
server = (Boolean) transport.transportQuery( Transport.IS_SERVER );
if (!server)
{
user = uri.getUser();
password = uri.getPassword();
}
// Log.report( "KeepAliveInstalled",
// "delay", delay, "count", count, "server", server );
vf = (ValueFactory) resources.get( Transport.VALUE_FACTORY );
mt_request = new Type( "_Etch_PwAuthReq" );
mt_response = new Type( "_Etch_PwAuthResp" );
vf.addType( mt_request );
vf.addType( mt_response );
mf_user = new Field( "user" );
mf_password = new Field( "password" );
mf_ok = new Field( "ok" );
mf_status = new Field( "status" );
mt_request.putValidator( mf_user, Validator_string.get( 0 ) );
mt_request.putValidator( mf_password, Validator_string.get( 0 ) );
mt_request.setResult( mt_response );
mt_request.lock();
mt_response.putValidator( mf_ok, Validator_boolean.get( 0 ) );
mt_response.putValidator( mf_status, Validator_string.get( 0 ) );
mt_response.lock();
}
private String user;
private String password;
private final boolean server;
private final ValueFactory vf;
private final Field mf_user;
private final Field mf_password;
private final Field mf_ok;
private final Field mf_status;
private final Type mt_request;
private final Type mt_response;
@Override
public String toString()
{
return "PwAuth/"+transport;
}
/**
* @return the server operating mode.
*/
public boolean getServer()
{
return server;
}
@Override
public boolean sessionMessage( Who sender, Message msg ) throws Exception
{
if (msg.isType( mt_request ) )
{
handlePwAuthReq( sender, msg );
return true;
}
if (msg.isType( mt_response ))
{
handlePwAuthResp( sender, msg );
return true;
}
return super.sessionMessage( sender, msg );
}
@Override
protected boolean sessionUp() throws Exception
{
if (!server)
{
// client: send authentication request.
sendPwAuthReq();
}
return true;
}
@Override
protected boolean sessionDown() throws Exception
{
if (server)
{
// make sure that server session's idea of user is cleared.
sessionNotifyUser( null, null );
}
return true;
}
private void sendPwAuthReq() throws Exception
{
if (user == null)
{
user = (String) session.sessionQuery( USER );
if (user == null)
throw new IllegalStateException( "PwAuth: user == null" );
}
if (password == null)
{
password = (String) session.sessionQuery( PASSWORD );
if (password == null)
throw new IllegalStateException( "PwAuth: password == null" );
}
Message msg = new Message( mt_request, vf );
msg.put( mf_user, user );
msg.put( mf_password, password );
transport.transportMessage( null, msg );
}
private void handlePwAuthReq( Who sender, Message msg ) throws Exception
{
if (!server)
{
// we're a client that got a request, likely we're talking to a
// server that is confused.
return;
}
Log.report( "GotPwAuthReq", "sender", sender, "msg", msg );
String u = (String) msg.get( mf_user );
String p = (String) msg.get( mf_password );
try
{
OkStatus as = (OkStatus) sessionQuery( new UserPassword( u, p ) );
if (as.ok)
sessionNotifyUser( u, p );
else
sessionNotifyUser( null, null );
sendPwAuthResp( sender, msg, as.ok, as.status );
}
catch ( Exception e )
{
sessionNotifyUser( null, null );
sendPwAuthResp( sender, msg, false, e.toString() );
throw e;
}
}
private void sendPwAuthResp( Who recipient, Message msg, boolean result, String status )
throws Exception
{
Message rmsg = msg.reply();
rmsg.put( mf_ok, result );
rmsg.put( mf_status, status );
transport.transportMessage( recipient, rmsg );
}
private void handlePwAuthResp( Who sender, Message msg ) throws Exception
{
if (server)
{
// we're a server that got a response, likely we're talking to a
// client that is confused.
return;
}
Log.report( "GotPwAuthResp", "sender", sender, "msg", msg );
boolean result = (Boolean) msg.get( mf_ok );
String status = (String) msg.get( mf_status );
session.sessionNotify( new OkStatus( result, status ) );
}
@Override
public Object transportQuery( Object query ) throws Exception
{
if (query == USER)
return user;
if (query == PASSWORD)
return password;
return super.transportQuery( query );
}
@Override
public void transportControl( Object control, Object value )
throws Exception
{
if (control == USER)
{
checkNotServer();
user = (String) value;
return;
}
if (control == PASSWORD)
{
checkNotServer();
password = (String) value;
return;
}
if (control == AUTHENTICATE)
{
checkNotServer();
sendPwAuthReq();
return;
}
super.transportControl( control, value );
}
private void checkNotServer()
{
if (server)
throw new IllegalStateException( "control not permitted by server" );
}
@SuppressWarnings("hiding")
private void sessionNotifyUser( String user, String password )
throws Exception
{
this.user = user;
this.password = password;
session.sessionNotify( new UserPassword( user, password ) );
}
/**
* Server session query to get AuthOkStatus, also server session event
* to report changes in authentication status.
*/
public static class UserPassword
{
/**
* Constructs the CheckAuth.
*
* @param user name to check.
* @param password of the user.
*/
public UserPassword( String user, String password )
{
this.user = user;
this.password = password;
}
@Override
public String toString()
{
return String.format( "AuthUserPassword( %s )", user );
}
/** The user name to check */
public final String user;
/** The password of the user */
public final String password;
}
/**
* Server session query response to AuthUserPassword.
*/
public static class OkStatus
{
/**
* Constructs the AuthOkStatus.
*
* @param ok true if user / password valid.
* @param status message related to ok.
*/
public OkStatus( boolean ok, String status )
{
this.ok = ok;
this.status = status;
}
@Override
public String toString()
{
return String.format( "AuthOkStatus( %s, %s )", ok, status );
}
/** true if the user / password matched */
public final boolean ok;
/** message related to ok */
public final String status;
}
}