|
|
@ -1,16 +1,22 @@
|
|
|
|
package com.example.sshd.service;
|
|
|
|
package com.example.sshd.service;
|
|
|
|
|
|
|
|
|
|
|
|
import org.apache.commons.lang3.StringUtils;
|
|
|
|
import org.apache.commons.lang3.StringUtils;
|
|
|
|
|
|
|
|
import org.dom4j.Element;
|
|
|
|
|
|
|
|
import org.dom4j.Namespace;
|
|
|
|
import org.jivesoftware.whack.ExternalComponentManager;
|
|
|
|
import org.jivesoftware.whack.ExternalComponentManager;
|
|
|
|
import org.slf4j.Logger;
|
|
|
|
import org.slf4j.Logger;
|
|
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
|
|
|
|
|
import org.springframework.beans.factory.annotation.Qualifier;
|
|
|
|
import org.springframework.stereotype.Component;
|
|
|
|
import org.springframework.stereotype.Component;
|
|
|
|
import org.xmpp.component.AbstractComponent;
|
|
|
|
import org.xmpp.component.AbstractComponent;
|
|
|
|
import org.xmpp.component.ComponentException;
|
|
|
|
import org.xmpp.component.ComponentException;
|
|
|
|
|
|
|
|
import org.xmpp.packet.IQ;
|
|
|
|
import org.xmpp.packet.Message;
|
|
|
|
import org.xmpp.packet.Message;
|
|
|
|
|
|
|
|
import org.xmpp.packet.IQ.Type;
|
|
|
|
|
|
|
|
|
|
|
|
import com.example.sshd.config.XmppComponentConfig;
|
|
|
|
import com.example.sshd.config.XmppComponentConfig;
|
|
|
|
|
|
|
|
import com.github.benmanes.caffeine.cache.Cache;
|
|
|
|
|
|
|
|
|
|
|
|
import jakarta.annotation.PostConstruct;
|
|
|
|
import jakarta.annotation.PostConstruct;
|
|
|
|
|
|
|
|
|
|
|
@ -18,10 +24,17 @@ import jakarta.annotation.PostConstruct;
|
|
|
|
public class EchoComponent extends AbstractComponent {
|
|
|
|
public class EchoComponent extends AbstractComponent {
|
|
|
|
|
|
|
|
|
|
|
|
private static final Logger logger = LoggerFactory.getLogger(EchoComponent.class);
|
|
|
|
private static final Logger logger = LoggerFactory.getLogger(EchoComponent.class);
|
|
|
|
|
|
|
|
public static final String CONST_OPERATION_ADD_USER = "adduser";
|
|
|
|
|
|
|
|
public static final String CONST_OPERATION_CHANGE_USER_PASSWORD = "chgpasswd";
|
|
|
|
|
|
|
|
public static final String CONST_OPERATION_DELETE_USER = "deluser";
|
|
|
|
|
|
|
|
|
|
|
|
@Autowired
|
|
|
|
@Autowired
|
|
|
|
XmppComponentConfig xmppComponentConfig;
|
|
|
|
XmppComponentConfig xmppComponentConfig;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Autowired
|
|
|
|
|
|
|
|
@Qualifier("userAdminCache")
|
|
|
|
|
|
|
|
private volatile Cache<String, Message> userAdminCache;
|
|
|
|
|
|
|
|
|
|
|
|
ExternalComponentManager externalComponentManager = null;
|
|
|
|
ExternalComponentManager externalComponentManager = null;
|
|
|
|
|
|
|
|
|
|
|
|
@PostConstruct
|
|
|
|
@PostConstruct
|
|
|
@ -35,6 +48,13 @@ public class EchoComponent extends AbstractComponent {
|
|
|
|
xmppComponentConfig.getSecretKey());
|
|
|
|
xmppComponentConfig.getSecretKey());
|
|
|
|
externalComponentManager.addComponent(xmppComponentConfig.getSubdomainPrefix(), this,
|
|
|
|
externalComponentManager.addComponent(xmppComponentConfig.getSubdomainPrefix(), this,
|
|
|
|
xmppComponentConfig.getPort());
|
|
|
|
xmppComponentConfig.getPort());
|
|
|
|
|
|
|
|
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
externalComponentManager.removeComponent(xmppComponentConfig.getSubdomainPrefix());
|
|
|
|
|
|
|
|
} catch (ComponentException e) {
|
|
|
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
@Override
|
|
|
@ -47,20 +67,222 @@ public class EchoComponent extends AbstractComponent {
|
|
|
|
return this.getClass().getName();
|
|
|
|
return this.getClass().getName();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void doEcho(final Message inMsg, String body) {
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
Message outMsg = new Message();
|
|
|
|
|
|
|
|
outMsg.setType(inMsg.getType());
|
|
|
|
|
|
|
|
outMsg.setFrom(inMsg.getTo());
|
|
|
|
|
|
|
|
if (StringUtils.endsWith(inMsg.getSubject(), "@" + xmppComponentConfig.getDomain())) {
|
|
|
|
|
|
|
|
outMsg.setTo(inMsg.getSubject());
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
outMsg.setTo(inMsg.getFrom());
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
outMsg.setSubject(inMsg.getSubject());
|
|
|
|
|
|
|
|
outMsg.setBody(body == null ? inMsg.getBody() : body);
|
|
|
|
|
|
|
|
externalComponentManager.sendPacket(this, outMsg);
|
|
|
|
|
|
|
|
logger.info("[doEcho] -- SENT -- {}", outMsg);
|
|
|
|
|
|
|
|
} catch (Exception err) {
|
|
|
|
|
|
|
|
logger.error("[doEcho] ", err);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
protected void handleMessage(final Message inMsg) {
|
|
|
|
protected void handleMessage(final Message inMsg) {
|
|
|
|
logger.info("-- RECEIVED -- {}", inMsg);
|
|
|
|
logger.info("[handleMessage] -- RECEIVED -- {}", inMsg);
|
|
|
|
Message outMsg = new Message();
|
|
|
|
try {
|
|
|
|
outMsg.setType(inMsg.getType());
|
|
|
|
if (StringUtils.isNotBlank(inMsg.getBody())) {
|
|
|
|
outMsg.setFrom(inMsg.getTo());
|
|
|
|
String[] commandParts = StringUtils.split(inMsg.getBody(), ' ');
|
|
|
|
if (StringUtils.endsWith(inMsg.getSubject(), "@" + xmppComponentConfig.getDomain())) {
|
|
|
|
switch (commandParts[0]) {
|
|
|
|
outMsg.setTo(inMsg.getSubject());
|
|
|
|
case CONST_OPERATION_ADD_USER:
|
|
|
|
} else {
|
|
|
|
if (commandParts.length == 3)
|
|
|
|
outMsg.setTo(inMsg.getFrom());
|
|
|
|
requestAddUserForm(inMsg);
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
doEcho(inMsg, "adduser <username> <password>");
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CONST_OPERATION_DELETE_USER:
|
|
|
|
|
|
|
|
if (commandParts.length == 2)
|
|
|
|
|
|
|
|
requestDeleteUserForm(inMsg);
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
doEcho(inMsg, "deluser <username>");
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CONST_OPERATION_CHANGE_USER_PASSWORD:
|
|
|
|
|
|
|
|
if (commandParts.length == 3)
|
|
|
|
|
|
|
|
requestChangeUserPassword(inMsg);
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
doEcho(inMsg, "chgpasswd <username> <new_password>");
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
|
|
doEcho(inMsg, null);
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} catch (Exception err) {
|
|
|
|
|
|
|
|
logger.error("[handleMessage] ", err);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
|
|
|
protected void handleIQResult(IQ iq) {
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
logger.debug("[handleIQResult] {} has received iq-result: {}", getName(), iq);
|
|
|
|
|
|
|
|
if (iq.getChildElement() != null) {
|
|
|
|
|
|
|
|
logger.debug("[{}] {}'s child-namespace: {}", iq.getID(), getName(),
|
|
|
|
|
|
|
|
iq.getChildElement().getNamespace());
|
|
|
|
|
|
|
|
logger.debug("[{}] {}'s child-name: {}", iq.getID(), getName(), iq.getChildElement().getName());
|
|
|
|
|
|
|
|
if (iq.getChildElement().getNamespace().equals(Namespace.get("http://jabber.org/protocol/commands"))
|
|
|
|
|
|
|
|
&& iq.getChildElement().getName().equals("command")) {
|
|
|
|
|
|
|
|
Message inMsg = userAdminCache.getIfPresent(iq.getID());
|
|
|
|
|
|
|
|
handleCommands(iq.getID(), inMsg, iq.getChildElement());
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} catch (Exception err) {
|
|
|
|
|
|
|
|
logger.error("[handleIQResult] ", err);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
protected void handleCommands(String id, Message inMsg, Element command) throws InterruptedException {
|
|
|
|
|
|
|
|
String status = command.attributeValue("status");
|
|
|
|
|
|
|
|
String node = command.attributeValue("node");
|
|
|
|
|
|
|
|
String sessionid = command.attributeValue("sessionid");
|
|
|
|
|
|
|
|
logger.debug("[{}] sessionid: {}, status: {}, node: {}", id, sessionid, status, node);
|
|
|
|
|
|
|
|
if (status.equals("executing")) {
|
|
|
|
|
|
|
|
if (node.equals("http://jabber.org/protocol/admin#add-user")) {
|
|
|
|
|
|
|
|
sendAddUserForm(sessionid, inMsg);
|
|
|
|
|
|
|
|
} else if (node.equals("http://jabber.org/protocol/admin#delete-user")) {
|
|
|
|
|
|
|
|
sendDeleteUserForm(sessionid, inMsg);
|
|
|
|
|
|
|
|
} else if (node.equals("http://jabber.org/protocol/admin#change-user-password")) {
|
|
|
|
|
|
|
|
sendChangeUserPasswordForm(sessionid, inMsg);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} else if (status.equals("completed")) {
|
|
|
|
|
|
|
|
doEcho(inMsg, "OK");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public void requestAddUserForm(Message inMsg) {
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
IQ addUserIq = new IQ(Type.set);
|
|
|
|
|
|
|
|
addUserIq.setFrom(xmppComponentConfig.getSubdomainPrefix() + "." + xmppComponentConfig.getDomain());
|
|
|
|
|
|
|
|
addUserIq.setTo(xmppComponentConfig.getDomain());
|
|
|
|
|
|
|
|
Element child = addUserIq.setChildElement("command", "http://jabber.org/protocol/commands");
|
|
|
|
|
|
|
|
child.addAttribute("action", "execute");
|
|
|
|
|
|
|
|
child.addAttribute("node", "http://jabber.org/protocol/admin#add-user");
|
|
|
|
|
|
|
|
userAdminCache.put(addUserIq.getID(), inMsg);
|
|
|
|
|
|
|
|
externalComponentManager.sendPacket(this, addUserIq);
|
|
|
|
|
|
|
|
logger.info("[requestAddUserForm] -- SENT -- {}", addUserIq);
|
|
|
|
|
|
|
|
} catch (Exception err) {
|
|
|
|
|
|
|
|
logger.error("[requestAddUserForm] ", err);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void createFormTypeElement(Element x, String var, String type, String value) {
|
|
|
|
|
|
|
|
Element formType = x.addElement("field");
|
|
|
|
|
|
|
|
formType.addAttribute("var", var);
|
|
|
|
|
|
|
|
if (type != null) {
|
|
|
|
|
|
|
|
formType.addAttribute("type", type);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
formType.addElement("value").setText(value);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public void sendAddUserForm(String sessionId, Message inMsg) {
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
String[] commandParts = StringUtils.split(inMsg.getBody(), ' ');
|
|
|
|
|
|
|
|
IQ addUserIq = new IQ(Type.set);
|
|
|
|
|
|
|
|
addUserIq.setFrom(xmppComponentConfig.getSubdomainPrefix() + "." + xmppComponentConfig.getDomain());
|
|
|
|
|
|
|
|
addUserIq.setTo(xmppComponentConfig.getDomain());
|
|
|
|
|
|
|
|
Element child = addUserIq.setChildElement("command", "http://jabber.org/protocol/commands");
|
|
|
|
|
|
|
|
child.addAttribute("node", "http://jabber.org/protocol/admin#add-user");
|
|
|
|
|
|
|
|
child.addAttribute("sessionid", sessionId);
|
|
|
|
|
|
|
|
Element x = child.addElement("x", "jabber:x:data");
|
|
|
|
|
|
|
|
x.addAttribute("type", "submit");
|
|
|
|
|
|
|
|
createFormTypeElement(x, "FORM_TYPE", "hidden", "http://jabber.org/protocol/admin");
|
|
|
|
|
|
|
|
createFormTypeElement(x, "accountjid", "jid-single",
|
|
|
|
|
|
|
|
commandParts[1] + "@" + xmppComponentConfig.getDomain());
|
|
|
|
|
|
|
|
createFormTypeElement(x, "password", "text-private", commandParts[2]);
|
|
|
|
|
|
|
|
createFormTypeElement(x, "password-verify", "text-private", commandParts[2]);
|
|
|
|
|
|
|
|
userAdminCache.put(addUserIq.getID(), inMsg);
|
|
|
|
|
|
|
|
externalComponentManager.sendPacket(this, addUserIq);
|
|
|
|
|
|
|
|
logger.info("[sendAddUserForm] -- SENT -- {}", addUserIq);
|
|
|
|
|
|
|
|
} catch (Exception err) {
|
|
|
|
|
|
|
|
logger.error("[sendAddUserForm] ", err);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public void requestDeleteUserForm(Message inMsg) {
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
IQ deleteUserIq = new IQ(Type.set);
|
|
|
|
|
|
|
|
deleteUserIq.setFrom(xmppComponentConfig.getSubdomainPrefix() + "." + xmppComponentConfig.getDomain());
|
|
|
|
|
|
|
|
deleteUserIq.setTo(xmppComponentConfig.getDomain());
|
|
|
|
|
|
|
|
Element child = deleteUserIq.setChildElement("command", "http://jabber.org/protocol/commands");
|
|
|
|
|
|
|
|
child.addAttribute("action", "execute");
|
|
|
|
|
|
|
|
child.addAttribute("node", "http://jabber.org/protocol/admin#delete-user");
|
|
|
|
|
|
|
|
userAdminCache.put(deleteUserIq.getID(), inMsg);
|
|
|
|
|
|
|
|
externalComponentManager.sendPacket(this, deleteUserIq);
|
|
|
|
|
|
|
|
logger.info("[requestDeleteUserForm] -- SENT -- {}", deleteUserIq);
|
|
|
|
|
|
|
|
} catch (Exception err) {
|
|
|
|
|
|
|
|
logger.error("[requestDeleteUserForm] ", err);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public void sendDeleteUserForm(String sessionId, Message inMsg) {
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
String[] commandParts = StringUtils.split(inMsg.getBody(), ' ');
|
|
|
|
|
|
|
|
IQ deleteUserIq = new IQ(Type.set);
|
|
|
|
|
|
|
|
deleteUserIq.setFrom(xmppComponentConfig.getSubdomainPrefix() + "." + xmppComponentConfig.getDomain());
|
|
|
|
|
|
|
|
deleteUserIq.setTo(xmppComponentConfig.getDomain());
|
|
|
|
|
|
|
|
Element child = deleteUserIq.setChildElement("command", "http://jabber.org/protocol/commands");
|
|
|
|
|
|
|
|
child.addAttribute("node", "http://jabber.org/protocol/admin#delete-user");
|
|
|
|
|
|
|
|
child.addAttribute("sessionid", sessionId);
|
|
|
|
|
|
|
|
Element x = child.addElement("x", "jabber:x:data");
|
|
|
|
|
|
|
|
x.addAttribute("type", "submit");
|
|
|
|
|
|
|
|
createFormTypeElement(x, "FORM_TYPE", "hidden", "http://jabber.org/protocol/admin");
|
|
|
|
|
|
|
|
createFormTypeElement(x, "accountjids", "jid-single",
|
|
|
|
|
|
|
|
commandParts[1] + "@" + xmppComponentConfig.getDomain());
|
|
|
|
|
|
|
|
userAdminCache.put(deleteUserIq.getID(), inMsg);
|
|
|
|
|
|
|
|
externalComponentManager.sendPacket(this, deleteUserIq);
|
|
|
|
|
|
|
|
logger.info("[sendDeleteUserForm] -- SENT -- {}", deleteUserIq);
|
|
|
|
|
|
|
|
} catch (Exception err) {
|
|
|
|
|
|
|
|
logger.error("[sendDeleteUserForm] ", err);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public void requestChangeUserPassword(Message inMsg) {
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
IQ changeUserPasswordIq = new IQ(Type.set);
|
|
|
|
|
|
|
|
changeUserPasswordIq
|
|
|
|
|
|
|
|
.setFrom(xmppComponentConfig.getSubdomainPrefix() + "." + xmppComponentConfig.getDomain());
|
|
|
|
|
|
|
|
changeUserPasswordIq.setTo(xmppComponentConfig.getDomain());
|
|
|
|
|
|
|
|
Element child = changeUserPasswordIq.setChildElement("command", "http://jabber.org/protocol/commands");
|
|
|
|
|
|
|
|
child.addAttribute("action", "execute");
|
|
|
|
|
|
|
|
child.addAttribute("node", "http://jabber.org/protocol/admin#change-user-password");
|
|
|
|
|
|
|
|
userAdminCache.put(changeUserPasswordIq.getID(), inMsg);
|
|
|
|
|
|
|
|
externalComponentManager.sendPacket(this, changeUserPasswordIq);
|
|
|
|
|
|
|
|
logger.info("[requestChangeUserPassword] -- SENT -- {}", changeUserPasswordIq);
|
|
|
|
|
|
|
|
} catch (Exception err) {
|
|
|
|
|
|
|
|
logger.error("[requestChangeUserPassword] ", err);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public void sendChangeUserPasswordForm(String sessionId, Message inMsg) {
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
String[] commandParts = StringUtils.split(inMsg.getBody(), ' ');
|
|
|
|
|
|
|
|
IQ changeUserPasswordIq = new IQ(Type.set);
|
|
|
|
|
|
|
|
changeUserPasswordIq
|
|
|
|
|
|
|
|
.setFrom(xmppComponentConfig.getSubdomainPrefix() + "." + xmppComponentConfig.getDomain());
|
|
|
|
|
|
|
|
changeUserPasswordIq.setTo(xmppComponentConfig.getDomain());
|
|
|
|
|
|
|
|
Element child = changeUserPasswordIq.setChildElement("command", "http://jabber.org/protocol/commands");
|
|
|
|
|
|
|
|
child.addAttribute("node", "http://jabber.org/protocol/admin#change-user-password");
|
|
|
|
|
|
|
|
child.addAttribute("sessionid", sessionId);
|
|
|
|
|
|
|
|
Element x = child.addElement("x", "jabber:x:data");
|
|
|
|
|
|
|
|
x.addAttribute("type", "submit");
|
|
|
|
|
|
|
|
createFormTypeElement(x, "FORM_TYPE", "hidden", "http://jabber.org/protocol/admin");
|
|
|
|
|
|
|
|
createFormTypeElement(x, "accountjid", "jid-single",
|
|
|
|
|
|
|
|
commandParts[1] + "@" + xmppComponentConfig.getDomain());
|
|
|
|
|
|
|
|
createFormTypeElement(x, "password", "text-private", commandParts[2]);
|
|
|
|
|
|
|
|
userAdminCache.put(changeUserPasswordIq.getID(), inMsg);
|
|
|
|
|
|
|
|
externalComponentManager.sendPacket(this, changeUserPasswordIq);
|
|
|
|
|
|
|
|
logger.info("[sendChangeUserPasswordForm] -- SENT -- {}", changeUserPasswordIq);
|
|
|
|
|
|
|
|
} catch (Exception err) {
|
|
|
|
|
|
|
|
logger.error("[sendChangeUserPasswordForm] ", err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
outMsg.setSubject(inMsg.getSubject());
|
|
|
|
|
|
|
|
outMsg.setBody(inMsg.getBody());
|
|
|
|
|
|
|
|
externalComponentManager.sendPacket(this, outMsg);
|
|
|
|
|
|
|
|
logger.info("-- SENT -- {}", outMsg);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|