/*
* Copyright (c) 2005-2007, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. 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.wso2.carbon.identity.provider.xmpp;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jivesoftware.smack.Chat;
import org.jivesoftware.smack.ChatManager;
import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.Roster;
import org.jivesoftware.smack.RosterEntry;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.filter.AndFilter;
import org.jivesoftware.smack.filter.FromContainsFilter;
import org.jivesoftware.smack.filter.PacketFilter;
import org.jivesoftware.smack.filter.PacketTypeFilter;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Presence;
import org.wso2.carbon.identity.base.IdentityConstants;
import org.wso2.carbon.identity.core.model.XMPPSettingsDO;
import org.wso2.carbon.identity.core.util.IdentityUtil;
import java.util.Collection;
public class MPAuthenticationProvider {
private static final Log log = LogFactory.getLog(MPAuthenticationProvider.class);
private String userXmppId;
private String userPIN;
private boolean isPINEnabled;
private boolean pinDisabledResponse = true;
/**
* @param xmppSettingsDO
*/
public MPAuthenticationProvider(XMPPSettingsDO xmppSettingsDO) {
this.userXmppId = xmppSettingsDO.getXmppUserName().trim();
this.userPIN = xmppSettingsDO.getUserCode().trim();
this.isPINEnabled = xmppSettingsDO.isPINEnabled();
}
/**
* Authenticating a particular user
*
* @return true if authentication is successful, and false otherwise.
*/
public boolean authenticate() {
//creating the connection
XMPPConnection connection = createConnection();
//connecting to the server
boolean connectionStatus = connect(connection);
if (!connectionStatus) {
log.info("Failed to connect to the Server");
return false;
}
//signing in
boolean loginStatus = login(connection);
if (!loginStatus) {
log.info("login failed");
return false;
}
Roster roster = connection.getRoster();
Presence presence = roster.getPresence(userXmppId);
boolean isAvailable = false;
if (!presence.isAvailable()) {
isAvailable = checkforUserAvailability(roster, userXmppId);
}
if (!isAvailable) {
log.info("user is not online");
return false;
}
boolean validation = getUserResponse(connection, userXmppId);
// Giving a second chance to provide the PIN
if (!validation && !pinDisabledResponse) {
validation = getUserResponse(connection, userXmppId);
}
return validation;
}
/**
* Create a connection to the XMPP server with the available configuration details given in
* the identity.xml
*
* @return XMPPConnection
*/
private XMPPConnection createConnection() {
String xmppServer = IdentityUtil.getProperty(IdentityConstants.ServerConfig.XMPP_SETTINGS_SERVER);
int xmppPort = Integer.parseInt(IdentityUtil.getProperty(IdentityConstants.ServerConfig.XMPP_SETTINGS_PORT));
String xmppExt = IdentityUtil.getProperty(IdentityConstants.ServerConfig.XMPP_SETTINGS_EXT);
ConnectionConfiguration config = new ConnectionConfiguration(xmppServer, xmppPort, xmppExt);
config.setSASLAuthenticationEnabled(true);
return new XMPPConnection(config);
}
/**
* Checking whether the user is avaiable online.
*
* @param roster
* @param userName
* @return
*/
private boolean isAvailable(Roster roster, String userName) {
Collection<RosterEntry> entrySet = roster.getEntries();
for (RosterEntry entry : entrySet) {
if (entry.getUser().equals(userName)) {
return true;
}
}
return false;
}
/**
* Connecting to the server
*
* @param connection
* @return
*/
private boolean connect(XMPPConnection connection) {
for (int i = 0; i < 3; i++) {
try {
connection.connect();
return true;
} catch (XMPPException ex) {
log.error("Failed to create connection with the server", ex);
if (log.isInfoEnabled()) {
log.info("Attempting to connect to the server again..");
}
}
}
return false;
}
/**
* Login to the XMPP server with the credentials specifid in the Identity.xml
*
* @param connection
* @return
*/
public boolean login(XMPPConnection connection) {
String userName = IdentityUtil.getProperty(IdentityConstants.ServerConfig.XMPP_SETTINGS_USERNAME);
String password = IdentityUtil.getProperty(IdentityConstants.ServerConfig.XMPP_SETTINGS_PASSWORD);
for (int i = 0; i < 3; i++) {
try {
connection.login(userName, password, null);
return true;
} catch (XMPPException ex) {
log.error("login failed. Trying again..", ex);
}
}
return false;
}
/**
* Checking whether the user is available in the contact list.
*
* @param roster
* @param userXmppId
* @return
*/
public boolean checkforUserAvailability(Roster roster, String userXmppId) {
boolean isAvailable = false;
XmppSubscriptionListener listener = new XmppSubscriptionListener(userXmppId);
roster.addRosterListener(listener);
Presence presence = roster.getPresence(userXmppId);
boolean status = isAvailable(roster, userXmppId);
try {
if (!status) {
if (log.isInfoEnabled()) {
log.info("The User is not subscribed.");
}
boolean contactAddStatus = new XmppSubscriber().addContact(roster, userXmppId);
if (contactAddStatus) {
if (log.isInfoEnabled()) {
log.info("User's Contact is added successfully..");
}
} else {
if (log.isInfoEnabled()) {
log.info("Failed to add contact.");
}
}
if (!presence.isAvailable()) {
while (!listener.isOnline()) {
Thread.sleep(5000);
}
}
isAvailable = true;
} else {
if (log.isInfoEnabled()) {
log.info("User is not online..");
}
while (!listener.isOnline()) {
Thread.sleep(5000);
}
isAvailable = true;
}
} catch (InterruptedException e) {
log.error("Error while checking user availability", e);
} finally {
roster.removeRosterListener(listener);
}
return isAvailable;
}
/**
* Read the PIN number sent by the user as the reply.
*
* @param connection
* @param userName
* @return
*/
public boolean getUserResponse(XMPPConnection connection, String userName) {
String response = null;
Presence presence = connection.getRoster().getPresence(userName);
if (presence.isAvailable()) {
try {
ChatManager chatManager = connection.getChatManager();
Chat chat = chatManager.createChat(userName, null);
PacketFilter filter =
new AndFilter(new PacketTypeFilter(Message.class), new FromContainsFilter(userName));
XmppResponseListener chatListener = new XmppResponseListener();
connection.addPacketListener(chatListener, filter);
if (isPINEnabled) {
chat.sendMessage("Please reply with your PIN Number here.");
if (log.isInfoEnabled()) {
log.info("User PIN is sent to the user and awaiting for the response.");
}
while (!chatListener.isResponseReceived()) {
Thread.sleep(100);
}
response = chatListener.getResponse();
if (response != null) {
return userPIN.contentEquals(response.trim());
}
} else {
chat.sendMessage(
"You are about to get authenticated for your OpenID. Do you want to continue: [Yes] or [No]");
if (log.isInfoEnabled()) {
log.info("User PIN is sent to the user and awaiting for the response.");
}
while (!chatListener.isResponseReceived()) {
Thread.sleep(100);
}
response = chatListener.getResponse();
if (response != null) {
if ("YES".equalsIgnoreCase(response.trim())) {
return true;
} else if ("NO".equalsIgnoreCase(response.trim())) {
return false;
} else {
pinDisabledResponse = false;
return false;
}
}
}
} catch (Exception e) {
log.error("Error while getting user response", e);
}
} else {
return false;
}
return false;
}
}