Initial Commit

This commit is contained in:
Mark Milligan
2021-01-14 16:28:24 -06:00
parent 21c28201c5
commit 1334c110ff
318 changed files with 24160 additions and 0 deletions

View File

@@ -0,0 +1,23 @@
package com.lanternsoftware.currentmonitor.context;
import com.lanternsoftware.dataaccess.currentmonitor.CurrentMonitorDao;
import com.lanternsoftware.dataaccess.currentmonitor.MongoCurrentMonitorDao;
import com.lanternsoftware.util.LanternFiles;
import com.lanternsoftware.util.dao.mongo.MongoConfig;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
public class Globals implements ServletContextListener {
public static CurrentMonitorDao dao;
@Override
public void contextInitialized(ServletContextEvent sce) {
dao = new MongoCurrentMonitorDao(MongoConfig.fromDisk(LanternFiles.OPS_PATH + "mongo.cfg"));
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
dao.shutdown();
}
}

View File

@@ -0,0 +1,57 @@
package com.lanternsoftware.currentmonitor.servlet;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdTokenVerifier;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.lanternsoftware.currentmonitor.context.Globals;
import com.lanternsoftware.util.LanternFiles;
import com.lanternsoftware.util.NullUtils;
import com.lanternsoftware.util.ResourceLoader;
import com.lanternsoftware.util.dao.DaoEntity;
import com.lanternsoftware.util.dao.DaoSerializer;
import com.lanternsoftware.util.servlet.BasicAuth;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Collections;
@WebServlet("/auth/*")
public class AuthServlet extends CMServlet {
private static final NetHttpTransport transport = new NetHttpTransport();
private static final JacksonFactory jsonFactory = new JacksonFactory();
private static final Logger logger = LoggerFactory.getLogger(AuthServlet.class);
private static final String googleSsoKey = ResourceLoader.loadFileAsString(LanternFiles.OPS_PATH + "google_sso_key.txt");
@Override
protected void doGet(HttpServletRequest _req, HttpServletResponse _rep) {
String authCode = _req.getHeader("auth_code");
if (NullUtils.isEmpty(authCode)) {
BasicAuth auth = new BasicAuth(_req);
if (NullUtils.isEqual(auth.getUsername(), "googlesso")) {
GoogleIdTokenVerifier verifier = new GoogleIdTokenVerifier.Builder(transport, jsonFactory).setAudience(Collections.singletonList(googleSsoKey)).build();
try {
GoogleIdToken idToken = verifier.verify(auth.getPassword());
if (idToken != null) {
GoogleIdToken.Payload payload = idToken.getPayload();
String email = payload.getEmail();
authCode = Globals.dao.getAuthCodeForEmail(email);
}
}
catch (Exception _e) {
logger.error("Failed to validate google auth token", _e);
}
}
else
authCode = Globals.dao.authenticateAccount(auth.getUsername(), auth.getPassword());
}
DaoEntity rep = new DaoEntity("auth_code", authCode);
if (isPath(_req, 0, "bin"))
zipBsonResponse(_rep, rep);
else
jsonResponse(_rep, rep);
}
}

View File

@@ -0,0 +1,101 @@
package com.lanternsoftware.currentmonitor.servlet;
import com.lanternsoftware.util.CollectionUtils;
import com.lanternsoftware.util.NullUtils;
import com.lanternsoftware.util.dao.DaoEntity;
import com.lanternsoftware.util.dao.DaoSerializer;
import org.apache.commons.io.IOUtils;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.core.MediaType;
import java.io.InputStream;
import java.io.OutputStream;
public abstract class CMServlet extends HttpServlet {
public static void setResponseHtml(HttpServletResponse _response, String _sHtml) {
setResponseEntity(_response, MediaType.TEXT_HTML, _sHtml);
}
public static void setResponseEntity(HttpServletResponse _response, String _sContentType, String _sEntity) {
setResponseEntity(_response, 200, _sContentType, _sEntity);
}
public static void setResponseEntity(HttpServletResponse _response, String _sContentType, byte[] _btData) {
setResponseEntity(_response, 200, _sContentType, _btData);
}
public static void setResponseEntity(HttpServletResponse _response, int _iStatus, String _sContentType, String _sEntity) {
setResponseEntity(_response, _iStatus, _sContentType, NullUtils.toByteArray(_sEntity));
}
public static void setResponseEntity(HttpServletResponse _response, int _iStatus, String _sContentType, byte[] _btData) {
OutputStream os = null;
try {
_response.setStatus(_iStatus);
_response.setCharacterEncoding("UTF-8");
_response.setContentType(_sContentType);
if ((_btData != null) && (_btData.length > 0)) {
_response.setContentLength(_btData.length);
os = _response.getOutputStream();
os.write(_btData);
} else
_response.setContentLength(0);
} catch (Exception e) {
e.printStackTrace();
} finally {
IOUtils.closeQuietly(os);
}
}
protected void zipBsonResponse(HttpServletResponse _response, Object _object)
{
setResponseEntity(_response, 200, MediaType.APPLICATION_OCTET_STREAM, DaoSerializer.toZipBson(_object));
}
protected void jsonResponse(HttpServletResponse _response, Object _object)
{
setResponseEntity(_response, 200, MediaType.APPLICATION_JSON, DaoSerializer.toJson(_object));
}
protected void jsonResponse(HttpServletResponse _response, String _json)
{
setResponseEntity(_response, 200, MediaType.APPLICATION_JSON, _json);
}
protected String getRequestPayloadAsString(HttpServletRequest _req) {
return NullUtils.toString(getRequestPayload(_req));
}
protected byte[] getRequestPayload(HttpServletRequest _req) {
InputStream is = null;
try {
is = _req.getInputStream();
return IOUtils.toByteArray(is);
}
catch (Exception e) {
e.printStackTrace();
return null;
}
finally {
IOUtils.closeQuietly(is);
}
}
protected DaoEntity getRequestZipBson(HttpServletRequest _req) {
return DaoSerializer.fromZipBson(getRequestPayload(_req));
}
protected <T> T getRequestPayload(HttpServletRequest _req, Class<T> _retClass) {
return DaoSerializer.fromZipBson(getRequestPayload(_req), _retClass);
}
protected String[] path(HttpServletRequest _req) {
return NullUtils.cleanSplit(NullUtils.makeNotNull(_req.getPathInfo()), "/");
}
protected boolean isPath(HttpServletRequest _req, int _index, String _path) {
return NullUtils.isEqual(_path, CollectionUtils.get(path(_req), _index));
}
}

View File

@@ -0,0 +1,49 @@
package com.lanternsoftware.currentmonitor.servlet;
import com.lanternsoftware.datamodel.currentmonitor.AuthCode;
import com.lanternsoftware.util.CollectionUtils;
import com.lanternsoftware.util.LanternFiles;
import com.lanternsoftware.util.NullUtils;
import com.lanternsoftware.util.ResourceLoader;
import com.lanternsoftware.util.dao.DaoEntity;
import com.lanternsoftware.util.dao.DaoSerializer;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
@WebServlet("/command")
public class CommandServlet extends SecureServlet {
@Override
protected void get(AuthCode _authCode, HttpServletRequest _req, HttpServletResponse _rep) {
File folder = new File(LanternFiles.OPS_PATH + _authCode.getAccountId());
List<String> commands = new ArrayList<>();
if (folder.exists() && folder.isDirectory()) {
for (File command : CollectionUtils.asArrayList(folder.listFiles())) {
if (command.isDirectory())
continue;
String c = command.getName();
String extension = NullUtils.after(c, ".");
if (NullUtils.isNotEmpty(extension))
c = c.replace("." + extension, "");
commands.add(c);
}
}
zipBsonResponse(_rep, new DaoEntity("commands", commands));
}
@Override
protected void post(AuthCode _authCode, HttpServletRequest _req, HttpServletResponse _rep) {
DaoEntity payload = getRequestZipBson(_req);
if (payload == null)
return;
String command = DaoSerializer.getString(payload, "command");
String path = LanternFiles.OPS_PATH + _authCode.getAccountId() + File.separator + "payload" + File.separator;
new File(path).mkdirs();
ResourceLoader.writeFile(path+ command + ".txt", DaoSerializer.getString(payload, "payload"));
}
}

View File

@@ -0,0 +1,34 @@
package com.lanternsoftware.currentmonitor.servlet;
import com.lanternsoftware.currentmonitor.context.Globals;
import com.lanternsoftware.datamodel.currentmonitor.AuthCode;
import com.lanternsoftware.datamodel.currentmonitor.BreakerConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/config/*")
public class ConfigServlet extends SecureServlet {
@Override
protected void get(AuthCode _authCode, HttpServletRequest _req, HttpServletResponse _rep) {
if (isPath(_req, 0, "bin"))
zipBsonResponse(_rep, Globals.dao.getMergedConfig(_authCode));
else
jsonResponse(_rep, Globals.dao.getMergedConfig(_authCode));
}
@Override
protected void post(AuthCode _authCode, HttpServletRequest _req, HttpServletResponse _rep) {
BreakerConfig config = getRequestPayload(_req, BreakerConfig.class);
if (config == null) {
_rep.setStatus(400);
return;
}
if (config.getAccountId() != _authCode.getAccountId()) {
_rep.setStatus(401);
return;
}
Globals.dao.putConfig(config);
}
}

View File

@@ -0,0 +1,48 @@
package com.lanternsoftware.currentmonitor.servlet;
import com.lanternsoftware.currentmonitor.context.Globals;
import com.lanternsoftware.datamodel.currentmonitor.AuthCode;
import com.lanternsoftware.datamodel.currentmonitor.BreakerGroupEnergy;
import com.lanternsoftware.datamodel.currentmonitor.EnergyBlockViewMode;
import com.lanternsoftware.util.CollectionUtils;
import com.lanternsoftware.util.NullUtils;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
import java.util.List;
@WebServlet("/energy/group/*")
public class GroupEnergyServlet extends SecureServlet {
@Override
protected void get(AuthCode _authCode, HttpServletRequest _req, HttpServletResponse _rep) {
String[] path = path(_req);
if (path.length < 3) {
_rep.setStatus(400);
return;
}
EnergyBlockViewMode viewMode = NullUtils.toEnum(EnergyBlockViewMode.class, path[1], EnergyBlockViewMode.DAY);
Date start = new Date(NullUtils.toLong(path[2]));
List<BreakerGroupEnergy> energies = CollectionUtils.transform(_authCode.getAllAccountIds(), _id->Globals.dao.getBreakerGroupEnergy(_id, path[0], viewMode, start), true);
if (CollectionUtils.isNotEmpty(energies)) {
BreakerGroupEnergy energy;
if (energies.size() > 1) {
energy = new BreakerGroupEnergy();
energy.setAccountId(_authCode.getAccountId());
energy.setGroupId("Sites");
energy.setGroupName("Sites");
energy.setStart(start);
energy.setViewMode(viewMode);
energy.setSubGroups(CollectionUtils.asArrayList(energies));
}
else
energy = CollectionUtils.getFirst(energies);
if (NullUtils.isEqual(CollectionUtils.get(path, 3), "bin"))
zipBsonResponse(_rep, energy);
else
jsonResponse(_rep, energy);
} else
_rep.setStatus(404);
}
}

View File

@@ -0,0 +1,26 @@
package com.lanternsoftware.currentmonitor.servlet;
import com.lanternsoftware.currentmonitor.context.Globals;
import com.lanternsoftware.datamodel.currentmonitor.AuthCode;
import com.lanternsoftware.datamodel.currentmonitor.Breaker;
import com.lanternsoftware.datamodel.currentmonitor.BreakerPower;
import com.lanternsoftware.datamodel.currentmonitor.BreakerGroup;
import com.lanternsoftware.util.CollectionUtils;
import com.lanternsoftware.util.NullUtils;
import com.lanternsoftware.util.dao.DaoEntity;
import com.lanternsoftware.util.dao.DaoSerializer;
import org.bson.Document;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/power/group/*")
public class GroupPowerServlet extends SecureServlet {
@Override
protected void get(AuthCode _authCode, HttpServletRequest _req, HttpServletResponse _rep) {
String[] path = path(_req);
if (path.length < 1)
zipBsonResponse(_rep, new DaoEntity("breakers", DaoSerializer.toDaoEntities(Globals.dao.getBreakerPowerForAccount(_authCode.getAccountId()))));
}
}

View File

@@ -0,0 +1,49 @@
package com.lanternsoftware.currentmonitor.servlet;
import com.lanternsoftware.currentmonitor.context.Globals;
import com.lanternsoftware.datamodel.currentmonitor.AuthCode;
import com.lanternsoftware.datamodel.currentmonitor.BreakerPower;
import com.lanternsoftware.datamodel.currentmonitor.HubPowerMinute;
import com.lanternsoftware.util.CollectionUtils;
import com.lanternsoftware.util.NullUtils;
import com.lanternsoftware.util.dao.DaoSerializer;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
@WebServlet("/power/*")
public class PowerServlet extends SecureServlet {
@Override
protected void get(AuthCode _authCode, HttpServletRequest _req, HttpServletResponse _rep) {
String[] path = path(_req);
if (path.length < 2) {
_rep.setStatus(400);
return;
}
int hub = DaoSerializer.toInteger(CollectionUtils.get(path, 0));
int port = DaoSerializer.toInteger(CollectionUtils.get(path, 1));
jsonResponse(_rep, Globals.dao.getLatestBreakerPower(_authCode.getAccountId(), hub, port));
}
@Override
protected void post(AuthCode _authCode, HttpServletRequest _req, HttpServletResponse _rep) {
String[] path = path(_req);
if ((path.length > 0) && NullUtils.isEqual(CollectionUtils.get(path, 0), "hub")) {
Globals.dao.putHubPowerMinute(getRequestPayload(_req, HubPowerMinute.class));
return;
}
if ((path.length > 0) && NullUtils.isEqual(CollectionUtils.get(path, 0), "batch")) {
List<BreakerPower> powers = DaoSerializer.getList(getRequestZipBson(_req), "readings", BreakerPower.class);
if (!powers.isEmpty()) {
CollectionUtils.edit(powers, _p->_p.setAccountId(_authCode.getAccountId()));
Globals.dao.getProxy().save(powers);
}
return;
}
BreakerPower power = getRequestPayload(_req, BreakerPower.class);
power.setAccountId(_authCode.getAccountId());
Globals.dao.putBreakerPower(power);
}
}

View File

@@ -0,0 +1,35 @@
package com.lanternsoftware.currentmonitor.servlet;
import com.lanternsoftware.currentmonitor.context.Globals;
import com.lanternsoftware.datamodel.currentmonitor.AuthCode;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public abstract class SecureServlet extends CMServlet {
@Override
protected void doGet(HttpServletRequest _req, HttpServletResponse _rep) {
AuthCode authCode = Globals.dao.decryptAuthCode(_req.getHeader("auth_code"));
if (authCode == null) {
_rep.setStatus(401);
return;
}
get(authCode, _req, _rep);
}
protected void get(AuthCode _authCode, HttpServletRequest _req, HttpServletResponse _rep) {
}
@Override
protected void doPost(HttpServletRequest _req, HttpServletResponse _rep) {
AuthCode authCode = Globals.dao.decryptAuthCode(_req.getHeader("auth_code"));
if (authCode == null) {
_rep.setStatus(401);
return;
}
post(authCode, _req, _rep);
}
protected void post(AuthCode _authCode, HttpServletRequest _req, HttpServletResponse _rep) {
}
}

View File

@@ -0,0 +1,45 @@
package com.lanternsoftware.currentmonitor.servlet;
import com.lanternsoftware.currentmonitor.context.Globals;
import com.lanternsoftware.datamodel.currentmonitor.Account;
import com.lanternsoftware.datamodel.currentmonitor.SignupResponse;
import com.lanternsoftware.util.NullUtils;
import com.lanternsoftware.util.dao.DaoEntity;
import com.lanternsoftware.util.dao.DaoSerializer;
import com.lanternsoftware.util.email.EmailValidator;
import com.lanternsoftware.util.servlet.BasicAuth;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/signup")
public class SignupServlet extends CMServlet {
@Override
protected void doGet(HttpServletRequest _req, HttpServletResponse _rep) {
BasicAuth auth = new BasicAuth(_req);
Account acct = Globals.dao.getAccountByUsername(auth.getUsername());
if (acct != null) {
jsonResponse(_rep, SignupResponse.error("An account for " + auth.getUsername() + " already exists"));
return;
}
if (!EmailValidator.getInstance().isValid(auth.getUsername())) {
jsonResponse(_rep, SignupResponse.error(auth.getUsername() + " is not a valid email address"));
return;
}
if (NullUtils.length(auth.getPassword()) < 8) {
jsonResponse(_rep, SignupResponse.error("Your password must be at least 8 characters long"));
return;
}
if (NullUtils.isEqual("password", auth.getPassword())) {
jsonResponse(_rep, SignupResponse.error("Seriously? \"password\"? Come on."));
return;
}
acct = new Account();
acct.setUsername(auth.getUsername());
acct.setPassword(auth.getPassword());
Globals.dao.putAccount(acct);
String authCode = Globals.dao.authenticateAccount(auth.getUsername(), auth.getPassword());
jsonResponse(_rep, SignupResponse.success(authCode));
}
}

View File

@@ -0,0 +1,22 @@
package com.lanternsoftware.currentmonitor.servlet;
import com.lanternsoftware.util.LanternFiles;
import com.lanternsoftware.util.ResourceLoader;
import com.lanternsoftware.util.dao.DaoSerializer;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.core.MediaType;
import java.io.File;
@WebServlet("/update/*")
public class UpdateServlet extends CMServlet {
@Override
protected void doGet(HttpServletRequest _req, HttpServletResponse _rep) {
if (isPath(_req, 0, "version"))
setResponseEntity(_rep, MediaType.APPLICATION_OCTET_STREAM, DaoSerializer.toZipBson(DaoSerializer.parse(ResourceLoader.loadFileAsString(LanternFiles.OPS_PATH + "release" + File.separator + "version.json"))));
else
setResponseEntity(_rep, MediaType.APPLICATION_OCTET_STREAM, ResourceLoader.loadFile(LanternFiles.OPS_PATH + "release" + File.separator + "lantern-currentmonitor.jar"));
}
}

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<property name="log.pattern" value="%date %-5level %logger{0} - %message%n"/>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
</appender>
<logger name="com.lanternsoftware" level="DEBUG"/>
<root level="OFF">
<appender-ref ref="STDOUT"/>
</root>
</configuration>

View File

@@ -0,0 +1,9 @@
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<listener>
<listener-class>com.lanternsoftware.currentmonitor.context.Globals</listener-class>
</listener>
</web-app>