Skip to content
Snippets Groups Projects
Commit cd352ad3 authored by William Enright's avatar William Enright
Browse files

ported logic for checking for available updates, handling licensing

parent e5a5664a
No related branches found
No related tags found
No related merge requests found
Showing
with 341 additions and 5 deletions
......@@ -27,6 +27,11 @@
<artifactId>xbean-classloader</artifactId>
<version>${xbean.version}</version>
</dependency>
<dependency>
<groupId>org.zeromq</groupId>
<artifactId>jeromq</artifactId>
<version>0.5.2</version>
</dependency>
</dependencies>
</project>
\ No newline at end of file
......@@ -56,5 +56,6 @@ module jams.common {
requires java.base;
requires java.sql;
requires org.apache.xbean.classloader;
requires jeromq;
}
package net.jami.jams.common.utils;
import lombok.extern.slf4j.Slf4j;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
@Slf4j
public class LicenseUtils {
public static String checkVersion(String destinationDir, String jarPath) throws IOException {
String resp = "";
File file = new File(jarPath);
JarFile jar = new JarFile(file);
// first get all directories,
// then make those directory on the destination Path
for (Enumeration<JarEntry> enums = jar.entries(); enums.hasMoreElements(); ) {
JarEntry entry = (JarEntry) enums.nextElement();
String fileName = destinationDir + File.separator + entry.getName();
File f = new File(fileName);
if (fileName.endsWith("/")) {
f.mkdirs();
}
}
//now create all files
for (Enumeration<JarEntry> enums = jar.entries(); enums.hasMoreElements(); ) {
JarEntry entry = (JarEntry) enums.nextElement();
if (entry.toString().contains("META-INF/MANIFEST.MF")) {
String fileName = destinationDir + File.separator + entry.getName();
File f = new File(fileName);
if (!fileName.endsWith("/")) {
InputStream is = jar.getInputStream(entry);
FileOutputStream fos = new FileOutputStream(f);
// write contents of 'is' to 'fos'
while (is.available() > 0) {
fos.write(is.read());
}
fos.close();
is.close();
}
}
}
String manifestPath = destinationDir + "/META-INF/MANIFEST.MF";
Manifest manifest = new Manifest(new URL("file:///" + manifestPath).openStream());
resp = manifest.getMainAttributes().getValue("Implementation-Version");
return resp;
}
}
package net.jami.jams.common.utils;
import lombok.Getter;
import lombok.Setter;
import org.zeromq.SocketType;
import org.zeromq.ZMQ;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger;
@Getter
@Setter
public class UpdateInterface extends Thread {
private AtomicBoolean updateAvailable = new AtomicBoolean(false);
private volatile String versions;
private ZMQ.Context context = ZMQ.context(1);
private ZMQ.Socket sender = context.socket(SocketType.REQ);
private ZMQ.Socket receiver = context.socket(SocketType.SUB);
private final static Logger logger = Logger.getLogger(UpdateInterface.class.getName());
public UpdateInterface() {
receiver.connect("tcp://localhost:4572");
sender.connect("tcp://localhost:4573");
receiver.subscribe("UPDATE");
this.start();
}
public void approveUpdate(){
sender.send("DO-UPDATE");
}
@Override
public void run() {
while(true){
try{
receiver.recv();
updateAvailable.set(true);
versions = receiver.recvStr();
}
catch (Exception e){
System.out.println(e.toString());
}
}
}
}
......@@ -36,11 +36,6 @@
<artifactId>guava</artifactId>
<version>20.0</version>
</dependency>
<dependency>
<groupId>org.zeromq</groupId>
<artifactId>jeromq</artifactId>
<version>0.5.2</version>
</dependency>
<dependency>
<groupId>com.blockfeed</groupId>
<artifactId>messaging</artifactId>
......
......@@ -66,6 +66,11 @@
<artifactId>asm</artifactId>
<version>${asm.version}</version>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20190722</version>
</dependency>
</dependencies>
<build>
......
......@@ -14,6 +14,10 @@ module jams.server {
requires jami.dht;
requires nimbus.jose.jwt;
requires java.desktop;
requires org.json;
requires java.naming;
requires java.logging;
requires javax.servlet.api;
exports net.jami.jams.server.servlets.general to org.apache.tomcat.embed.core;
exports net.jami.jams.server.servlets.filters to org.apache.tomcat.embed.core;
exports net.jami.jams.server.servlets.api.auth.login to org.apache.tomcat.embed.core;
......
......@@ -11,6 +11,7 @@ import net.jami.jams.common.jami.NameServer;
import net.jami.jams.common.serialization.JsoniterRegistry;
import net.jami.jams.common.server.ServerSettings;
import net.jami.jams.common.utils.LibraryLoader;
import net.jami.jams.common.utils.UpdateInterface;
import net.jami.jams.nameserver.LocalNameServer;
import net.jami.jams.nameserver.PublicNameServer;
import net.jami.jams.server.core.TomcatLauncher;
......@@ -26,6 +27,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
public class Server {
public static AtomicBoolean isInstalled = new AtomicBoolean(false);
private static volatile boolean activated = false;
static {
JsoniterRegistry.initCodecs();
......@@ -37,6 +39,7 @@ public class Server {
public static AuthenticationModule userAuthenticationModule;
public static NameServer nameServer;
private static TomcatLauncher tomcatLauncher = null;
public static final UpdateInterface updateInterface = new UpdateInterface();
public static void main(String[] args) {
//Start tomcat.
......@@ -89,4 +92,12 @@ public class Server {
log.info("Started server with empty modules waiting for setup...");
}
}
public static boolean isActivated() {
return activated;
}
public static void setActivated(boolean isActivated) {
Server.activated = isActivated;
}
}
package net.jami.jams.server.licensing;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import net.jami.jams.server.Server;
import org.json.JSONObject;
import javax.naming.ldap.LdapName;
import javax.naming.ldap.Rdn;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Base64;
import java.util.concurrent.atomic.AtomicBoolean;
@Getter
@Setter
@Slf4j
public class LicenseService {
private AtomicBoolean activationStatus = new AtomicBoolean(false);
private String licenseType = "COMMUNITY";
//Load the license.
public void loadLicense() {
try {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
//Assuming the file exists, we just read the file.
String b64License = new String(Files.readAllBytes(Path.of(System.getProperty("user.dir") + File.separator + "license.dat")));
//Since this is base64, we need to decode it.
String strLicense = new String(Base64.getDecoder().decode(b64License));
//Now we need to split it. This is actually easy.
int cutPoint = strLicense.indexOf("-----BEGIN PRIVATE KEY-----");
String publicKey = strLicense.substring(0,cutPoint);
InputStream inputStream = new ByteArrayInputStream(publicKey.getBytes(StandardCharsets.UTF_8));
Certificate c = cf.generateCertificate(inputStream);
String dn = ((X509Certificate)c).getSubjectDN().toString();
LdapName ln = new LdapName(dn);
byte[] array = null;
for(Rdn rdn : ln.getRdns()) {
try {
array = Base64.getDecoder().decode(((String)rdn.getValue()).getBytes());
} catch (IllegalArgumentException e) {
}
}
//This is kept inside the resources/oem folder, this is a lot less violent than what we had before.
//TODO: You should re-use the same technique to validate whatever they stick in the textbox.
InputStream input = LicenseService.class.getClassLoader().getResourceAsStream("oem/ca.crt");
if (input == null) {
System.out.println("No CA Found... this is critical!");
System.exit(-1);
}
Certificate ca = cf.generateCertificate(input);
try{
((X509Certificate) c).checkValidity();
c.verify(ca.getPublicKey());
Server.setActivated(true);
if (array != null) {
JSONObject jsonObject = new JSONObject(new String(array));
licenseType = (String) jsonObject.get("type");
}
}
catch (Exception e){
Server.setActivated(false);
licenseType = "COMMUNITY";
log.warn("Your license is no longer valid or has been tampered with - " + e.toString());
}
}
catch (Exception e){
Server.setActivated(false);
licenseType = "COMMUNITY";
//logger.warning("An exception occurred while checking your license: " + e.toString());
}
}
public String getLicenseType() {
loadLicense();
return licenseType;
}
}
package net.jami.jams.server.servlets.api.update;
import com.jsoniter.output.JsonStream;
import net.jami.jams.ca.JamsCA;
import net.jami.jams.common.cryptoengineapi.CertificateAuthority;
import net.jami.jams.common.utils.LicenseUtils;
import net.jami.jams.server.Server;
import net.jami.jams.server.licensing.LicenseService;
import net.jami.jams.server.startup.CryptoEngineLoader;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
@WebServlet("/api/auth/license")
public class LicenseServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
resp.setHeader("Access-Control-Allow-Origin", JamsCA.serverDomain);
resp.setContentType("application/json");
try {
resp.setStatus(200);
HashMap<String,Object> payload = new HashMap<>();
payload.put("isActive", Server.isActivated());
payload.put("licenseType", new LicenseService().getLicenseType());
payload.put("currentVersion", LicenseUtils.checkVersion(System.getProperty("user.dir") + "/tmpjar/", System.getProperty("user.dir") + "/jams-server.jar"));
resp.getOutputStream().write(JsonStream.serialize(payload).getBytes());
}
catch (Exception e){
resp.setStatus(403);
}
}
}
\ No newline at end of file
package net.jami.jams.server.servlets.api.update;
import com.jsoniter.output.JsonStream;
import net.jami.jams.ca.JamsCA;
import net.jami.jams.server.Server;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.logging.Logger;
@WebServlet("/api/checkupdate")
public class NeedsUpdateServlet extends HttpServlet {
private final static Logger logger = Logger.getLogger(NeedsUpdateServlet.class.getName());
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
resp.setHeader("Access-Control-Allow-Origin", JamsCA.serverDomain);
resp.setContentType("application/json");
try {
HashMap<String,Object> payload = new HashMap<>();
if(!Server.updateInterface.getVersions().equals("{}")) {
payload.put("updateAvailable", Server.updateInterface.getUpdateAvailable().get());
payload.put("newVersions", Server.updateInterface.getVersions());
}
else
payload.put("updateAvailable",false);
resp.getOutputStream().write(JsonStream.serialize(payload).getBytes());
resp.setStatus(200);
}
catch (Exception e){
resp.setStatus(500);
}
}
@Override
protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
}
package net.jami.jams.server.servlets.api.update;
import net.jami.jams.ca.JamsCA;
import net.jami.jams.server.Server;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.logging.Logger;
@WebServlet("/api/startupdate")
public class StartUpdateServlet extends HttpServlet {
private final static Logger logger = Logger.getLogger(StartUpdateServlet.class.getName());
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
resp.setHeader("Access-Control-Allow-Origin", JamsCA.serverDomain);
resp.setContentType("application/json");
try {
Server.updateInterface.approveUpdate();
resp.setStatus(200);
}
catch (Exception e){
resp.setStatus(500);
}
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment