public /*synchronized*/ final ConnectReturnQosServer connect(ConnectQosServer connectQos, String secretSessionId) throws XmlBlasterException
{
if (connectQos.getSessionQos().getSessionName().getLoginName().equals(this.glob.getId())) {
String text = "You are not allowed to login with the cluster node name " + connectQos.getSessionName().toString() + ", access denied.";
log.warning(text);
throw new XmlBlasterException(glob, ErrorCode.USER_CONFIGURATION_IDENTICALCLIENT,
ME+".connect()", text);
}
{ // Administrative block clients
SubjectInfo si = getSubjectInfoByName(connectQos.getSessionQos().getSessionName());
if (si != null && si.isBlockClientLogin()) {
// Future todo: Throw out existing session, not only block new
// logins
log.warning("Access for " + connectQos.getSessionQos().getSessionName().toString() + " is blocked and denied (jconsole->blockClientLogin)");
throw new XmlBlasterException(glob, ErrorCode.COMMUNICATION_NOCONNECTION_SERVERDENY, ME + ".connect()",
"Access for "
+ connectQos.getSessionQos().getSessionName().toString()
+ " is currently not possible, please contact the server administrator");
}
{
SessionInfo sesi = getSessionInfoByName(connectQos.getSessionQos().getSessionName());
if (sesi != null && sesi.isBlockClientSessionLogin()) {
// Future todo: Throw out existing session, not only block new
// logins
log.warning("Access for " + connectQos.getSessionQos().getSessionName().toString()
+ " is blocked and denied (jconsole->blockClientSessionLogin)");
throw new XmlBlasterException(glob, ErrorCode.COMMUNICATION_NOCONNECTION_SERVERDENY, ME + ".connect()",
"Access for " + connectQos.getSessionQos().getSessionName().toString()
+ " session is currently not possible, please contact the server administrator");
}
}
}
// [1] Try reconnecting with secret sessionId
try {
if (log.isLoggable(Level.FINE)) log.fine("Entering connect(sessionName=" + connectQos.getSessionName().getAbsoluteName() + ")"); // " secretSessionId=" + secretSessionId + ")");
if (log.isLoggable(Level.FINEST)) log.finest("ConnectQos=" + connectQos.toXml());
// Get or create the secretSessionId (we respect a user supplied secretSessionId) ...
if (secretSessionId == null || secretSessionId.length() < 2) {
secretSessionId = connectQos.getSessionQos().getSecretSessionId();
if (secretSessionId != null && secretSessionId.length() >= 2)
log.info(connectQos.getSessionName().getAbsoluteName() + " is using secretSessionId '" + secretSessionId + "' from ConnectQos");
}
if (secretSessionId != null && secretSessionId.length() >= 2) {
SessionInfo info = getSessionInfo(secretSessionId);
if (info != null) { // authentication succeeded
updateConnectQos(info, connectQos);
ConnectReturnQosServer returnQos = new ConnectReturnQosServer(glob, info.getConnectQos().getData());
returnQos.getSessionQos().setSecretSessionId(secretSessionId);
returnQos.getSessionQos().setSessionName(info.getSessionName());
returnQos.setReconnected(true);
returnQos.getData().addClientProperty(Constants.CLIENTPROPERTY_RCVTIMESTAMPSTR, IsoDateParser.getCurrentUTCTimestampNanos());
log.info("Reconnected with given secretSessionId.");
return returnQos;
}
}
}
catch (Throwable e) {
log.severe("Internal error when trying to reconnect to session " + connectQos.getSessionName() + " with secret session ID: " + e.toString());
e.printStackTrace();
throw XmlBlasterException.convert(glob, ME, ErrorCode.INTERNAL_CONNECTIONFAILURE.toString(), e);
}
// [2] Try reconnecting with publicSessionId
if (connectQos.hasPublicSessionId()) {
SessionInfo info = getSessionInfo(connectQos.getSessionName());
if (info != null && !info.isShutdown() && !info.getConnectQos().bypassCredentialCheck()) {
if (connectQos.getSessionQos().reconnectSameClientOnly()) {
String text = "Only the creator of session " + connectQos.getSessionName().toString() + " may reconnect, access denied.";
log.warning(text);
throw new XmlBlasterException(glob, ErrorCode.USER_CONFIGURATION_IDENTICALCLIENT,
ME+".connect()", text);
}
try {
// Check password as we can't trust the public session ID
connectQos = info.getSecuritySession().init(connectQos, null);
boolean ok = info.getSecuritySession().verify(connectQos.getSecurityQos());
if (!ok)
throw new XmlBlasterException(glob, ErrorCode.USER_SECURITY_AUTHENTICATION_ACCESSDENIED,
ME, "Access denied for " + connectQos.getSecurityQos().getUserId() + " " + connectQos.getClientPluginType());
String oldSecretSessionId = info.getSecretSessionId();
if (secretSessionId == null || secretSessionId.length() < 2) {
// Keep the old secretSessionId
connectQos.getSessionQos().setSecretSessionId(oldSecretSessionId);
}
else {
// The CORBA driver insists in a new secretSessionId
changeSecretSessionId(oldSecretSessionId, secretSessionId);
connectQos.getSessionQos().setSecretSessionId(secretSessionId);
}
updateConnectQos(info, connectQos); // fires event
ConnectReturnQosServer returnQos = new ConnectReturnQosServer(glob, info.getConnectQos().getData());
returnQos.getSessionQos().setSessionName(info.getSessionName());
returnQos.setReconnected(true);
returnQos.getData().addClientProperty(Constants.CLIENTPROPERTY_RCVTIMESTAMPSTR, IsoDateParser.getCurrentUTCTimestampNanos());
log.info("Reconnected with given publicSessionId to '" + info.getSessionName() + "'.");
return returnQos;
}
catch (XmlBlasterException e) {
log.warning("Access is denied when trying to reconnect to session " + info.getSessionName() + ": " + e.getMessage());
throw e; // Thrown if authentication failed
}
catch (Throwable e) {
log.severe("Internal error when trying to reconnect to session " + info.getSessionName() + " with public session ID: " + e.toString());
e.printStackTrace();
throw XmlBlasterException.convert(glob, ME, ErrorCode.INTERNAL_CONNECTIONFAILURE.toString(), e);
}
}
}
// [3] Generate a secret session ID
if (secretSessionId == null || secretSessionId.length() < 2) {
secretSessionId = createSessionId("null" /*subjectCtx.getName()*/);
connectQos.getSessionQos().setSecretSessionId(secretSessionId); // assure consistency
if (log.isLoggable(Level.FINE)) log.fine("Empty secretSessionId - generated secretSessionId=" + secretSessionId);
}
I_Session sessionCtx = null;
I_Manager securityMgr = null;
SessionInfo sessionInfo = null;
// [4] Authenticate new client with password
try {
// Get suitable SecurityManager and context ...
securityMgr = plgnLdr.getManager(connectQos.getClientPluginType(), connectQos.getClientPluginVersion());
if (securityMgr == null) {
log.warning("Access is denied, there is no security manager configured for this connect QoS: " + connectQos.toXml());
throw new XmlBlasterException(glob, ErrorCode.USER_SECURITY_AUTHENTICATION_ACCESSDENIED, ME, "There is no security manager configured with the given connect QoS");
}
sessionCtx = securityMgr.reserveSession(secretSessionId); // always creates a new I_Session instance
connectQos = sessionCtx.init(connectQos, null);
if (connectQos.bypassCredentialCheck()) {
// This happens when a session is auto created by a PtP message
// Only ConnectQosServer (which is under control of the core) can set this flag
if (log.isLoggable(Level.FINE)) log.fine("SECURITY SWITCH OFF: Granted access to xmlBlaster without password, bypassCredentialCheck=true");
}
else {
String securityInfo = sessionCtx.init(connectQos.getSecurityQos()); // throws XmlBlasterExceptions if authentication fails
if (securityInfo != null && securityInfo.length() > 1) log.warning("Ignoring security info: " + securityInfo);
}
// Now the client is authenticated
}
catch (XmlBlasterException e) {
// If access is denied: cleanup resources
log.warning("Access is denied: " + e.getMessage() + ": " + connectQos.toString());
if (securityMgr != null) securityMgr.releaseSession(secretSessionId, null); // allways creates a new I_Session instance
throw e;
}
catch (Throwable e) {
log.severe("PANIC: Access is denied: " + e.getMessage() + "\n" + ServerScope.getStackTraceAsString(e));
e.printStackTrace();
// On error: cleanup resources
securityMgr.releaseSession(secretSessionId, null); // allways creates a new I_Session instance
throw XmlBlasterException.convert(glob, ME, ErrorCode.INTERNAL_CONNECTIONFAILURE.toString(), e);
}
if (log.isLoggable(Level.FINE)) log.fine("Checking if user is known ...");
SubjectInfo subjectInfo = null;
try {
/*
// Check if user is known, otherwise create an entry ...
I_Subject subjectCtx = sessionCtx.getSubject();
SessionName subjectName = new SessionName(glob, connectQos.getSessionName(), 0L); // Force to be of type subject (no public session ID)
boolean subjectIsAlive = false;
synchronized(this.loginNameSubjectInfoMap) { // Protect against two simultaneous logins
subjectInfo = (SubjectInfo)this.loginNameSubjectInfoMap.get(subjectName.getLoginName());
//log.error(ME, "DEBUG ONLY, subjectName=" + subjectName.toString() + " loginName=" + subjectName.getLoginName() + " state=" + toXml());
if (subjectInfo == null) {
subjectInfo = new SubjectInfo(getGlobal(), this, subjectName);
this.loginNameSubjectInfoMap.put(subjectInfo.getLoginName(), subjectInfo); // Protect against two simultaneous logins
}
subjectIsAlive = subjectInfo.isAlive();
} // synchronized(this.loginNameSubjectInfoMap)
if (!subjectInfo.isAlive()) {
try {
subjectInfo.toAlive(subjectCtx, connectQos.getSubjectQueueProperty());
}
catch(Throwable e) {
synchronized(this.loginNameSubjectInfoMap) {
this.loginNameSubjectInfoMap.remove(subjectInfo.getLoginName());
}
throw e;
}
}
*/
// [5] New client is authenticated, create the SessioInfo
boolean returnLocked = true;
subjectInfo = getOrCreateSubjectInfoByName(connectQos.getSessionName(),
returnLocked, sessionCtx.getSubject(), connectQos.getSubjectQueueProperty());
try { // subjectInfo.getLock().release())
if (subjectInfo.isAlive()) {
if (connectQos.getData().hasSubjectQueueProperty())
subjectInfo.setSubjectQueueProperty(connectQos.getSubjectQueueProperty()); // overwrites only if not null
}
// Check if client does a relogin and wants to destroy old sessions
if (connectQos.clearSessions() == true) {
SessionInfo[] sessions = subjectInfo.getSessionsToClear(connectQos);
if (sessions.length > 0) {
for (int i=0; i<sessions.length; i++ ) {
SessionInfo si = sessions[i];
log.warning("Destroying session '" + si.getSecretSessionId() + "' of user '" + subjectInfo.getSubjectName() + "' as requested by client");
disconnect(si.getSecretSessionId(), (String)null);
}
// will create a new SubjectInfo instance (which should be OK)
return connect(connectQos, secretSessionId);
}
}
if (log.isLoggable(Level.FINE)) log.fine("Creating sessionInfo for " + subjectInfo.getId());
// A PtP with forceQueuing=true and a simultaneous connect of the same
// client: This code is thread safe with new SessionInfo() below
// to avoid duplicate SessionInfo
sessionInfo = getOrCreateSessionInfo(connectQos.getSessionName(), connectQos);
if (sessionInfo.isInitialized() &&
!sessionInfo.isShutdown() && sessionInfo.getConnectQos().bypassCredentialCheck()) {
if (log.isLoggable(Level.FINE)) log.fine("connect: Reused session with had bypassCredentialCheck=true");
String oldSecretSessionId = sessionInfo.getSecretSessionId();
sessionInfo.setSecuritySession(sessionCtx);
if (secretSessionId == null || secretSessionId.length() < 2) {
// Keep the old secretSessionId
connectQos.getSessionQos().setSecretSessionId(oldSecretSessionId);
}
else {
// The CORBA driver insists in a new secretSessionId
changeSecretSessionId(oldSecretSessionId, secretSessionId);
connectQos.getSessionQos().setSecretSessionId(secretSessionId);
}
updateConnectQos(sessionInfo, connectQos);
}
else {
// Create the new sessionInfo instance
if (log.isLoggable(Level.FINE)) log.fine("connect: sessionId='" + secretSessionId + "' connectQos='" + connectQos.toXml() + "'");
sessionInfo.init(subjectInfo, sessionCtx, connectQos);
synchronized(this.sessionInfoMap) {
this.sessionInfoMap.put(secretSessionId, sessionInfo);
}
}
connectQos.getSessionQos().setSecretSessionId(secretSessionId);
connectQos.getSessionQos().setSessionName(sessionInfo.getSessionName());
subjectInfo.notifyAboutLogin(sessionInfo);
fireClientEvent(sessionInfo, true);
}
finally {
if (subjectInfo != null) subjectInfo.getLock().release();
}
// --- compose an answer -----------------------------------------------
ConnectReturnQosServer returnQos = new ConnectReturnQosServer(glob, connectQos.getData());
returnQos.getSessionQos().setSecretSessionId(secretSessionId); // securityInfo is not coded yet !
returnQos.getSessionQos().setSessionName(sessionInfo.getSessionName());
returnQos.getData().addClientProperty(Constants.CLIENTPROPERTY_RCVTIMESTAMPSTR, IsoDateParser.getCurrentUTCTimestampNanos());
// Now some nice logging ...
StringBuffer sb = new StringBuffer(256);
if (connectQos.bypassCredentialCheck())
sb.append("Created tempory session for client ");
else
sb.append("Successful login for client ");
sb.append(sessionInfo.getSessionName().getAbsoluteName());
sb.append(", session");
sb.append(((connectQos.getSessionTimeout() > 0L) ?
" expires after"+Timestamp.millisToNice(connectQos.getSessionTimeout()) :
" lasts forever"));
sb.append(", ").append(subjectInfo.getNumSessions()).append(" of ");
sb.append(connectQos.getMaxSessions()).append(" sessions are in use.");
log.info(sb.toString());
if (log.isLoggable(Level.FINEST)) log.finest(toXml());
if (log.isLoggable(Level.FINEST)) log.finest("Returned QoS:\n" + returnQos.toXml());
if (log.isLoggable(Level.FINER)) log.finer("Leaving connect()");
return returnQos;
}
catch (XmlBlasterException e) {
String id = (sessionInfo != null) ? sessionInfo.getId() : ((subjectInfo != null) ? subjectInfo.getId() : "");
log.warning("Connection for " + id + " failed: " + e.getMessage());
// e.g. by TestPersistentSession.java
// persistence/session/maxEntriesCache=1
// persistence/session/maxEntries=2
if (!e.getErrorCode().isOfType(ErrorCode.USER_SECURITY_AUTHENTICATION)) {
// E.g. if sessionStore overflow: we don't want the client polling
//e.changeErrorCode(ErrorCode.USER_SECURITY_AUTHENTICATION_ACCESSDENIED);
e = new XmlBlasterException(e.getGlobal(), ErrorCode.USER_SECURITY_AUTHENTICATION_ACCESSDENIED,
ME, "Access to xmlBlaster denied", e);
}
e.setCleanupSession(true);
// cleanup delayed to give our throw return a chance to reach client before the socket is closed
// Too dangerous: The stale SessionInfo could be reaccessed during the sleep
// There for we do the delay in CallbackSocketDriver ...
//disconnectDelayed(secretSessionId, (String)null, 5000, e); // cleanup
disconnect(secretSessionId, (String)null);
throw e;
}
catch (Throwable t) {
t.printStackTrace();
log.severe("Internal error: Connect failed: " + t.getMessage());
//disconnectDelayed(secretSessionId, (String)null, 10000, t); // cleanup
disconnect(secretSessionId, (String)null);
// E.g. if NPE: we don't want the client polling: Should we change to USER_SECURITY_AUTHENTICATION_ACCESSDENIED?
XmlBlasterException e = XmlBlasterException.convert(glob, ME, ErrorCode.INTERNAL_CONNECTIONFAILURE.toString(), t);
e.setCleanupSession(true);
throw e;
}
}