Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
try to auto-login on startup
Browse files Browse the repository at this point in the history
daykin committed Nov 27, 2024
1 parent edf4749 commit 7753c37
Showing 12 changed files with 311 additions and 28 deletions.
6 changes: 6 additions & 0 deletions app/ux-analytics/monitor/pom.xml
Original file line number Diff line number Diff line change
@@ -88,6 +88,12 @@
<artifactId>s3</artifactId>
<version>2.25.66</version>
</dependency>
<dependency>
<groupId>org.mariadb.jdbc</groupId>
<artifactId>mariadb-java-client</artifactId>
<version>3.4.0</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.phoebus</groupId>
<artifactId>app-logbook-elog</artifactId>
Original file line number Diff line number Diff line change
@@ -6,10 +6,37 @@
import org.phoebus.applications.uxanalytics.monitor.backend.image.ImageClient;
import org.phoebus.applications.uxanalytics.monitor.util.ResourceOpenSources;
import org.phoebus.applications.uxanalytics.monitor.representation.ActiveTab;
import org.phoebus.security.store.SecureStore;
import org.phoebus.security.tokens.AuthenticationScope;
import org.phoebus.security.tokens.ScopedAuthenticationToken;

import java.util.logging.Logger;

@FunctionalInterface
public interface BackendConnection {
public Boolean connect(String hostOrRegion, Integer port, String usernameOrAccessKey, String passwordOrSecretKey);
public default boolean tryAutoConnect(AuthenticationScope scope){
//try to auto-connect with saved credentials
try{
SecureStore store = new SecureStore();
ScopedAuthenticationToken scopedAuthenticationToken = store.getScopedAuthenticationToken(scope);
if (scopedAuthenticationToken != null) {
String username = scopedAuthenticationToken.getUsername();
String password = scopedAuthenticationToken.getPassword();
return connect(null, null, username, password);
}
else{
//try anonymous login
if(!connect(null, null, "root", ""))
throw new Exception(this.getClass().toString());
return true;
}
}
catch(Exception e){
Logger.getAnonymousLogger().fine("Failed to auto-connect for UX Analytics backend connection: " + e.getMessage());
return false;
}
}
public default String getProtocol(){return "";}
public default String getHost(){return "localhost";}
public default String getPort(){return "";}
@@ -21,4 +48,6 @@ public default void handleClick(ActiveTab who, Widget widget, Integer x, Integer
public default void handleAction(ActiveTab who, Widget widget, ActionInfo info){}
public default void handlePVWrite(ActiveTab who, Widget widget, String PVName, String value){};
public default void handleDisplayOpen(DisplayInfo target, DisplayInfo src, ResourceOpenSources how){};


}
Original file line number Diff line number Diff line change
@@ -2,15 +2,10 @@

import com.mongodb.ConnectionString;
import com.mongodb.MongoClientSettings;
import com.mongodb.MongoCredential;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoDatabase;
import javafx.application.Platform;
import javafx.embed.swing.SwingFXUtils;
import javafx.scene.Node;
import javafx.scene.SnapshotParameters;
import javafx.scene.image.WritableImage;

import java.awt.image.BufferedImage;
import java.net.URI;
@@ -31,6 +26,9 @@
import org.phoebus.applications.uxanalytics.monitor.UXAMonitor;
import org.phoebus.applications.uxanalytics.monitor.representation.ActiveTab;
import org.phoebus.framework.preferences.PhoebusPreferenceService;
import org.phoebus.security.store.SecureStore;
import org.phoebus.security.tokens.AuthenticationScope;
import org.phoebus.security.tokens.ScopedAuthenticationToken;

public class MongoDBConnection implements BackendConnection {

@@ -58,7 +56,9 @@ public static MongoDBConnection getInstance(){
return instance;
}

private MongoDBConnection(){}
private MongoDBConnection(){ //try to auto-connect with saved credentials
tryAutoConnect(AuthenticationScope.MONGODB);
}

@Override
public Boolean connect(String hostname, Integer port, String username, String password) {
@@ -133,13 +133,6 @@ public Integer tearDown() {
return 0;
}

static BufferedImage getSnapshot(ActiveTab who) {
Node jfxNode = who.getParentTab().getContent();
SnapshotParameters params = new SnapshotParameters();
WritableImage snapshot = jfxNode.snapshot(params, null);
return SwingFXUtils.fromFXImage(snapshot, null);
}

@Override
public void handleClick(ActiveTab who, Integer x, Integer y) {
//if another image client hasn't been set up yet, default to a collection in the MongoDB database
@@ -157,7 +150,7 @@ public void handleClick(ActiveTab who, Integer x, Integer y) {
logger.log(Level.INFO, "Uploading image for " + who + " to " + path);
try {
((DisplayRuntimeInstance) who.getParentTab().getApplication()).getRepresentation_init().get(1, TimeUnit.SECONDS);
BufferedImage snapshot = getSnapshot(who);
BufferedImage snapshot = FileUtils.getSnapshot(who);
imageClient.uploadImage(URI.create(path), snapshot);
}
catch (Exception ex) {
Original file line number Diff line number Diff line change
@@ -12,6 +12,7 @@
import org.phoebus.applications.uxanalytics.monitor.util.ResourceOpenSources;
import org.phoebus.applications.uxanalytics.monitor.representation.ActiveTab;
import org.phoebus.framework.preferences.PhoebusPreferenceService;
import org.phoebus.security.tokens.AuthenticationScope;

import java.time.Instant;
import java.util.Map;
@@ -50,11 +51,17 @@ public static Neo4JConnection getInstance(){

private Session session;

private Neo4JConnection(){}
private Neo4JConnection(){
tryAutoConnect(AuthenticationScope.NEO4J);
}

@Override
public Boolean connect(String host, Integer port, String username, String password) {
try {
if(host == null)
host = getHost();
if(port == null)
port = Integer.parseInt(getPort());
driver = GraphDatabase.driver(PROTOCOL + host + ":" + port.toString(), AuthTokens.basic(username, password));
driver.verifyConnectivity();
logger.log(Level.INFO, "Connected to " + host + " on port " + port + " as " + username);
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
package org.phoebus.applications.uxanalytics.monitor.backend.database;

import javafx.application.Platform;
import org.phoebus.applications.uxanalytics.monitor.backend.image.FilesystemImageClient;
import org.phoebus.applications.uxanalytics.monitor.backend.image.ImageClient;
import org.phoebus.applications.uxanalytics.monitor.representation.ActiveTab;
import org.phoebus.applications.uxanalytics.monitor.util.FileUtils;
import org.phoebus.framework.preferences.PhoebusPreferenceService;
import org.phoebus.security.tokens.AuthenticationScope;

import java.net.URI;
import java.sql.*;
import java.util.logging.Level;
import java.util.logging.Logger;

public class SQLConnection implements BackendConnection{

Logger logger = Logger.getLogger(SQLConnection.class.getName());
Connection conn;
String userHost = null;
Integer userPort = null;
String userName = null;
String password = null;
String database = null;
String table = null;
private ImageClient imageClient;
private SQLConnection() {
tryAutoConnect(AuthenticationScope.MARIADB);
}
private static SQLConnection instance;
public static SQLConnection getInstance(){
if(instance == null){
instance = new SQLConnection();
}
return instance;
}

static class JDBCConnectionBuilder {
private String protocol = "jdbc:mysql://";
private String host = null;
private String port = null;
private String username = null;
private String password = null;
private String database = null;


public JDBCConnectionBuilder() {
}

public JDBCConnectionBuilder protocol(String protocol) {
this.protocol = protocol;
return this;
}

public JDBCConnectionBuilder host(String host) {
this.host = host;
return this;
}

public JDBCConnectionBuilder port(Integer port) {
if(port != null)
this.port = port.toString();
return this;
}

public JDBCConnectionBuilder username(String username) {
this.username = username;
return this;
}

public JDBCConnectionBuilder password(String password) {
this.password = password;
return this;
}

public JDBCConnectionBuilder database(String database) {
this.database = database;
return this;
}

public Connection build() {
String url = protocol;
if(host != null)
url += host;
else
url += PhoebusPreferenceService.userNodeForClass(SQLConnection.class).get("host", "localhost");
if(port != null)
url += ":" + port;
else
url += ":" + PhoebusPreferenceService.userNodeForClass(SQLConnection.class).get("port", "3306");
if(username == null)
username = PhoebusPreferenceService.userNodeForClass(SQLConnection.class).get("username", "root");
if(database != null)
url += "/" + database;
else
url += "/" + PhoebusPreferenceService.userNodeForClass(SQLConnection.class).get("database", "phoebus_analytics");
try{
Logger.getLogger(SQLConnection.class.getName()).info("Connecting to JDBC: " + url);
return DriverManager.getConnection(url, username, password);
} catch(SQLException e){
Logger.getLogger(JDBCConnectionBuilder.class.getName()).info("UX Analytics Failed to connect to JDBC: " + e.getMessage());
return null;
}
}
}

@Override
public Boolean connect(String hostOrRegion, Integer port, String usernameOrAccessKey, String passwordOrSecretKey) {
if(conn != null) {
try {
conn.close();
} catch (SQLException e) {
logger.log(Level.WARNING, "Failed to close existing JDBC connection: " + e.getMessage());
}
}
conn = null;
userHost = hostOrRegion;
userPort = port;
userName = usernameOrAccessKey;
database = PhoebusPreferenceService.userNodeForClass(SQLConnection.class).get("database", "phoebus_analytics");
conn = new JDBCConnectionBuilder()
.host(userHost)
.port(userPort)
.username(userName)
.password(passwordOrSecretKey)
.database(database)
.build();
return (conn != null);
}

@Override
public String getProtocol() {
return "jdbc:mysql://";
}

@Override
public String getHost() {
if(userHost != null)
return userHost;
return PhoebusPreferenceService.userNodeForClass(SQLConnection.class).get("host", "localhost");
}

@Override
public String getPort() {
if(userPort != null)
return userPort.toString();
return PhoebusPreferenceService.userNodeForClass(SQLConnection.class).get("port", "3306");
}

@Override
public String getDefaultUsername() {
return "root";
}

@Override
public Integer tearDown() {
try{
conn.close();
return 0;
} catch(SQLException e){
logger.warning("Failed to close JDBC connection: " + e.getMessage());
return -1;
}
}

@Override
public void setImageClient(ImageClient imageClient) {
this.imageClient = imageClient;
}

@Override
public void handleClick(ActiveTab who, Integer x, Integer y) {
if(conn!=null){
Platform.runLater(()->{
String path = FileUtils.analyticsPathForTab(who);
String tableName = path.substring(path.lastIndexOf('_') + 1);
if(imageClient==null){
logger.warning("No ImageClient set for SQLConnection, defaulting to filesystem");
imageClient = FilesystemImageClient.getInstance();
}
if(!imageClient.imageExists(URI.create(path))){
imageClient.uploadImage(URI.create(path), FileUtils.getSnapshot(who));

}

//create table if it doesn't exist
try{
Statement stmt = conn.createStatement();
stmt.execute("CREATE TABLE IF NOT EXISTS " + tableName + " (x INT, y INT, timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP)");
stmt.close();
} catch(SQLException e){
logger.warning("Failed to create table for " + tableName + ": " + e.getMessage());
}

//insert click into table
try{
PreparedStatement stmt = conn.prepareStatement("INSERT INTO " + tableName + " (x, y) VALUES (?, ?)");
stmt.setInt(1, x);
stmt.setInt(2, y);
stmt.execute();
stmt.close();
logger.fine("Inserted click into table " + tableName);
} catch(SQLException e){
logger.warning("Failed to insert click into table for " + tableName + ": " + e.getMessage());
}
});
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.phoebus.applications.uxanalytics.monitor.backend.database.authentication;

import org.phoebus.applications.uxanalytics.monitor.backend.database.SQLConnection;
import org.phoebus.security.authorization.ServiceAuthenticationProvider;
import org.phoebus.security.tokens.AuthenticationScope;

public class MariaDBAuthenticationProvider implements ServiceAuthenticationProvider {

@Override
public void authenticate(String username, String password) {
SQLConnection connection = SQLConnection.getInstance();
SQLConnection.getInstance().connect(connection.getHost(), Integer.parseInt(connection.getPort()), username, password);

}

@Override
public void logout(String token) {

}

@Override
public AuthenticationScope getAuthenticationScope() {
return AuthenticationScope.MARIADB;
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
package org.phoebus.applications.uxanalytics.monitor.backend.image;

import org.phoebus.framework.preferences.PhoebusPreferenceService;

import java.awt.image.BufferedImage;
import java.io.File;
import java.net.URI;
import java.util.logging.Logger;

public class FilesystemImageClient implements ImageClient {

static final Logger logger = Logger.getLogger(FilesystemImageClient.class.getName());

//Filesystem location to store images
private String imageLocation;
private String imageLocation = "./images";

public static FilesystemImageClient instance;
public static FilesystemImageClient getInstance(){
@@ -17,15 +22,17 @@ public static FilesystemImageClient getInstance(){
return instance;
}

private FilesystemImageClient(){}
private FilesystemImageClient(){
imageLocation = PhoebusPreferenceService.userNodeForClass(FilesystemImageClient.class).get("directory", "./images");
}

@Override
public Integer uploadImage(URI image, BufferedImage screenshot) {
try {
File outputfile = new File(imageLocation +"/"+ image.getPath()+".png");
//make directories if they don't exist
outputfile.getParentFile().mkdirs();
System.out.println("Saving image to: " + outputfile.getAbsolutePath());
logger.info("Saving image to: " + outputfile.getAbsolutePath());
javax.imageio.ImageIO.write(screenshot, "png", outputfile);
return 0;
} catch (Exception e) {
Original file line number Diff line number Diff line change
@@ -6,6 +6,7 @@
import java.util.logging.Level;

import org.phoebus.framework.preferences.PhoebusPreferenceService;
import org.phoebus.security.tokens.AuthenticationScope;
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.core.sync.RequestBody;
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
package org.phoebus.applications.uxanalytics.monitor.util;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.security.MessageDigest;

import javafx.embed.swing.SwingFXUtils;
import javafx.scene.Node;
import javafx.scene.SnapshotParameters;
import javafx.scene.image.WritableImage;
import org.csstudio.display.builder.model.util.ModelResourceUtil;
import org.phoebus.applications.uxanalytics.monitor.representation.ActiveTab;
import org.phoebus.framework.preferences.PhoebusPreferenceService;
@@ -152,4 +157,10 @@ public static String analyticsPathForTab(ActiveTab tab){
return getAnalyticsPathFor(path);
}

public static BufferedImage getSnapshot(ActiveTab who) {
Node jfxNode = who.getParentTab().getContent();
SnapshotParameters params = new SnapshotParameters();
WritableImage snapshot = jfxNode.snapshot(params, null);
return SwingFXUtils.fromFXImage(snapshot, null);
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
org.phoebus.applications.uxanalytics.monitor.backend.image.authentication.S3AuthenticationProvider
org.phoebus.applications.uxanalytics.monitor.backend.database.authentication.MongoDBAuthenticationProvider
org.phoebus.applications.uxanalytics.monitor.backend.database.authentication.Neo4JAuthenticationProvider
org.phoebus.applications.uxanalytics.monitor.backend.database.authentication.MariaDBAuthenticationProvider
Original file line number Diff line number Diff line change
@@ -1,23 +1,16 @@
package org.phoebus.applications.uxanalytics.ui;

import java.io.IOException;
import java.net.URI;
import java.util.logging.Level;
import java.util.logging.Logger;

import javafx.scene.layout.VBox;
import org.phoebus.applications.uxanalytics.monitor.*;
import org.phoebus.applications.uxanalytics.monitor.backend.database.BackendConnection;
import org.phoebus.applications.uxanalytics.monitor.backend.database.MongoDBConnection;
import org.phoebus.applications.uxanalytics.monitor.backend.database.SQLConnection;
import org.phoebus.applications.uxanalytics.monitor.backend.database.Neo4JConnection;
import org.phoebus.framework.spi.AppInstance;
import org.phoebus.framework.spi.AppResourceDescriptor;

import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

/**
* @author Evan Daykin
*/
@@ -27,7 +20,7 @@ public class UXAnalyticsMain implements AppResourceDescriptor {
public static final String NAME = "uxanalyticsconfig";
public static final String DISPLAY_NAME = "UX Analytics Config";
private BackendConnection phoebusConnection = Neo4JConnection.getInstance();
private BackendConnection jfxConnection = MongoDBConnection.getInstance();
private BackendConnection jfxConnection = SQLConnection.getInstance();
private final UXAMonitor monitor = UXAMonitor.getInstance();
@Override
public AppInstance create(URI resource) {
Original file line number Diff line number Diff line change
@@ -35,7 +35,8 @@ public enum AuthenticationScope {
SAVE_AND_RESTORE("save-and-restore"),
NEO4J("graph-database"),
S3("aws-image-bucket"),
MONGODB("mongodb-analytics-database");
MONGODB("mongodb-ux"),
MARIADB("mariadb-ux");



0 comments on commit 7753c37

Please sign in to comment.