Initial commit

This commit is contained in:
Isaac Parenteau committed 2018-07-07 20:43:51 -05:00
commit 880e39de2e
511 files changed
+38363

No files matched your search

@@ -0,0 +1,50 @@
package net.locusworks.portal.config;
import org.quartz.spi.JobFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import net.locusworks.portal.util.ApplicationContextHolder;
/**
* Configuration class to allow quartz scheduler access to the spring beans
* Without this configuration Quartz jobs would not be able to use
* auto wired methods as they would be null
* @author Isaac Parenteau
* @since 1.0.0-RELEASE
* @version 1.0
*/
@Configuration
public class PortalQuartzSchedulerConfig {
@Autowired
private ApplicationContext applicationContext;
/**
* Create the job factory bean
* @return Job factory bean
*/
@Bean
public JobFactory jobFactory() {
ApplicationContextHolder jobFactory = new ApplicationContextHolder();
jobFactory.setApplicationContext(applicationContext);
return jobFactory;
}
/**
* Create the Scheduler Factory bean
* @return scheduler factory object
*/
@Bean
public SchedulerFactoryBean schedulerFactory() {
SchedulerFactoryBean factory = new SchedulerFactoryBean();
factory.setAutoStartup(true);
factory.setSchedulerName("NGAS Portal scheduler");
factory.setOverwriteExistingJobs(true);
factory.setJobFactory(jobFactory());
return factory;
}
}
@@ -0,0 +1,63 @@
package net.locusworks.portal.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter;
import net.locusworks.portal.handlers.PortalAuthenticationFailureHandler;
import net.locusworks.portal.handlers.PortalAuthenticationSuccessHandler;
import net.locusworks.portal.handlers.PortalLogoutSuccessHandler;
import net.locusworks.portal.providers.CustomAuthenticationProvider;
/***
* Configures spring web security
* @author Isaac Parenteau
* @since 1.0.0-RELEASE
* @version 1.0
*/
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
CustomAuthenticationProvider provider;
/***
* Sets up the allowed pages and how to handle successful and failed login attempts
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
// See https://jira.springsource.org/browse/SPR-11496
.headers().addHeaderWriter(new XFrameOptionsHeaderWriter(XFrameOptionsHeaderWriter.XFrameOptionsMode.SAMEORIGIN))
.and()
.formLogin()
.successHandler(new PortalAuthenticationSuccessHandler()) // handle successful login
.failureHandler(new PortalAuthenticationFailureHandler()) // handle failure login
// specify our own login page rather than the default, this is what spring will forward to if an un-authenticated request come in
.loginPage("/client/index.html")
.loginProcessingUrl("/springLogin") // this is the magic spring page that we post our login request (there is no real page hint hint)
.permitAll() // all users can access the login form
.and()
.logout()
.logoutSuccessHandler(new PortalLogoutSuccessHandler()) // calls this on logout success
.logoutUrl("/logout") // where the client should be redirected for logout, no file exists
.permitAll()
.and()
.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/ws/**").permitAll()//allow to subscribe to stomp topics
.antMatchers("/index.html").permitAll()
.antMatchers("/assets/**").permitAll()
.antMatchers("/account/isLoggedIn.do").permitAll()
.antMatchers("/account/login.do").permitAll()
.antMatchers("/client/**").permitAll()
.anyRequest().authenticated()// all other URLs require just authenticated access
.and()
.authenticationProvider(provider); // our custom authenticator
}
}
@@ -0,0 +1,88 @@
package net.locusworks.portal.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.ChannelRegistration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.security.config.annotation.web.messaging.MessageSecurityMetadataSourceRegistry;
import org.springframework.security.config.annotation.web.socket.AbstractSecurityWebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketTransportRegistration;
import net.locusworks.common.logger.ApplicationLogger;
import net.locusworks.common.logger.ApplicationLoggerFactory;
import net.locusworks.portal.common.enums.PermissionType;
import net.locusworks.portal.services.CommService;
import net.locusworks.portal.websocket.WebsocketOutboundChannelInterceptor;
/**
* Configure websocket use along with websocket security permissions
* @author Isaac Parenteau
* @since 1.0.0-RELEASE
* @version 1.0
*/
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {
private static final Integer BUFFER_LIMIT = 512 * 1024;
private static ApplicationLogger logger = ApplicationLoggerFactory.getLogger(WebSocketConfig.class);
//Socks library uses version 1.1.4. The current library included with spring websockets is 1.0.0. need to override it with our current one
public static final String SOCKS_1_1_4_LOCATION = "../../client/yarn_components/sockjs-client/dist/sockjs.min.js";
//Define our allowed authorties
private static String[] authorities = new String[] {
PermissionType.VIEW_SCRIPT_TAB.toString(), PermissionType.VIEW_CREDS_TAB.toString(),
PermissionType.VIEW_GIT_TAB.toString(), PermissionType.VIEW_PERMISSION_TAB.toString(),
PermissionType.VIEW_LOG_TAB.toString(), PermissionType.VIEW_USER_TAB.toString()
};
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker(CommService.STOMP_TOPIC_PREFIX, CommService.STOMP_QUEUE_PREFIX);
config.setApplicationDestinationPrefixes("/start", "/app");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
logger.info("Registering STOMP!");
registry.addEndpoint(CommService.STOMP_ENDPOINT)
.setAllowedOrigins("*")
.withSockJS()
.setClientLibraryUrl(SOCKS_1_1_4_LOCATION);
}
@Override
protected void configureInbound(MessageSecurityMetadataSourceRegistry message) {
message
.nullDestMatcher().permitAll()
.simpSubscribeDestMatchers("/user/queue/pong").permitAll()
.simpDestMatchers("/start/ping").permitAll()
.simpDestMatchers("/app/**").hasAnyAuthority(authorities)
.simpSubscribeDestMatchers(CommService.STOMP_TOPIC_PREFIX + "**").permitAll()
.anyMessage().denyAll();
}
@Override
protected boolean sameOriginDisabled() {
return true;
}
@Override
public void configureClientOutboundChannel(ChannelRegistration registration) {
registration.interceptors(new WebsocketOutboundChannelInterceptor());
}
@Override
public void configureWebSocketTransport(WebSocketTransportRegistration registration) {
// the default MessageSizeLimit is 16Kb, as evidenced by the stomp error:
// The 'content-length' header 333387 exceeds the configured message buffer size limit 65536
// setMessageSizeLimit can be used to increase it.
registration.setMessageSizeLimit(BUFFER_LIMIT);
registration.setSendBufferSizeLimit(BUFFER_LIMIT);
logger.info("Increasing websocket buffer limit to %d bytes", BUFFER_LIMIT);
}
}
@@ -0,0 +1,204 @@
package net.locusworks.portal.controllers;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import net.locusworks.common.logger.ApplicationLogger;
import net.locusworks.common.logger.ApplicationLoggerFactory;
import net.locusworks.common.utils.Success;
import net.locusworks.portal.common.enums.PermissionType;
import net.locusworks.portal.common.enums.TriggerType;
import net.locusworks.portal.common.exceptions.PortalException;
import net.locusworks.portal.json.ApplicationUserRequest;
import net.locusworks.portal.services.UserSecurityService;
/**
* Controller servlet to handle account activity
* @author Isaac Parenteau
* @since 1.0.0-RELEASE
* @version 1.0
*
*/
@Controller
@RequestMapping(value = "account")
public class AccountController extends BaseController {
private static ApplicationLogger logger = ApplicationLoggerFactory.getLogger(AccountController.class);
/**
* Add a user to the web application
* @param request User information to add/update
* @return the newly added user
* @throws Exception exception happened in the servlet
*/
@RequestMapping(value="addUser.do")
public @ResponseBody Success addUser(@RequestBody ApplicationUserRequest request) throws Exception {
if (!(isLoggedIn() && hasPermission(PermissionType.ADD_USERS))) {
throw PortalException.actionNotPermitted();
}
try {
ApplicationUserRequest aur = userSecurityService.saveApplicationUser(request);
if (aur.isNew()) {
appLogService.addUserAddedEntry(aur.getFormalName(), aur.getRole());
} else {
appLogService.addUserEditedEntry(aur.getFormalName());
}
sendAccountTrigger(aur, TriggerType.USER_ADDED);
return new Success(true, aur);
} catch(Exception ex) {
logger.error("Unable to add a user: %s", ex.getMessage());
if (!(ex instanceof PortalException))
throw ex;
throw (PortalException) ex;
}
}
/**
* Delete a user from the application
* @param request The user to delete
* @return True if the user was deleted successfully
* @throws Exception exception happened in the servlet
*/
@RequestMapping(value="deleteUser.do")
public @ResponseBody Success deleteUser(@RequestBody ApplicationUserRequest request) throws Exception {
if (!(isLoggedIn() && hasPermission(PermissionType.DELETE_USERS))) {
throw PortalException.actionNotPermitted();
}
try {
userSecurityService.deleteApplicationUser(request);
sendAccountTrigger(request, TriggerType.USER_DELETED);
appLogService.addUserDeletedEntry(request.getFormalName());
return Success.success();
} catch(Exception ex) {
logger.error("Unable to delete user: %s", ex.getMessage());
if (!(ex instanceof PortalException))
throw ex;
throw (PortalException) ex;
}
}
/**
* Get all user information
* @return Success status with the data
* @throws Exception exception happened in the servlet
*/
@RequestMapping(value="getAll.do")
public @ResponseBody Success getAllUsers() throws Exception {
if (!(isLoggedIn() && hasPermission(PermissionType.VIEW_USER_TAB))) {
throw PortalException.actionNotPermitted();
}
try {
List<ApplicationUserRequest> users = userSecurityService.getAllUsers();
return new Success(true, users);
} catch(Exception ex) {
logger.error("Unable to get all users: %s", ex.getMessage());
throw ex;
}
}
/**
* Checks to see if the user is already logged in and authenticated with the server
* This allows for refresh of the web page without bringing users back to the login screen
* @return success status with the login information
* @throws Exception exception happened in the servlet
*/
@RequestMapping(value="isLoggedIn.do")
public @ResponseBody Success isAuthenticated() throws Exception {
boolean isLoggedIn = isLoggedIn();
Success success = new Success(isLoggedIn, isLoggedIn? getAuthJsonData() : null);
logger.info("isLoggedIn.do? %s", isLoggedIn);
return success;
}
/**
* Log into the web application
* @param user user login information
* @param http the servlet response
* @param request servlet request
* @return success with the users information
* @throws Exception exception happened in the servlet
*/
@RequestMapping(value = "login.do")
public @ResponseBody Success login(@RequestBody Map<String, Object> user, HttpServletResponse http, HttpServletRequest request) throws Exception {
try {
String username = (String)user.get("username");
String password = (String)user.get("password");
if (StringUtils.isAnyBlank(username, password)) {
throw PortalException.unAuthorized();
}
ApplicationUserRequest aur = userSecurityService.getApplicationUser(username, password);
if (aur != null) {
userSecurityService.loginAuId(aur.getId());
UserSecurityService.saveSession(request.getSession().getId(), aur.getId());
}
return loginResponse();
} catch (Exception ex) {
logger.error(getClass().getName() + ".login(): " + ex.getMessage());
if (!(ex instanceof PortalException)) {
throw PortalException.unAuthorized();
}
throw (PortalException)ex;
}
}
/**
* Logout of the application and clear session
* @return Success with the corresponding information applicable to the request
* @throws Exception exception happened in the servlet
*/
@RequestMapping(value = "logout.do")
public @ResponseBody Success logout() throws Exception {
try {
ApplicationUserRequest aur = userSecurityService.getCurrentUser();
UserSecurityService.removeSessions(request.getSession().getId());
appLogService.endHttpRequest();
appLogService.addLogoutEntry(aur.getFormalName());
return Success.success();
} catch (Exception ex) {
logger.error(getClass().getName() + ".logout(): %s", ex.getMessage());
if (!(ex instanceof PortalException)) {
throw ex;
}
throw (PortalException)ex;
}
}
/**
* Sets up the login information for the user
* @return Success with the corresponding information applicable to the request
* @throws Exception exception happened in the servlet
*/
private Success loginResponse() throws Exception {
try {
String page = "dashboard";
if (isLoggedIn()) {
Map<String, Object> res = getAuthJsonData();
res.put("page", page);
appLogService.addLoginEntry();
return new Success(isLoggedIn(), res);
} else {
Map<String, Object> res = new HashMap<>();
res.put("message", "Invalid Username or Password");
return new Success(false, res);
}
} catch (Exception e) {
throw e;
}
}
}
@@ -0,0 +1,69 @@
package net.locusworks.portal.controllers;
import java.util.List;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import net.locusworks.common.logger.ApplicationLogger;
import net.locusworks.common.logger.ApplicationLoggerFactory;
import net.locusworks.common.utils.Success;
import net.locusworks.portal.common.enums.PermissionType;
import net.locusworks.portal.common.exceptions.PortalException;
import net.locusworks.portal.json.ApplicationLogRequest;
import net.locusworks.portal.json.PurgeLogRequest;
/**
* Controller for handling application logs
* @author Isaac Parenteau
* @since 1.0.0-RELEASE
* @version 1.0
*/
@Controller
@RequestMapping(value="logs")
public class ApplicationLogController extends BaseController {
private static ApplicationLogger logger = ApplicationLoggerFactory.getLogger(ApplicationLogController.class);
/**
* Get all the applications logs
* @return all the log entries
* @throws Exception exception
*/
@RequestMapping("getAll.do")
public @ResponseBody Success getAllLogs() throws Exception {
if (!(isLoggedIn() && hasPermission(PermissionType.VIEW_LOG_TAB))) {
throw PortalException.actionNotPermitted();
}
try {
List<ApplicationLogRequest> logs = appLogService.getAllLogs();
return new Success(true, logs);
} catch (Exception ex) {
logger.error("Unable to get all logs: %s", ex.getMessage());
throw ex;
}
}
/**
* Purge application logs between two dates
* @param request Date information to purge between
* @return success with the number of logs purged
* @throws Exception exception
*/
@RequestMapping("purge.do")
public @ResponseBody Success purgeLogs(@RequestBody PurgeLogRequest request) throws Exception {
if (!(isLoggedIn() && hasPermission(PermissionType.PURGE_LOGS))) {
throw PortalException.actionNotPermitted();
}
try {
Integer purgeCount = appLogService.purgeLogs(request);
appLogService.addPurgedLogs(request.getPurgeStartDate(), request.getPurgeEndDate());
return new Success(true, purgeCount);
} catch (Exception ex) {
logger.error("Unable to purge logs: %s", ex.getMessage());
throw ex;
}
}
}
@@ -0,0 +1,144 @@
package net.locusworks.portal.controllers;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMethod;
import net.locusworks.common.objectmapper.ObjectMapperHelper;
import net.locusworks.common.utils.Success;
import net.locusworks.portal.common.enums.PermissionType;
import net.locusworks.portal.common.enums.TriggerType;
import net.locusworks.portal.services.ApplicationLogService;
import net.locusworks.portal.services.CommService;
import net.locusworks.portal.services.UserSecurityService;
/**
* Base controller component that gives access to common objects that other controllers
* can take advantage of that extend this class
* @author Isaac Parenteau
* @since 1.0.0-RELEASE
* @version 1.0
*/
@Component
public class BaseController {
public static final String SUCCESS = ObjectMapperHelper.writeValue(Success.success()).getResults();
public static Enum<RequestMethod> REQUESTMETHOD = RequestMethod.POST;
@Autowired
protected UserSecurityService userSecurityService;
@Autowired
protected ApplicationLogService appLogService;
@Autowired
protected HttpServletRequest request;
/**
* A map representing the current users info
* All users subscribe to the account|system topic to allow for broadcast messages
* All users then subscribe to their own topic account|[id] for targetted messages
* @return the users authorization json data to access the site
*/
protected Map<String, Object> getAuthJsonData() {
Map<String, Object> data = userSecurityService.getAuthJsonData();
Set<String> topics = new LinkedHashSet<>();
topics.add(CommService.getGlobalStompAccountTopic());
topics.add(CommService.getStompAccountTopic(userSecurityService.getCurrentUser().getId()));
data.put("stompTopics", topics);
return data;
}
/**
* Checks to see if the user is currently logged in
* @return true if the user is logged in, false otherwise
* @throws Exception exception
*/
protected boolean isLoggedIn() throws Exception {
return UserSecurityService.isLoggedIn();
}
/**
* Checks to see if the current user has a give permission
* enabled
* @param permission Permission to check
* @return true if the user has the permission false otherwise
*/
protected boolean hasPermission(PermissionType permission) {
return userSecurityService.hasPermission(permission.toString());
}
/**
* Checks to see if a user has certain permissions enabled.
* The user has to have every permission specified enabled to return
* true.
* @param permissions Permissions to check
* @return true if the user has all the permissions false otherwise
*/
protected boolean hasPermissions(PermissionType... permissions) {
return hasPermissions(false, permissions);
}
/**
* Checks to see if a user has a permission enabled.
* If the or option is true the user only needs oen permission to return
* true otherwise the user will need all permissions to return true
* @param or enable user only needing one permission enabled
* @param permissions permissions to check
* @return true if the permission is enabled false otherwise.
*/
protected boolean hasPermissions(boolean or, PermissionType... permissions) {
boolean hasPermission = true;
for (PermissionType permission : permissions) {
hasPermission = or ? hasPermission || hasPermission(permission) : hasPermission && hasPermission(permission);
}
return hasPermission;
}
/**
* Send a system wide account stomp message trigger
* @param resource Resource to send
* @param type Trigger type
*/
protected void sendAccountTrigger(Object resource, TriggerType type) {
sendAccountTrigger(resource, type, 0);
}
/**
* Send an account a stomp message trigger
* @param resource resource to send
* @param type trigger type
* @param userId user account id to send to
*/
protected void sendAccountTrigger(Object resource, TriggerType type, Integer userId) {
CommService.sendAccountTrigger(resource, type, userId);
}
/**
* Send stomp message triggers to accounts
* @param resource the resource to send to to the client
* @param triggers triggers to fire.
*/
protected void sendAccountTrigger(Object resource, TriggerType... triggers) {
CommService.sendAccountTrigger(resource, triggers);
}
/**
* Send accounts a stomp message trigger
* @param resource resource to send
* @param type trigger type
* @param userIds user account ids to send to
*/
protected void sendAccountTrigger(Object resource, TriggerType type, Set<Integer> userIds) {
CommService.sendAccountTrigger(resource, type, userIds);
}
}
@@ -0,0 +1,121 @@
/**
*
*/
package net.locusworks.portal.controllers;
import java.util.Enumeration;
import java.util.Properties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import net.locusworks.common.configuration.PropertiesManager;
import net.locusworks.common.logger.ApplicationLogger;
import net.locusworks.common.logger.ApplicationLoggerFactory;
import net.locusworks.common.utils.Success;
import net.locusworks.portal.common.enums.PermissionType;
import net.locusworks.portal.common.exceptions.PortalException;
import net.locusworks.portal.common.services.ConfigurationService;
import net.locusworks.portal.json.ConfigurationRequest;
/**
* Controller to handle configuration values
* @author Isaac Parenteau
* @since 1.0.0-RELEASE
* @version 1.0
*/
@Controller
@RequestMapping(value = "config")
public class ConfigurationController extends BaseController {
private static ApplicationLogger logger = ApplicationLoggerFactory.getLogger(ConfigurationController.class);
@Autowired
private ConfigurationService confService;
/**
* Get a configuration value
* @param key Key for the value to get
* @return the value associated with the key
* @throws Exception exception
*/
@RequestMapping(value="getConfigurationValue.do")
public @ResponseBody Success getConfigurationValue(@RequestBody String key) throws Exception {
if (!(isLoggedIn() && hasPermission(PermissionType.VIEW_CONFIG))) {
throw PortalException.actionNotPermitted();
}
if (!confService.getConfiguration().containsKey(key)) {
throw PortalException.illegalArgument("No config value with key of " + key);
}
try {
return new Success(true, confService.getConfiguration(key));
} catch(Exception ex) {
logger.error("Unable to get configuration: %s", ex.getMessage());
throw ex;
}
}
/**
* Get the current configuration
* @return current configuration
* @throws Exception exception
*/
@RequestMapping(value="getConfiguration.do")
public @ResponseBody Success getConfiguration() throws Exception {
if (!(isLoggedIn() && hasPermission(PermissionType.VIEW_CONFIG))) {
throw PortalException.actionNotPermitted();
}
try {
return new Success(true, confService.getConfiguration());
} catch(Exception ex) {
logger.error("Unable to get configuration: %s", ex.getMessage());
throw ex;
}
}
/**
* Save configuration changes
* @param request Configuration changes to save
* @return success stating the save was successful
* @throws Exception exception
*/
@RequestMapping(value="saveConfiguration.do")
public @ResponseBody Success saveConfiguration(@RequestBody ConfigurationRequest request) throws Exception {
if (!(isLoggedIn() && hasPermission(PermissionType.CHANGE_CONFIG))) {
throw PortalException.actionNotPermitted();
}
try {
Properties old = new Properties();
//Get the old values before changes
PropertiesManager.addConfiguration(old, confService.getConfiguration());
confService.saveConfiguration(request);
Properties newProps = confService.getConfiguration();
Enumeration<?> oldEnum = old.propertyNames();
while (oldEnum.hasMoreElements()) {
String key = (String) oldEnum.nextElement();
if (!newProps.containsKey(key)) {
logger.warn("No new configuration value found for %s after update", key);
continue;
}
String oldValue = old.getProperty(key);
String newValue = newProps.getProperty(key);
if(!oldValue.equals(newValue)) {
appLogService.addConfigurationUpdatedEntry(key, oldValue, newValue);
}
}
return Success.success();
} catch(Exception ex) {
logger.error("Unable to save configuration: %s", ex.getMessage());
if (!(ex instanceof PortalException))
throw ex;
throw (PortalException) ex;
}
}
}
@@ -0,0 +1,118 @@
package net.locusworks.portal.controllers;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import net.locusworks.common.logger.ApplicationLogger;
import net.locusworks.common.logger.ApplicationLoggerFactory;
import net.locusworks.common.utils.Success;
import net.locusworks.portal.common.enums.CredentialType;
import net.locusworks.portal.common.enums.PermissionType;
import net.locusworks.portal.common.exceptions.PortalException;
import net.locusworks.portal.json.CredentialRequest;
import net.locusworks.portal.services.CredentialService;
/**
* Controller class to handle credential creations for scripts to use during run time
* These credentials are not the same as user credentials
* @author Isaac Parenteau
* @since 1.0.0-RELEASE
* @version 1.0
*/
@Controller
@RequestMapping(value="creds")
public class CredentialsController extends BaseController {
@Autowired
private CredentialService credentialService;
private static ApplicationLogger logger = ApplicationLoggerFactory.getLogger(CredentialsController.class);
/**
* Add credentials to the portal to be later used in a scheduled script
* @param request The credential request with the credential information
* @return the new credential request
* @throws Exception thrown when the credential request was rejected
*/
@RequestMapping(value="add.do")
public @ResponseBody Success addCredentials(@RequestBody CredentialRequest request) throws Exception {
if (!(isLoggedIn() && hasPermission(PermissionType.ADD_CREDENTIALS))) {
throw PortalException.actionNotPermitted();
}
try {
CredentialRequest pgr = credentialService.addCredentials(request);
appLogService.addCredentialsAddedEntry(pgr.getName(), CredentialType.in(request.getCredentialType(), CredentialType.SSH) ? "SSH Key" : "Username/Password");
return new Success(true, pgr);
} catch(Exception ex) {
logger.error("Unable to add a credentials: %s", ex.getMessage());
if (!(ex instanceof PortalException))
throw PortalException.egregiousServer();
throw (PortalException) ex;
}
}
/**
* Get the current set of stored credentials to be shown to the user for selection
* @return list of credentials
* @throws Exception thrown when the list cannot be retrieved
*/
@RequestMapping(value="get.do")
public @ResponseBody Success getCredentials() throws Exception {
if (!(isLoggedIn())) {
throw PortalException.actionNotPermitted();
}
try {
List<CredentialRequest> credentials = credentialService.getCredentials();
return new Success(true, credentials);
} catch(Exception ex) {
logger.error("Unable to add a credentials: %s", ex.getMessage());
if (!(ex instanceof PortalException))
throw PortalException.egregiousServer();
throw (PortalException) ex;
}
}
/**
* Delete a set of credentials from the database
* @param request the information about the credentials to delete
* @return the deleted credentials
* @throws Exception thrown when the credentials cannot be deleted
*/
@RequestMapping(value="delete.do")
public @ResponseBody Success deleteCredentials(@RequestBody CredentialRequest request) throws Exception {
if (!(isLoggedIn() && hasPermission(PermissionType.DELETE_CREDENTIALS))) {
throw PortalException.actionNotPermitted();
}
try {
credentialService.deleteCredentials(request);
appLogService.addCredentialsDeletedEntry(request.getName());
return new Success(true, request);
} catch(Exception ex) {
logger.error("Unable to add a credentials: %s", ex.getMessage());
throw ex;
}
}
/**
* Get the valid credential types the user can select from
* @return list of valid credential types
* @throws Exception thrown when the list of credential types cannot be retrieved
*/
@RequestMapping(value="getCredentialTypes.do")
public @ResponseBody Success getCredentialTypes() throws Exception {
if (!(isLoggedIn())) {
throw PortalException.actionNotPermitted();
}
try {
return new Success(true, credentialService.getCredentialTypes());
} catch(Exception ex) {
logger.error("Unable to get credential types: %s", ex.getMessage());
throw ex;
}
}
}
@@ -0,0 +1,104 @@
package net.locusworks.portal.controllers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
import net.locusworks.common.exceptions.ApplicationException;
import net.locusworks.common.logger.ApplicationLogger;
import net.locusworks.common.logger.ApplicationLoggerFactory;
import net.locusworks.common.objectmapper.ObjectMapperError;
import net.locusworks.common.objectmapper.ObjectMapperHelper;
import net.locusworks.portal.services.ApplicationLogService;
/**
* Controller to handle and log various exceptions
* @see net.locusworks.portal.common.exceptions.PortalException
* @author Isaac Parenteau
* @since 1.0.0-RELEASE
* @version 1.0
*/
@ControllerAdvice
public class ExceptionController extends ResponseEntityExceptionHandler {
private static ApplicationLogger logger = ApplicationLoggerFactory.getLogger(ExceptionController.class);
@Autowired
private ApplicationLogService appLogService;
/**
* Log all PortalExceptions to the application log service then pass the exception
* onto the client as the exception that was thrown
* Print the stack trace to the log file
* @param ex the thrown exception
* @param request the current web request
* @return http status 400 with the exception data
* {@code 400 Bad Request}.
* @see <a href="http://tools.ietf.org/html/rfc7231#section-6.5.1">HTTP/1.1: Semantics and Content, section 6.5.1</a>
*/
@ExceptionHandler(value = { ApplicationException.class })
protected ResponseEntity<Object> handlePortalException(Exception ex, WebRequest request) {
ApplicationException pre = (ApplicationException)ex;
logger.error(ex);
appLogService.addExceptionEntry(pre.getMessage());
return handleExceptionInternal(ex, toExceptionResponseString(pre), new HttpHeaders(), HttpStatus.BAD_REQUEST, request);
}
/**
* All other exceptions will be logged as an egregious server error and stack trace
* will be logged in the log files
* @param ex the thrown exception
* @param request the current web request
* @return http status 400 with the exception data
* {@code 400 Bad Request}.
* @see <a href="http://tools.ietf.org/html/rfc7231#section-6.5.1">HTTP/1.1: Semantics and Content, section 6.5.1</a>
*/
@ExceptionHandler(value = { Exception.class })
protected ResponseEntity<Object> handleUncaughtException(Exception ex, WebRequest request) {
ApplicationException pre = ApplicationException.egregiousServer();
appLogService.addExceptionEntry(pre.getMessage());
logger.error(ex);
return handleExceptionInternal(ex, toExceptionResponseString(pre), new HttpHeaders(), HttpStatus.BAD_REQUEST, request);
}
private String toExceptionResponseString(ApplicationException pre) {
return ObjectMapperHelper.writeValue(new ExceptionResponse(pre))
.withErrorHandler(new ObjectMapperError() {
@Override
public void getError(Throwable e) {
logger.error("Unable to convert to json: " + e.getMessage(), e);
}
}).getResults();
}
/**
* Class helper to jsonify the exception
* @author Isaac Parenteau
* @since 1.0.0-RELEASE
* @version 1.0
*/
protected static class ExceptionResponse {
int code;
String message;
boolean success = false;
/**
* Constructor
* @param e PortalException to convert to be able to jsonify and send to the client
*/
private ExceptionResponse(ApplicationException e) {
this.code = e.getCode();
this.message = e.getMessage();
}
public int getCode() { return code; }
public String getMessage() { return message; }
public boolean getSuccess() { return success; }
}
}
@@ -0,0 +1,107 @@
package net.locusworks.portal.controllers;
import java.io.File;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import net.locusworks.common.logger.ApplicationLogger;
import net.locusworks.common.logger.ApplicationLoggerFactory;
import net.locusworks.common.utils.Success;
import net.locusworks.portal.common.enums.PermissionType;
import net.locusworks.portal.common.enums.TriggerType;
import net.locusworks.portal.common.exceptions.PortalException;
import net.locusworks.portal.json.ResourceFileRequest;
import net.locusworks.portal.services.ResourceFileService;
import net.locusworks.portal.util.FileDownloadServletResponse;
/**
* Controller servlet class to handle file functions
* @author Isaac Parenteau
* @since 1.0.0-RELEASE
* @version 1.0
*/
@Controller
@RequestMapping(value = "file")
public class FileController extends BaseController {
private static ApplicationLogger logger = ApplicationLoggerFactory.getLogger(FileController.class);
@Autowired
private ResourceFileService fileService;
/**
* Download a specified file from the server
* @param fileId the id of the file to download
* @param response current web response
* @throws Exception thrown when there is an error in downloading the file
*/
@RequestMapping(value = "download.do")
public synchronized void downloadFile(@RequestParam(value = "id") Integer fileId, HttpServletResponse response) throws Exception {
ResourceFileRequest rfr = fileService.locateFile(fileId);
if (rfr == null) {
throw PortalException.noEntryExists("no file exists to be downloaded for given hash");
}
File fileToDownload = new File(String.format("%s/%s", rfr.getFileLocation(), rfr.getFileName()));
if (!fileToDownload.exists()) {
throw PortalException.noSuchFile(rfr.getFileName());
}
appLogService.addFileDownloadEntry(rfr.getFileName());
try(FileDownloadServletResponse fdsr = new FileDownloadServletResponse(response, rfr.getFileName())) {
fdsr.sendFile(fileToDownload);
} catch (InterruptedException ex) {
logger.warn("File download was interrupted: %s", ex.getMessage());
}
}
/**
* Delete a file from the portal server
* @param file information about the file to delete
* @return the deleted file
* @throws Exception thrown when the requested file cannot be deleted
*/
@RequestMapping(value = "delete.do", method=RequestMethod.POST)
public synchronized @ResponseBody Success deleteFile(@RequestBody ResourceFileRequest file) throws Exception {
if (!(isLoggedIn() && hasPermission(PermissionType.DELETE_FILE))) {
throw PortalException.actionNotPermitted();
}
ResourceFileRequest rfr = fileService.locateFile(file.getId());
if (rfr == null) {
throw PortalException.noEntryExists("no file exists to be downloaded for given hash");
}
try {
File fileToDelete = new File(String.format("%s/%s", rfr.getFileLocation(), rfr.getFileName()));
if (!fileToDelete.exists()) {
throw PortalException.noSuchFile(fileToDelete.getName());
}
boolean deleted = FileUtils.deleteQuietly(fileToDelete);
if (!deleted) {
throw PortalException.unableToDeleteFile(file.getFileName());
}
fileService.deleteResourceFile(rfr.getId());
appLogService.addFileDeletedEntry(rfr.getFileName());
sendAccountTrigger(rfr, TriggerType.FILE_DELETED);
return new Success(true, rfr);
} catch (Exception ex) {
logger.error("Unable to delete file: %s", ex.getMessage());
if (!(ex instanceof PortalException))
throw ex;
throw (PortalException)ex;
}
}
}
@@ -0,0 +1,123 @@
package net.locusworks.portal.controllers;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import net.locusworks.common.logger.ApplicationLogger;
import net.locusworks.common.logger.ApplicationLoggerFactory;
import net.locusworks.common.utils.Success;
import net.locusworks.portal.common.enums.PermissionType;
import net.locusworks.portal.common.exceptions.PortalException;
import net.locusworks.portal.json.GitRequest;
import net.locusworks.portal.services.GitService;
/**
* Controller handling git operations from the client
* @author Isaac Parenteau
* @since 1.0.0-RELEASE
* @version 1.0
*/
@Controller
@RequestMapping(value = "git")
public class GitController extends BaseController {
private static ApplicationLogger logger = ApplicationLoggerFactory.getLogger(GitController.class);
@Autowired
private GitService gitService;
/**
* Add a git repository to the server
* @param request the git information to add
* @return The added git information
* @throws Exception thrown when an exception occured when trying to add the git information
*/
@RequestMapping(value="add.do")
public @ResponseBody Success addRepository(@RequestBody GitRequest request) throws Exception {
if (!(isLoggedIn() && hasPermission(PermissionType.ADD_GIT_REPO))) {
throw PortalException.actionNotPermitted();
}
try {
GitRequest pgr = gitService.addGitRepository(request);
appLogService.addRepositoryAddedEntry(request.getName());
return new Success(true, pgr);
} catch(Exception ex) {
logger.error("Unable to add a repository: %s", ex.getMessage());
if (!(ex instanceof PortalException))
throw ex;
throw (PortalException) ex;
}
}
/**
* Issues a git pull request to the specified git repository
* @param request the git request with the repository information to do the pull request on
* @return the git request with the updated information after the pull request was finished
* @throws Exception thrown when an error occured when trying to do the pull request
*/
@RequestMapping(value="pull.do")
public @ResponseBody Success pullRepository(@RequestBody GitRequest request) throws Exception {
if (!(isLoggedIn() && hasPermission(PermissionType.PULL_GIT_REPO))) {
throw PortalException.actionNotPermitted();
}
try {
GitRequest pgr = gitService.pullGitRepository(request);
appLogService.addRepositoryPullEntry(request.getName());
return new Success(true, pgr);
} catch(Exception ex) {
logger.error("Unable to pull repository: %s", ex.getMessage());
if (!(ex instanceof PortalException))
throw ex;
throw (PortalException) ex;
}
}
/**
* Delete a git repository from the server
* @param request Information about the repository to delete
* @return the deleted git repository information
* @throws Exception thrown when an error occurred when trying to delete the git repository
*/
@RequestMapping(value="delete.do")
public @ResponseBody Success deleteRepository(@RequestBody GitRequest request) throws Exception {
if (!(isLoggedIn() && hasPermission(PermissionType.DELETE_GIT_REPO))) {
throw PortalException.actionNotPermitted();
}
try {
GitRequest pgr = gitService.deleteGitRepository(request);
appLogService.addRepositoryRemovedEntry(request.getName());
return new Success(true, pgr);
} catch(Exception ex) {
logger.error("Unable to delete repository: %s", ex.getMessage());
if (!(ex instanceof PortalException))
throw ex;
throw (PortalException) ex;
}
}
/**
* Get all the current git repositories stored in the database
* @return list of all the git repositories
* @throws Exception thrown when an error occurred when trying to retrieve the repositories
*/
@RequestMapping(value="getAll.do")
public @ResponseBody Success getAllRepositories() throws Exception {
if (!(isLoggedIn())) {
throw PortalException.actionNotPermitted();
}
try {
List<GitRequest> pgrList = gitService.getRepositories();
return new Success(true, pgrList);
} catch(Exception ex) {
logger.error("Unable to add a repository: %s", ex.getMessage());
if (!(ex instanceof PortalException))
throw ex;
throw (PortalException) ex;
}
}
}
@@ -0,0 +1,47 @@
package net.locusworks.portal.controllers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.simp.annotation.SendToUser;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import net.locusworks.portal.services.SecureRandomService;
import net.locusworks.portal.websocket.PingPong;
/**
* Controller used for checking web socket availability
* @author Isaac Parenteau
* @since 1.0.0-RELEASE
* @version 1.0
*/
@Controller
@RequestMapping(value = "info")
public class InfoController {
@Autowired
private SecureRandomService randomService;
/**
* Play ping pong between the client and server to see if web sockets work
* @param input the ping pong input
* @return the return data to check for connectivity
* @throws Exception exception
*/
@MessageMapping("/ping")
@SendToUser(value="/queue/pong", broadcast=false) // send only to the session that sent the request
public PingPong ping(PingPong input) throws Exception {
int receivedBytes = input.getData().length;
int pullBytes = input.getPull();
PingPong response = input;
if (pullBytes == 0) {
response.setData(new byte[0]);
} else if (pullBytes != receivedBytes) {
// create random byte array
byte[] data = randomService.nextBytes(pullBytes);
response.setData(data);
}
return response;
}
}
@@ -0,0 +1,66 @@
package net.locusworks.portal.controllers;
import java.util.List;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import net.locusworks.common.logger.ApplicationLogger;
import net.locusworks.common.logger.ApplicationLoggerFactory;
import net.locusworks.common.logger.ApplicationLoggerFactory.LoggerInfo;
import net.locusworks.common.utils.Success;
import net.locusworks.portal.common.enums.PermissionType;
import net.locusworks.portal.common.exceptions.PortalException;
/**
* Controller that handles adjusting the loggers log levels from the client
* @author Isaac Parenteau
* @since 1.0.0-RELEASE
* @version 1.0
*/
@Controller
@RequestMapping(value="logger")
public class LogController extends BaseController {
private ApplicationLogger logger = ApplicationLoggerFactory.getLogger(LogController.class);
/**
* Get all the currently registered loggers
* @return list of all the currently registered loggers
* @throws Exception exception
*/
@RequestMapping(value="getLoggers.do")
public @ResponseBody Success getLoggers() throws Exception {
if (!isLoggedIn() && hasPermission(PermissionType.VIEW_LOG_LEVELS)) {
throw PortalException.actionNotPermitted();
}
try {
return new Success(true, ApplicationLoggerFactory.getLoggers());
} catch (Exception ex) {
logger.error("Unable to get loggers %s", ex.getMessage());
throw ex;
}
}
/**
* Set the loggers to a given log level
* @param loggerInfos The loggers to set
* @return true if the loggers were set successfully
* @throws Exception exception
*/
@RequestMapping(value="setLoggers.do")
public @ResponseBody Success setLoggers(@RequestBody List<LoggerInfo> loggerInfos) throws Exception {
if (!isLoggedIn() && hasPermission(PermissionType.CHANGE_LOG_LEVELS)) {
throw PortalException.actionNotPermitted();
}
try {
ApplicationLoggerFactory.setLogLevels(loggerInfos);
return Success.success();
} catch (Exception ex) {
logger.error("Unable to set loggers %s", ex.getMessage());
throw ex;
}
}
}
@@ -0,0 +1,167 @@
package net.locusworks.portal.controllers;
import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import net.locusworks.common.logger.ApplicationLogger;
import net.locusworks.common.logger.ApplicationLoggerFactory;
import net.locusworks.common.utils.Success;
import net.locusworks.common.utils.Utils;
import net.locusworks.portal.common.enums.PermissionType;
import net.locusworks.portal.common.enums.TriggerType;
import net.locusworks.portal.common.exceptions.PortalException;
import net.locusworks.portal.json.PermissionGroupRequest;
import net.locusworks.portal.services.PermissionsService;
import net.locusworks.portal.services.UserSecurityService;
/**
* Controller for handling permissions
* @author Isaac Parenteau
* @since 1.0.0-RELEASE
* @version 1.0
*/
@Controller
@RequestMapping(value="permissions")
public class PermissionsController extends BaseController {
private static ApplicationLogger logger = ApplicationLoggerFactory.getLogger(PermissionsController.class);
@Autowired
private PermissionsService permService;
/**
* Update a permission category
* @param request Request with updated information
* @return the updated permisison requests
* @throws Exception exception
*/
@RequestMapping(value="updatePermissionCategory.do")
public @ResponseBody Success updatePermissionCategory(@RequestBody PermissionGroupRequest request) throws Exception {
if (!(isLoggedIn() && hasPermission(PermissionType.ADD_PERMS))) {
throw PortalException.unAuthorized();
}
try {
PermissionGroupRequest updatedGroup = permService.updatePermissionGroup(request);
appLogService.addPermissionsChangedEntry(updatedGroup.getDisplayName());
Utils.safeSet(UserSecurityService.getSessionUserIds()).forEach(id -> userSecurityService.loginAuId(id));
//Only send the trigger to the users effected by the change
Set<Integer> permAccountGroup = permService.getPermissionGroupAccounts(updatedGroup.getName());
sendAccountTrigger(updatedGroup, TriggerType.PERMISSIONS_CHANGED, permAccountGroup);
sendAccountTrigger(updatedGroup, TriggerType.PERMISSIONS_UPDATED);
return new Success(true, updatedGroup);
} catch(Exception ex) {
logger.error("unable tup date permission categories: %s", ex.getMessage());
if (!(ex instanceof PortalException))
throw ex;
throw (PortalException) ex;
}
}
/**
* add a new permission category
* @param request New permission category information
* @return newly created permission category
* @throws Exception exception
*/
@RequestMapping(value="addPermssionCategory.do")
public @ResponseBody Success addPermssionCategory(@RequestBody PermissionGroupRequest request) throws Exception {
if (!(isLoggedIn() && hasPermission(PermissionType.ADD_PERMS))) {
throw PortalException.unAuthorized();
}
try {
PermissionGroupRequest addedGroup = permService.addPermissionGroup(request);
appLogService.addPermissionsChangedEntry(addedGroup.getDisplayName());
sendAccountTrigger(addedGroup, TriggerType.PERMISSIONS_ADDED);
return new Success(true, addedGroup);
} catch(Exception ex) {
logger.error("unable to add permission category: %s", ex.getMessage());
if (!(ex instanceof PortalException))
throw ex;
throw (PortalException) ex;
}
}
/**
* Get all permission groups with the enabled/disabled permissions associated with them
* @return list of permission groups
* @throws Exception exception
*/
@RequestMapping(value="getPermissionGroups.do")
public @ResponseBody Success getPermissionGroups() throws Exception {
if (!(isLoggedIn() && hasPermission(PermissionType.VIEW_PERMISSION_TAB))) {
throw PortalException.actionNotPermitted();
}
try {
return new Success(true, permService.getPermissionGroups());
} catch(Exception ex) {
logger.error("unable to get permission groups %s", ex.getMessage());
throw ex;
}
}
/**
* Gets all the permission groups minus the individual permissions
* @return all the permission groups
* @throws Exception exception
*/
@RequestMapping(value="getAll.do")
public @ResponseBody Success getAllPermissionGroups() throws Exception {
try {
return new Success(true, permService.getPermissionGroups(false));
} catch(Exception ex) {
logger.error("unabe to get all permission groups: %s", ex.getMessage());
throw ex;
}
}
/**
* Gets all the different permission categories
* @return list of permission categories
* @throws Exception exception
*/
@RequestMapping(value="getPermissionCategories.do")
public @ResponseBody Success getPermissionCategories() throws Exception {
if (!(isLoggedIn() && hasPermission(PermissionType.ADD_PERMS))) {
throw PortalException.actionNotPermitted();
}
try {
return new Success(true, permService.getPermissionCatgories());
} catch(Exception ex) {
logger.error("unable to get permission categories: %s", ex.getMessage());
throw ex;
}
}
/**
* Delete a permission group. Will throw an exception if there are users
* associated with the permission group
* @param request data to delete
* @return the deleted information
* @throws Exception exception
*/
@RequestMapping(value="deletePermissionGroup.do")
public @ResponseBody Success deletePermissionGroup(@RequestBody PermissionGroupRequest request) throws Exception {
if (!(isLoggedIn() && hasPermission(PermissionType.DELETE_PERMS))) {
throw PortalException.actionNotPermitted();
}
try {
permService.deletePermissionGroup(request);
for (Integer id : UserSecurityService.getSessionUserIds()) {
userSecurityService.loginAuId(id);
}
appLogService.addPermissionsDeletedEntry(request.getDisplayName());
sendAccountTrigger(request, TriggerType.PERMISSIONS_DELETED);
return new Success(true, request);
} catch(Exception ex) {
logger.error("unable to delete permission group: %s", ex.getMessage());
if (!(ex instanceof PortalException))
throw ex;
throw (PortalException) ex;
}
}
}
@@ -0,0 +1,221 @@
package net.locusworks.portal.controllers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import net.locusworks.common.logger.ApplicationLogger;
import net.locusworks.common.logger.ApplicationLoggerFactory;
import net.locusworks.common.utils.Success;
import net.locusworks.portal.common.enums.PermissionType;
import net.locusworks.portal.common.enums.TriggerType;
import net.locusworks.portal.common.exceptions.PortalException;
import net.locusworks.portal.json.ScriptRequest;
import net.locusworks.portal.json.ScriptScheduleRequest;
import net.locusworks.portal.services.ScriptService;
/**
* Controller for handling creating/editing/deleting/running scripts
* @author Isaac Parenteau
* @since 1.0.0-RELEASE
* @version 1.0
*/
@Controller
@RequestMapping(value="scripts")
public class ScriptController extends BaseController {
private static ApplicationLogger logger = ApplicationLoggerFactory.getLogger(ScriptController.class);
@Autowired
private ScriptService scriptService;
/**
* Manually run a specified script
* @param request the script to execute
* @return the executed script information
* @throws Exception thrown when the script to be executed encounters an exception
*/
@RequestMapping(value="runScript.do")
public @ResponseBody Success runScript(@RequestBody ScriptRequest request) throws Exception {
if (!(isLoggedIn() && hasPermission(PermissionType.RUN_SCRIPT))) {
throw PortalException.actionNotPermitted();
}
try {
scriptService.runScript(request);
return new Success(true, request);
} catch(Exception ex) {
logger.error("Unable to run script: %s", ex.getMessage());
throw ex;
}
}
/**
* Add a script to the database for later execution
* @param request Information about the script to add
* @return the newly added script
* @throws Exception thrown when the script to be added encounters an exception
*/
@RequestMapping(value="addScript.do")
public @ResponseBody Success addScript(@RequestBody ScriptRequest request) throws Exception {
if (!(isLoggedIn() && hasPermission(PermissionType.ADD_SCRIPT))) {
throw PortalException.actionNotPermitted();
}
try {
ScriptRequest newScript = scriptService.addScript(request);
appLogService.addScriptAddedEntry(newScript.getName());
sendAccountTrigger(newScript, TriggerType.SCRIPT_ADDED);
return new Success(true, newScript);
} catch(Exception ex) {
logger.error("Unable to add a script: %s", ex.getMessage());
throw ex;
}
}
/**
* Update a scripts information
* @param request Information about the script to update
* @return the updated script
* @throws Exception thrown when the script to be updated encounters an exception
*/
@RequestMapping(value="updateScript.do")
public @ResponseBody Success updateScript(@RequestBody ScriptRequest request) throws Exception {
if (!(isLoggedIn() && hasPermission(PermissionType.ADD_SCRIPT))) {
throw PortalException.actionNotPermitted();
}
try {
ScriptRequest newScript = scriptService.updateScript(request);
appLogService.addScriptEditedEntry(newScript.getName());
sendAccountTrigger(newScript, TriggerType.SCRIPT_UPDATED);
return new Success(true, newScript);
} catch(Exception ex) {
logger.error("Unable to update script: %s", ex.getMessage());
throw ex;
}
}
/**
* Delete a script
* @param request the Script to delete
* @return the deleted script
* @throws Exception thrown when the script to be deleted encounters an exception
*/
@RequestMapping(value="deleteScript.do")
public @ResponseBody Success deleteScript(@RequestBody ScriptRequest request) throws Exception {
if (!(isLoggedIn() && hasPermission(PermissionType.DELETE_SCRIPT))) {
throw PortalException.actionNotPermitted("Not authorized to delete Scripts");
}
if (scriptService.scriptHasResourceFilesAttached(request.getId()) && !hasPermission(PermissionType.DELETE_FILE)) {
throw PortalException.actionNotPermitted("Not authorized to delete resource files attached to script");
}
try {
ScriptRequest deleted = scriptService.deleteScript(request);
appLogService.addScriptDeletedEntry(deleted.getName());
sendAccountTrigger(deleted, TriggerType.SCRIPT_DELETED);
return new Success(true, deleted);
} catch(Exception ex) {
logger.error("Unable to delete script: %s", ex.getMessage());
throw ex;
}
}
/**
* Delete a specific schedule from a script
* @param request The script schedule to be deleted
* @return the deleted script schedule
* @throws Exception thrown when the schedule encounters an error when trying to be deleted
*/
@RequestMapping(value="deleteScriptSchedule.do")
public @ResponseBody Success deleteScriptSchedule(@RequestBody ScriptScheduleRequest request) throws Exception {
if (!(isLoggedIn() && hasPermission(PermissionType.DELETE_SCRIPT_SCHED_STATUS))) {
throw PortalException.actionNotPermitted();
}
if (scriptService.scriptScheduleHasResourceFilesAttached(request.getId()) && !hasPermission(PermissionType.DELETE_FILE)) {
throw PortalException.actionNotPermitted("Not authorized to delete resource files attached to script schedule");
}
try {
ScriptScheduleRequest deleted = scriptService.deleteScriptSchedule(request);
appLogService.addScriptScheduleDeletedEntry(deleted.getJobId());
sendAccountTrigger(deleted, TriggerType.SCRIPT_SCHEDULE_DELETED);
return new Success(true, deleted);
} catch(Exception ex) {
logger.error("Unable to delete script schedule: %s", ex.getMessage());
throw ex;
}
}
/**
* Get all the scripts
* @return list of scripts
* @throws Exception thrown when the list can't be retrieved
*/
@RequestMapping(value="getAll.do")
public @ResponseBody Success getAll() throws Exception {
if (!(isLoggedIn() && hasPermission(PermissionType.VIEW_SCRIPT_TAB))) {
throw PortalException.actionNotPermitted();
}
try {
return new Success(true, scriptService.getAllScripts());
} catch(Exception ex) {
logger.error("Unable get all scripts: %s", ex.getMessage());
throw ex;
}
}
/**
* Get the list of allowed schedule frequency
* @return a list of schedule frequencies
* @throws Exception thrown when the list cannot be retrieved
*/
@RequestMapping(value="getScheduleFrequencies.do")
public @ResponseBody Success getScheduleFrequencies() throws Exception {
if (!(isLoggedIn())) {
throw PortalException.actionNotPermitted();
}
try {
return new Success(true, scriptService.getScheduleFrequencies());
} catch(Exception ex) {
logger.error("Unable to get script schedule frequencies: %s", ex.getMessage());
throw ex;
}
}
/**
* Get a list of allowed script types
* @return list of script types
* @throws Exception thrown when the list cannot be retrieved
*/
@RequestMapping(value="getScriptType.do")
public @ResponseBody Success getScriptType() throws Exception {
if (!(isLoggedIn())) {
throw PortalException.actionNotPermitted();
}
try {
return new Success(true, scriptService.getScriptTypes());
} catch(Exception ex) {
logger.error("Unable to get script types: %s", ex.getMessage());
throw ex;
}
}
/**
* Get the status of a script schedule
* @param uuid the schedules unique identifier
* @return the status of the schedule
* @throws Exception thrown when the status cannot be retrieved
*/
@RequestMapping(value="getScriptScheduleStatus.do")
public @ResponseBody Success getScriptScheduleStatus(@RequestBody String uuid) throws Exception {
if (!(isLoggedIn() && hasPermission(PermissionType.VIEW_SCRIPT_SCHED_STATUS))) {
throw PortalException.actionNotPermitted();
}
try {
return new Success(true, scriptService.getScriptSchedResults(uuid));
} catch(Exception ex) {
logger.error("Unable to get script schedule status: ", ex);
throw ex;
}
}
}
@@ -0,0 +1,242 @@
package net.locusworks.portal.controllers;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import net.locusworks.common.logger.ApplicationLogger;
import net.locusworks.common.logger.ApplicationLoggerFactory;
import net.locusworks.common.utils.Success;
import net.locusworks.portal.common.enums.PermissionType;
import net.locusworks.portal.common.enums.TriggerType;
import net.locusworks.portal.common.exceptions.PortalException;
import net.locusworks.portal.common.triggers.AccountTrigger;
import net.locusworks.portal.json.EmailTemplateRequest;
import net.locusworks.portal.json.EnvironmentVariableRequest;
import net.locusworks.portal.json.ValueRequest;
import net.locusworks.portal.services.CommService;
import net.locusworks.portal.services.SystemService;
/**
* Controller for the handling system environment variables
* @author Isaac Parenteau
* @since 1.0.0-RELEASE
* @version 1.0
*/
@Controller
@RequestMapping(value="system")
public class SystemController extends BaseController {
private static ApplicationLogger logger = ApplicationLoggerFactory.getLogger(SystemController.class);
@Autowired
private SystemService sysService;
/**
* Add global environment variables to the system
* @param envVars the list of variables to add
* @return the added variables
* @throws Exception thrown when the variables cannot be added
*/
@RequestMapping(value="addEnvVars.do")
public @ResponseBody Success addEnvVars(@RequestBody List<EnvironmentVariableRequest> envVars) throws Exception {
if (!(isLoggedIn() && hasPermission(PermissionType.ADD_ENV_VARS))) {
throw PortalException.actionNotPermitted();
}
try {
List<EnvironmentVariableRequest> newEnvVars = sysService.addEnvironmentVariables(envVars);
envVars.forEach(evr -> {
if(evr.getIsNew()) {
appLogService.addEnvVarAddedEntry(evr.getName());;
} else {
appLogService.addEnvVarEditedEntry(evr.getName());
}
});
sendAccountTrigger(newEnvVars, TriggerType.ENV_VARS_ADDED);
return new Success(true, newEnvVars);
} catch(Exception ex) {
logger.error("Unable to add a repository: ", ex);
if (!(ex instanceof PortalException))
throw PortalException.egregiousServer();
throw (PortalException) ex;
}
}
/**
* Delete environment variables from the system
* @param envVars list of variables to remove
* @return list of deleted environment variables
* @throws Exception thrown when an error occurred while trying to delete the environment variables
*/
@RequestMapping(value="deleteEnvVars.do")
public @ResponseBody Success deleteEnvVars(@RequestBody List<EnvironmentVariableRequest> envVars) throws Exception {
if (!(isLoggedIn() && hasPermission(PermissionType.DELETE_ENV_VARS))) {
throw PortalException.actionNotPermitted();
}
try {
List<EnvironmentVariableRequest> deletedEnvVars = sysService.deleteEnvironmentVariables(envVars);
deletedEnvVars.forEach(evr -> appLogService.addEnvVarDeletedEntry(evr.getName()));
sendAccountTrigger(deletedEnvVars, TriggerType.ENV_VARS_DELETED);
return new Success(true, deletedEnvVars);
} catch(Exception ex) {
logger.error("Unable to add a repository: %s", ex.getMessage());
throw ex;
}
}
/**
* Get all the environment variables
* @return List of environment variables
* @throws Exception thrown when an error occurrs while trying to retrieve the list
*/
@RequestMapping(value="getAll.do")
public @ResponseBody Success getAll() throws Exception {
if (!(isLoggedIn() && hasPermission(PermissionType.VIEW_ENV_VARS))) {
throw PortalException.actionNotPermitted();
}
try {
return new Success(true, sysService.getAllEnvironmentVariables());
} catch(Exception ex) {
logger.error("Unable to add a repository: %s", ex.getMessage());
throw ex;
}
}
/**
* Update the smtp configuration
* @param request Information about the smtp configuration values
* @return Updated list for smtp configuration
* @throws Exception thrown when an error occurs while trying to update the smtp configuration
*/
@RequestMapping(value="updateSmtpConfig.do")
public @ResponseBody Success updateSmtpConfig(@RequestBody List<ValueRequest> request) throws Exception{
if (!(isLoggedIn() && hasPermission(PermissionType.EDIT_SMTP_CONFIG))) {
throw PortalException.actionNotPermitted();
}
try {
List<ValueRequest> smtpConfig = sysService.updateSmtpConfig(request);
appLogService.addSMTPConfigChangeEntry();
sendAccountTrigger(smtpConfig, TriggerType.SMTP_CONFIG_UPDATED);
return new Success(true, smtpConfig);
} catch(Exception ex) {
logger.error("Unable to update smtp configuration: %s", ex.getMessage());
throw ex;
}
}
/**
* Get the smtp configuration
* @return List of smtp configuration
* @throws Exception thrown when an error occurs while trying to retrieve the list
*/
@RequestMapping(value="getSmtpConfig.do")
public @ResponseBody Success getSmtpConfig() throws Exception {
if (!(isLoggedIn() && hasPermission(PermissionType.VIEW_SMTP_CONFIG))) {
throw PortalException.actionNotPermitted();
}
try {
return new Success(true, sysService.getSmtpConfiguration());
} catch(Exception ex) {
logger.error("Unable to get smtp configuration: %s", ex.getMessage());
throw ex;
}
}
/**
* Get the list of email templates for the client
* @return list of email templates
* @throws Exception thrown when an error occurs while trying to retrieve the list
*/
@RequestMapping(value = "getEmailTemplates.do")
public @ResponseBody Success getEmailTemplates() throws Exception {
if (!(isLoggedIn() && hasPermission(PermissionType.VIEW_EMAIL_TAB))) {
throw PortalException.actionNotPermitted();
}
try {
return new Success(true, sysService.getEmailTemplates());
} catch(Exception ex) {
logger.error("Unable to get email templates: %s", ex.getMessage());
throw ex;
}
}
/**
* Add or update an email template
* @param request information about the email template to use for the add/update
* @return the newly created or updated template
* @throws Exception thrown when an error occurs during the transaction
*/
@RequestMapping(value = "addEmailTemplate.do")
public @ResponseBody Success addEmailTemplate(@RequestBody EmailTemplateRequest request) throws Exception {
if (!(isLoggedIn() && hasPermission(PermissionType.ADD_EMAIL))) {
throw PortalException.actionNotPermitted();
}
try {
EmailTemplateRequest tmplReq = sysService.addEmailTemplate(request);
if (request.getId() == null) {
appLogService.addEmailTemplateAddedEntry(tmplReq.getEmailName());
} else {
appLogService.addEmailTemplateEditedEntry(tmplReq.getEmailName());
}
sendAccountTrigger(tmplReq, TriggerType.EMAIL_TEMPLATE_UPDATED);
return new Success(true, tmplReq);
} catch(Exception ex) {
logger.error("Unable to add/update email templates: %s", ex.getMessage());
throw ex;
}
}
/**
* delete an email template from the system
* @param request The email template to delete
* @return the email template that was deleted
* @throws Exception thrown when an error occurs while trying to retrieve the list
*/
@RequestMapping(value = "deleteEmailTemplate.do")
public @ResponseBody Success deleteEmailTemplate(@RequestBody EmailTemplateRequest request) throws Exception {
if (!(isLoggedIn() && hasPermission(PermissionType.DELETE_EMAIL))) {
throw PortalException.actionNotPermitted();
}
try {
EmailTemplateRequest etr = sysService.deleteEmailTemplate(request);
appLogService.addEmailTemplateDeletedEntry(etr.getEmailName());
sendAccountTrigger(etr, TriggerType.EMAIL_TEMPLATE_DELETED);
return new Success(true, etr);
} catch(Exception ex) {
logger.error("Unable to delete email templates: %s", ex.getMessage());
throw ex;
}
}
/**
* Update the encryption seed
* @param seed Seed to update
* @return true if success
* @throws Exception thrown when an error occurs while trying to update seed
*/
@RequestMapping(value = "updateSeed.do")
public @ResponseBody Success updateSeed(@RequestBody String seed) throws Exception {
if (!(isLoggedIn() && hasPermission(PermissionType.UPDATE_SEED_VALUE))) {
throw PortalException.actionNotPermitted();
}
try {
CommService.sendEvent(new AccountTrigger(TriggerType.SEED_UPDATED, seed));
sendAccountTrigger(seed, TriggerType.SEED_UPDATED);
return Success.success();
} catch(Exception ex) {
logger.error("Unable to delete email templates: %s", ex.getMessage());
throw ex;
}
}
}
@@ -0,0 +1,10 @@
/**
* Controller package houses all the rest requests from the client and sends the information back to the client
* The client send json rest requests to the sever. These Controllers map to the request url and can handle
* either POST or GET requests. Once a mapping is found in one of the controllers the method is then executed
* and the results are sent back up to the client for display
* @author Isaac Parenteau
* @since 1.0.0-RELEASE
* @version 1.0
*/
package net.locusworks.portal.controllers;
@@ -0,0 +1,69 @@
package net.locusworks.portal.extendedlogger;
import net.locusworks.common.logger.ApplicationLogger;
import net.locusworks.common.logger.LogLevel;
/**
* Extended the portal logger class to for specialized loggers such as the leak prevent and jsch
* @author Isaac Parenteau
* @since 1.0.0-RELEASE
* @version 1.0
*/
public class ExtendedLogger extends ApplicationLogger implements se.jiderhamn.classloader.leak.prevention.Logger, com.jcraft.jsch.Logger {
public ExtendedLogger() {
this(ExtendedLogger.class.getSimpleName());
}
public ExtendedLogger(String name) {
super(name);
}
@Override
public void warn(Throwable arg0) {
super.warn(DEFAULT_FORMAT, getStackTrace(arg0.getStackTrace()));
}
@Override
public void error(Throwable arg0) {
super.error(DEFAULT_FORMAT, getStackTrace(arg0.getStackTrace()));
}
@Override
public boolean isEnabled(int level) {
switch(level) {
case com.jcraft.jsch.Logger.DEBUG:
return isDebugEnabled();
case com.jcraft.jsch.Logger.INFO:
return isInfoEnabled();
case com.jcraft.jsch.Logger.WARN:
return isWarnEnabled();
case com.jcraft.jsch.Logger.ERROR:
case com.jcraft.jsch.Logger.FATAL:
return isErrorEnabled();
default:
return false;
}
}
@Override
public void log(int level, String message) {
switch(level) {
case com.jcraft.jsch.Logger.DEBUG:
super.log(LogLevel.DEBUG, message);
break;
case com.jcraft.jsch.Logger.INFO:
super.log(LogLevel.INFO, message);
break;
case com.jcraft.jsch.Logger.WARN:
super.log(LogLevel.WARN, message);
break;
case com.jcraft.jsch.Logger.ERROR:
case com.jcraft.jsch.Logger.FATAL:
super.log(LogLevel.ERROR, message);
break;
default:
return;
}
}
}
@@ -0,0 +1,64 @@
package net.locusworks.portal.extendedlogger;
import net.locusworks.common.logger.ApplicationLogger;
import net.locusworks.common.logger.ApplicationLoggerFactory;
import net.locusworks.common.logger.LogLevel;
/**
* Extended portal logger factory to create ExtendedApplicationLoggers to be used
* in specialized areas that is outside the normal purview of the standard portal logger
* such as the leak preventer and JSch loggers. This adds the extended logger
* to the list of normal loggers so the user can see them and give them the ability
* to control their log levels
* @author Isaac Parenteau
* @since 1.0.0-RELEASE
* @version 1.0
*/
public class ExtendedLoggerFactory {
/**
* Get an extended portal logger
* @param name Name of the logger
* @return the extended portal logger
*/
public static ExtendedLogger getExtendedLogger(String name) {
ApplicationLogger logger = ApplicationLoggerFactory.getLogger(name);
if (!(logger instanceof ExtendedLogger)) {
logger = new ExtendedLogger(name);
ApplicationLoggerFactory.addLogger(logger);
}
return (ExtendedLogger)logger;
}
/**
* Get an extended portal logger
* @param clazz class to prepend its name to any message
* @return the extended portal logger
*/
public static ExtendedLogger getExtendedLogger(Class<?> clazz) {
return getExtendedLogger(clazz, true);
}
/**
* Get an extended portal logger
* @param clazz class to prepend its name to any message
* @param level The log level to set the logger to
* @return the extended portal logger
*/
public static ExtendedLogger getExtendedLogger(Class<?> clazz, LogLevel level) {
ExtendedLogger logger = getExtendedLogger(clazz, true);
logger.setLogLevel(level);
return logger;
}
/**
* Get the current logger
* @param clazz class to prepend its name to any message
* @param useSimpleName use the simple class name
* @return the extended portal logger
*/
public static ExtendedLogger getExtendedLogger(Class<?> clazz, boolean useSimpleName) {
return getExtendedLogger(useSimpleName ? clazz.getSimpleName() : clazz.getName());
}
}
@@ -0,0 +1,50 @@
package net.locusworks.portal.handlers;
import org.springframework.messaging.Message;
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import net.locusworks.common.logger.ApplicationLogger;
import net.locusworks.common.logger.ApplicationLoggerFactory;
import static net.locusworks.common.Charsets.UTF_8;
/**
* Abstract class to handle stomp messages
* @author Isaac Parenteau
* @since 1.0.0-RELEASE
* @version 1.0
*/
public abstract class AbstractObjectMessageHandler implements StompMessageHandler {
private String destination;
private static ApplicationLogger logger = ApplicationLoggerFactory.getLogger(AbstractObjectMessageHandler.class);
@Override
public void handleMessage(Message<byte[]> message) {
StompHeaderAccessor headers = StompHeaderAccessor.wrap(message);
if (!getDestination().equals(headers.getDestination())) {
logger.warn("%s.handleMessage() unexpected message: %s", getClass().getName(), headers.getDestination());
} else {
try {
String json = new String(message.getPayload(), UTF_8);
handleJson(json);
} catch (Exception e) {
logger.error(String.format("%s.handleMessage() error: %s", getClass().getName(), e.getMessage()), e);
}
}
}
@Override
public String getDestination() {
return destination;
}
@Override
public void setDestination(String destination) {
this.destination = destination;
}
public abstract void handleJson(String json) throws Exception;
}
@@ -0,0 +1,78 @@
package net.locusworks.portal.handlers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.messaging.simp.broker.BrokerAvailabilityEvent;
import org.springframework.stereotype.Component;
import net.locusworks.common.logger.ApplicationLogger;
import net.locusworks.common.logger.ApplicationLoggerFactory;
import net.locusworks.portal.common.http.HttpClientHelper;
import net.locusworks.portal.services.SchedulerService;
import net.locusworks.portal.services.WebCommService;
import net.locusworks.portal.services.CommService.EndPoint;
/**
* Component to handle broker availability events
* @author Isaac Parenteau
* @since 1.0.0-RELEASE
* @version 1.0
*/
@Component
public class BrokerAvailabilityHandler implements ApplicationListener<BrokerAvailabilityEvent> {
private static final int MAX_ATTEMPTS = 30;
private StompSubscriber stompThread;
private static ApplicationLogger logger = ApplicationLoggerFactory.getLogger(BrokerAvailabilityHandler.class);
@Autowired
private SchedulerService scheduler;
@Override
public void onApplicationEvent(BrokerAvailabilityEvent event) {
try {
if (event.isBrokerAvailable() && (stompThread == null)) {
// this event happens before our webapp is ready to accept connections. Upon server startup
//Create a new stop thread to listen to events
stompThread = new StompSubscriber();
scheduler.schedule(stompThread);
}
} catch (Exception e) {
logger.error(e);
}
}
/**
* Helper class that sets up the stomp subscriber for the server
* @author Isaac Parenteau
* @since 1.0.0-RELEASE
* @version 1.0
*/
private static class StompSubscriber implements Runnable {
@Override
public void run() {
EndPoint endPoint = WebCommService.getServerEndPoints().get(0);
int responseCode = 0;
try {
for (int attempts = 0; attempts < MAX_ATTEMPTS; attempts++) {
Thread.sleep(2000);
logger.info("** Attempting to reach stomp endpoint **");
HttpClientHelper client = new HttpClientHelper(endPoint.protocol, endPoint.host, endPoint.port);
responseCode = client.getGetResponseCode("/portal/ws/stomp/info");
if (responseCode == 200) {
// subscribe to triggers generated by systems
WebCommService.subscribeToTopic(new SystemTriggerMessageHandler());
break;
}
}
if (responseCode != 200) {
logger.warn("Max attempts reached while trying to establish stomp connection. Aborting");
}
} catch (Exception e) {
logger.error(e);
}
}
}
}
@@ -0,0 +1,97 @@
package net.locusworks.portal.handlers;
import static net.locusworks.portal.common.utils.Constants.PROPERTIES_FILE;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import net.locusworks.common.configuration.PropertiesManager;
import net.locusworks.common.logger.ApplicationLogger;
import net.locusworks.common.logger.ApplicationLoggerFactory;
import net.locusworks.common.logger.ApplicationLoggerFactory.LoggerInfo;
import net.locusworks.common.logger.ApplicationLoggerInitializer;
import net.locusworks.common.logger.LogLevel;
import net.locusworks.common.properties.OrderedProperties;
import net.locusworks.portal.common.enums.Configuration;
/**
* Class to initialize the application logger service.
* Gets the log level from the properties file. Normally this is done
* through the configuration service but the configuration service uses
* the Application logger which would cause circular dependency
* @author Isaac Parenteau
* @since 1.0.0-RELEASE
* @version 1.0
*/
public class LoggerInitializer implements ApplicationLoggerInitializer {
private static final String LOGGER_FILE = "portal-loggers.properties";
@Override
public void loadLoggers() {
File loggerFile = new File(String.format("%s/conf/%s", System.getProperty("catalina.base"), LOGGER_FILE));
if (!loggerFile.exists()) {
return;
}
try {
Properties conf = PropertiesManager.loadConfiguration(loggerFile);
List<LoggerInfo> loggerInfo = conf.entrySet()
.stream()
.map(item -> new LoggerInfo(String.valueOf(item.getKey()), String.valueOf(item.getValue())))
.sorted((item1, item2) -> item1.getLogger().compareTo(item2.getLogger()))
.collect(Collectors.toList());
ApplicationLoggerFactory.addLoggers(loggerInfo);
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
@Override
public void saveLogLevels(Map<String, ApplicationLogger> loggers) {
OrderedProperties conf = new OrderedProperties();
File loggerFile = new File(String.format("%s/conf/%s", System.getProperty("catalina.base"), LOGGER_FILE));
loggers.entrySet()
.stream()
.filter(item -> item.getKey() != null && item.getValue() != null)
.map(item -> item.getValue())
.filter(item -> item.getLogLevel() != null)
.sorted((item1, item2) -> item1.getName().compareTo(item2.getName()))
.forEach(item -> conf.setProperty(item.getName(), item.getLogLevel().toName()));
try(FileOutputStream fos = new FileOutputStream(loggerFile)) {
conf.store(fos, "Portal Logger Levels");
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
@Override
public LogLevel initialize() {
try {
Properties defaults = PropertiesManager.loadConfiguration(this.getClass(), PROPERTIES_FILE);
File patchRepoConfFile = new File(String.format("%s/conf/%s", System.getProperty("catalina.base"), PROPERTIES_FILE));
Properties conf = PropertiesManager.loadConfiguration(patchRepoConfFile);
String logLevel = conf.getProperty(Configuration.LOG_LEVEL.getValue());
if (StringUtils.isBlank(logLevel)) {
logLevel = defaults.getProperty(Configuration.LOG_LEVEL.getValue());
}
if (StringUtils.isBlank(logLevel)) {
throw new RuntimeException("Unable to get logger properties from properties file");
}
return LogLevel.valueOf(logLevel);
} catch (Exception ex) {
throw new RuntimeException("Unable to get properites: ", ex);
}
}
}
@@ -0,0 +1,32 @@
package net.locusworks.portal.handlers;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;
import net.locusworks.common.objectmapper.ObjectMapperHelper;
import net.locusworks.common.utils.Success;
/**
* Component to handle failed authentications
* @author Isaac Parenteau
* @since 1.0.0-RELEASE
* @version 1.0
*/
@Component
public class PortalAuthenticationFailureHandler implements AuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request,
HttpServletResponse response, AuthenticationException exception) throws IOException {
// return a json success message
String json = ObjectMapperHelper.writeValue(Success.fail()).getResults();
response.getWriter().write(json);
response.getWriter().flush();
response.getWriter().close();
}
}
@@ -0,0 +1,36 @@
package net.locusworks.portal.handlers;
import java.io.IOException;
import java.util.HashMap;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import net.locusworks.common.objectmapper.ObjectMapperHelper;
/**
* Component to handle success authentications
* @author Isaac Parenteau
* @since 1.0.0-RELEASE
* @version 1.0
*/
@Component
public class PortalAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response, Authentication authentication) throws IOException {
// return a json success message
HashMap<String, Object> success = new HashMap<>();
success.put("success", true);
// see CustomAuthenticationProvider for why the next line is a string - it can be anything
success.put("username", (String)((UsernamePasswordAuthenticationToken)authentication).getPrincipal());
response.getWriter().write(ObjectMapperHelper.writeValue(success).getResults());
response.getWriter().flush();
response.getWriter().close();
}
}
@@ -0,0 +1,41 @@
package net.locusworks.portal.handlers;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
import net.locusworks.common.logger.ApplicationLogger;
import net.locusworks.common.logger.ApplicationLoggerFactory;
/**
* Component to handle logout attempts
* @author Isaac Parenteau
* @since 1.0.0-RELEASE
* @version 1.0
*/
public class PortalLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler implements LogoutSuccessHandler {
private static ApplicationLogger logger = ApplicationLoggerFactory.getLogger(PortalLogoutSuccessHandler.class);
@Override
public void onLogoutSuccess(HttpServletRequest hsr, HttpServletResponse hsr1, Authentication a) throws IOException, ServletException {
try {
SecurityContextHolder.clearContext();
HttpSession session = hsr.getSession(false);
if (session != null) {
session.invalidate();
}
logger.info(a.getName() + " logged out");
} catch (Exception ex) {
logger.error(a.getName() + " log out failed", ex);
}
}
}
@@ -0,0 +1,88 @@
package net.locusworks.portal.handlers;
import java.io.IOException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
import net.locusworks.common.logger.ApplicationLogger;
import net.locusworks.common.logger.ApplicationLoggerFactory;
import net.locusworks.portal.common.enums.TriggerType;
import net.locusworks.portal.common.services.AESService;
import net.locusworks.portal.common.services.ConfigurationService;
import net.locusworks.portal.common.triggers.AccountTrigger;
import net.locusworks.portal.common.triggers.SeedTrigger;
import net.locusworks.portal.services.ApplicationLogService;
import net.locusworks.portal.services.CommService;
import net.locusworks.portal.services.CredentialService;
import net.locusworks.portal.services.SystemService;
/**
* Handler to trigger when aes seed has been updated.
* This will go through the various services to update the encryption values
* @author Isaac Parenteau
* @since 1.0.0-RELEASE
* @version 1.0
*/
@Component
public class SeedUpdatedHandler implements ApplicationListener<AccountTrigger>{
private static ApplicationLogger logger = ApplicationLoggerFactory.getLogger(SeedUpdatedHandler.class);
@Autowired
private CredentialService credService;
@Autowired
private SystemService sysService;
@Autowired
private ConfigurationService confService;
@Autowired
private AESService aesService;
@Autowired
private ApplicationLogService appLogService;
@Override
public void onApplicationEvent(AccountTrigger event) {
if (event.getType() != TriggerType.SEED_UPDATED) return;
String seed = String.valueOf(event.getResources().get(0));
try {
aesService.saveSeed(seed);
} catch (IOException e) {
String error = String.format("Unable to save new seed: %s", e.getMessage());
appLogService.addExceptionEntry(error);
logger.error(error, e);
return;
}
appLogService.addGenericLogEntry("Updating encryption keys");
try {
credService.updateEncryptedCredentials(seed);
} catch (Exception ex) {
logger.error(ex);
appLogService.addExceptionEntry("Unable to update credential service keys");
}
try {
sysService.updateEncryptedCredentials(seed);
} catch (Exception ex) {
logger.error(ex);
appLogService.addExceptionEntry("Unable to updated system service keys");
}
try {
confService.updateEncryption(seed);
} catch (Exception ex) {
logger.error(ex);
appLogService.addExceptionEntry("Unable to update configuration service keys");
}
appLogService.addGenericLogEntry("Finished updating encryption keys");
CommService.sendEvent(new SeedTrigger(seed));
}
}
@@ -0,0 +1,30 @@
package net.locusworks.portal.handlers;
import org.springframework.messaging.Message;
/**
* Interface for handling stomp messages
* @author Isaac Parenteau
* @since 1.0.0-RELEASE
* @version 1.0
*/
public interface StompMessageHandler {
/**
* Handle messages to be sent via stomp messages
* @param message Message to be sent
*/
void handleMessage(Message<byte[]> message);
/**
* Get the destination for the message
* @return the message destination
*/
String getDestination();
/**
* Set the destination
* @param destination destination to set
*/
void setDestination(String destination);
}
@@ -0,0 +1,30 @@
package net.locusworks.portal.handlers;
import net.locusworks.common.logger.ApplicationLogger;
import net.locusworks.common.logger.ApplicationLoggerFactory;
import net.locusworks.portal.handlers.AbstractObjectMessageHandler;
import net.locusworks.portal.services.CommService;
/**
* Handler for system trigger event
* @author Isaac Parenteau
* @since 1.0.0-RELEASE
* @version 1.0
*/
public class SystemTriggerMessageHandler extends AbstractObjectMessageHandler {
private static ApplicationLogger logger = ApplicationLoggerFactory.getLogger(SystemTriggerMessageHandler.class);
/**
* Constructor
* Sets the destination to be the global system topic
*/
public SystemTriggerMessageHandler() {
setDestination(CommService.getStompAccountTopic(CommService.STOMP_SYSTEM_TOPIC_SUFFIX));
}
@Override
public void handleJson(String json) throws Exception {
logger.debug("%n%s", json);
}
}
@@ -0,0 +1,299 @@
package net.locusworks.portal.handlers;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.springframework.messaging.Message;
import org.springframework.messaging.simp.stomp.BufferingStompDecoder;
import org.springframework.messaging.simp.stomp.StompCommand;
import org.springframework.messaging.simp.stomp.StompDecoder;
import org.springframework.messaging.simp.stomp.StompEncoder;
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.client.standard.StandardWebSocketClient;
import org.springframework.web.socket.handler.AbstractWebSocketHandler;
import org.springframework.web.socket.sockjs.client.SockJsClient;
import org.springframework.web.socket.sockjs.client.Transport;
import org.springframework.web.socket.sockjs.client.WebSocketTransport;
import net.locusworks.common.logger.ApplicationLogger;
import net.locusworks.common.logger.ApplicationLoggerFactory;
import net.locusworks.portal.common.http.HttpClientHelper.HttpSchema;
import net.locusworks.portal.common.services.ConfigurationService;
import net.locusworks.portal.handlers.StompMessageHandler;
import net.locusworks.portal.services.WebCommService;
import net.locusworks.portal.services.CommService.EndPoint;
import net.locusworks.portal.util.ApplicationContextHolder;
import static net.locusworks.portal.common.utils.Constants.TRUST_STORE_PASSWORD;
import static net.locusworks.portal.common.utils.Constants.TRUST_STORE_PATH;
import static net.locusworks.common.Charsets.UTF_8;
/**
* Sets up to the Web socket handler
* @author Isaac Parenteau
* @since 1.0.0-RELEASE
* @version 1.0
*/
public class SystemWebSocketHandler extends AbstractWebSocketHandler {
private static ApplicationLogger logger = ApplicationLoggerFactory.getLogger(SystemWebSocketHandler.class);
private final StompEncoder encoder = new StompEncoder();
private StompDecoder stompDecoder = null;
private BufferingStompDecoder decoder = null;
// this map holds the destinations mapped to the message handlers
private final Map<String, StompMessageHandler> handlerMap = new HashMap<>();
// this map allows us to look up the appropriate message handler based on the
// subscriptionId in the message header
private final Map<String, StompMessageHandler> subIdHandlerMap = new HashMap<>();
private final Map<String, String> topicToSubId = new HashMap<>();
private boolean connected = false;
private boolean connecting = false;
private WebSocketSession session;
private int subCounter = 0;
/**
* add a subscription, connecting if we need to
* @param handler the subscription handler
* @throws Exception exception
*/
public void addSubscription(StompMessageHandler handler) throws Exception {
synchronized(handler) {
String destination = handler.getDestination();
handlerMap.put(destination, handler);
// if not connected, make the connection, and if successful all the
// known subscriptions will be registered
if (!connected && !connecting) {
connect();
} else if (connected) { // already connected, skip right to subscription
subscribe(destination);
}
}
}
// This is the call back for the SockJsClient after it makes its handshake.
// we must send a CONNECT message post handshake
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
this.session = session;
logger.info("Websocket connection");
// connect?
StompHeaderAccessor headers1 = StompHeaderAccessor.create(StompCommand.CONNECT);
headers1.setAcceptVersion("1.1,1.0");
headers1.setHeartbeat(0, 0);
sendEmptyMessage(headers1);
}
// Called when we loose our connection, try to re-establish
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
this.session = session;
logger.info("Websocket closed: " + closeStatus.getReason());
connected = false;
connecting = false;
subCounter = 0;
if (!closeStatus.equalsCode(CloseStatus.GOING_AWAY) ) {
logger.info("Websocket attempt re-connect");
connect();
}
}
/**
* Use the Spring SockJsClient to connect to ourself, we become a STOMP client
* to our sockJsClient-> to this instance of an AbstractWebSocketHandler. Successful incoming messages
* will be delegated to the registered StompMessageHandler for the subscription
* @throws Exception exception
*/
private void connect() throws Exception {
connecting = true;
stompDecoder = new StompDecoder();
decoder = new BufferingStompDecoder(stompDecoder, 64 * 1024);
try {
List<Transport> transports = new ArrayList<>();
EndPoint endPoint = WebCommService.getServerEndPoints().get(0);
HttpSchema schema = HttpSchema.findEnum(endPoint.protocol);
StandardWebSocketClient swsc = new StandardWebSocketClient();
if (schema == HttpSchema.HTTPS) {
ConfigurationService service = ApplicationContextHolder.getContext().getBean(ConfigurationService.class);
Properties props = System.getProperties();
props.setProperty(TRUST_STORE_PATH, service.getCaCertFile());
props.setProperty(TRUST_STORE_PASSWORD, service.getCaCertPassword());
System.setProperties(props);
}
transports.add(new WebSocketTransport(swsc));
String endpoint = String.format("%s://%s:%s/%s", schema == HttpSchema.HTTPS ? "wss" : "ws",
endPoint.host, endPoint.port, "portal/ws/stomp");
SockJsClient sockJsClient = new SockJsClient(transports);
sockJsClient.doHandshake(this, endpoint);
} catch (Exception ex) {
logger.error("Unable to connect to stomp socket " + ex.getMessage(), ex);
}
}
/**
* Use the given headers subscriptionId to locate the corresponding message handler
* @param headers Stomp header
* @return the stomp message handler
* @throws Exception exception
*/
private StompMessageHandler getHandler(StompHeaderAccessor headers) throws Exception {
StompMessageHandler handler = subIdHandlerMap.get(headers.getSubscriptionId());
if (handler == null) {
throw new Exception("No handler defined for " + headers.getSubscriptionId());
}
return handler;
}
// Yes, we handle partials, see BufferingStompDecoder
@Override
public boolean supportsPartialMessages() {
return true;
}
@Override
public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
this.session = session;
connecting = false;
logger.error("Transport error", exception);
}
// The sockJsClient will call this method with incoming messeges.
@Override
public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
this.session = session;
// handle incoming messages, do we need to handle any other type of messages?
// we are strictly SockJs....so, I don't think so
if (message instanceof TextMessage) {
TextMessage textMessage = (TextMessage)message;
ByteBuffer payload = ByteBuffer.wrap(textMessage.getPayload().getBytes(UTF_8));
List<Message<byte[]>> messages = decoder.decode(payload);
messages
.stream()
.map(incoming -> StompHeaderAccessor.wrap(incoming))
.filter(incomingHeader -> StompCommand.CONNECTED.equals(incomingHeader.getCommand()))
.forEach(item -> {
connected = true;
connecting = false;
handlerMap.keySet().forEach(dest -> subscribe(dest));
});
for (Message<byte[]> incoming : messages) {
StompHeaderAccessor incomingHeaders = StompHeaderAccessor.wrap(incoming);
// we're connected, lets subscribe to topics konwn at this point, anyone
// can subscribe after this point
if (StompCommand.CONNECTED.equals(incomingHeaders.getCommand())) {
connected = true;
connecting = false;
for (String destination : handlerMap.keySet()) {
subscribe(destination);
}
} else if (StompCommand.MESSAGE.equals(incomingHeaders.getCommand())) {
// get the subscription id and delegate the message to the supplied handler
getHandler(incomingHeaders).handleMessage(incoming);
}
}
}
}
/**
* Caution!!! This does not seem to play well with concurrent messages
* this class should be used as a listener only, sends should go out via
* SimpMessagingTemplate for now.
* @param destination destination to send the message to
* @param payload the data to send
* @throws Exception exception to be thrown
*/
public void sendMessage(String destination, String payload) throws Exception {
StompHeaderAccessor headers = StompHeaderAccessor.create(StompCommand.SEND);
headers.setDestination(destination);
send(headers, payload);
}
/**
* Send a message with no body, CONNECT, SUBSCRIBE, ETC
* @param headers the stomp headers to include into the blank message
* @throws Exception exception
*/
private void sendEmptyMessage(StompHeaderAccessor headers) throws Exception {
send(headers, "");
}
/**
* Send a stomp message
* @param headers stomp header
* @param payload payload to send
* @throws Exception exception
*/
private void send(StompHeaderAccessor headers, String payload) throws Exception {
Message<byte[]> byteMessage = MessageBuilder.withPayload(payload.getBytes(UTF_8)).setHeaders(headers).build();
byte[] bytes = encoder.encode(byteMessage);
Map<String, String> message = new LinkedHashMap<>();
message.put("Sending message", String.valueOf(headers.getCommand()));
message.put("Destination", headers.getDestination());
message.put("SubId", headers.getSubscriptionId());
logger.info(message, new StringBuilder("[Stomp Event]"));
session.sendMessage(new TextMessage(new String(bytes, UTF_8)));
}
/**
* Subscribe to a destination topic and stash the subscriptionId
* @param destination destination of the message
* @throws Exception exception
*/
private void subscribe(String destination) {
try {
logger.info("Subscribing...");
StompHeaderAccessor headers = StompHeaderAccessor.create(StompCommand.SUBSCRIBE);
subCounter = subCounter + 1;
String subId = "sub-id-" + subCounter;
headers.setSubscriptionId(subId);
headers.setDestination(destination);
sendEmptyMessage(headers);
// keep track of our handlers by subscriptionId
subIdHandlerMap.put(subId, handlerMap.get(destination));
topicToSubId.put(destination, subId);
} catch (Exception e) {
logger.error(String.format("Unable to subscrib to topic %s: %s", destination, e.getMessage()), e);
}
}
/**
* Unsubscribe from a topic
* @param destination the topic destination to unsubscribe from
*/
public void unsubscribe(String destination) {
try {
if (connected) {
StompHeaderAccessor headers = StompHeaderAccessor.create(StompCommand.SEND);
headers.setDestination(destination);
sendEmptyMessage(headers);
}
} catch (Exception e) {
logger.error("Error unsubscribing", e);
}
// remove the handler
handlerMap.remove(destination);
subIdHandlerMap.remove(topicToSubId.get(destination));
}
}
@@ -0,0 +1,7 @@
package net.locusworks.portal.handlers;
public interface TaskSchedulerHandler {
public void runTask();
}
@@ -0,0 +1,53 @@
package net.locusworks.portal.providers;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.stereotype.Component;
import net.locusworks.portal.common.triggers.AccountTrigger;
import net.locusworks.portal.common.triggers.SeedTrigger;
/**
* Component Class to handle publishing trigger events to allow for listeners to
* intercept and act on the data
* @author Isaac Parenteau
* @since 1.0.0-RELEASE
* @version 1.0
*/
@Component(ApplicationEventProvider.EVENT_PROVIDER_NAME)
public class ApplicationEventProvider implements ApplicationEventPublisherAware {
public static final String EVENT_PROVIDER_NAME ="applicationEventProvider";
private ApplicationEventPublisher publisher;
/**
* Publish an account trigger to the system
* @param trigger AccountTrigger to publish to the system
*/
public void publishAccountTrigger(AccountTrigger trigger) {
publishEvent(trigger);
}
/**
* Publish a seed trigger to the system
* @param trigger SeedTrigger to publish to the system
*/
public void publishSeedTrigger(SeedTrigger trigger) {
publishEvent(trigger);
}
/**
* Publish an ApplicationEvent to the system
* @param event the ApplicationEvent to publish
*/
public void publishEvent(ApplicationEvent event) {
publisher.publishEvent(event);
}
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
publisher = applicationEventPublisher;
}
}
@@ -0,0 +1,72 @@
package net.locusworks.portal.providers;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AccountExpiredException;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.stereotype.Component;
import net.locusworks.common.utils.Constants;
import net.locusworks.portal.database.entities.ApplicationUser;
import net.locusworks.portal.database.repos.PermissionGroupPermissionRepository;
import net.locusworks.portal.services.UserSecurityService;
/**
* Component that handles authentication and its token
* @author Isaac Parenteau
* @since 1.0.0-RELEASE
* @version 1.0
*
*/
@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
@Autowired
private UserSecurityService userSecurityService;
@Autowired
private PermissionGroupPermissionRepository pgpRepo;
@Override
public UsernamePasswordAuthenticationToken authenticate(Authentication authentication) throws AuthenticationException {
return null;
}
/**
* Authenticate a user with the server
* @param user The application user to authenticate
* @return authentication token
* @throws Exception generation exception if authentication fails
*/
public UsernamePasswordAuthenticationToken authenticate(ApplicationUser user) throws Exception {
if (user == null || !userSecurityService.isActive(user)) {
throw new AccountExpiredException("account not active");
}
// add permissions as granted authorities
List<GrantedAuthority> grantedAuths = pgpRepo.findByApplicationUser(user)
.stream()
.filter(pgp -> pgp.getAllowed() == Constants.TRUE)
.map(pgp -> new SimpleGrantedAuthority(pgp.getPermission().getName()))
.collect(Collectors.toList());
// lookup the customer
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(user.getEmailAddressTxt(),
userSecurityService.findUserPassword(user), grantedAuths);
token.setDetails(user);
return token;
}
@Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
@@ -0,0 +1,572 @@
package net.locusworks.portal.services;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import net.locusworks.common.logger.LogLevel;
import net.locusworks.common.utils.Utils;
import net.locusworks.portal.common.enums.EventType;
import net.locusworks.portal.common.enums.TriggerType;
import net.locusworks.portal.common.utils.DateService;
import net.locusworks.portal.database.entities.ApplicationLog;
import net.locusworks.portal.database.repos.ApplicationLogRepository;
import net.locusworks.portal.json.ApplicationLogRequest;
import net.locusworks.portal.json.ApplicationUserRequest;
import net.locusworks.portal.json.PurgeLogRequest;
import net.locusworks.common.logger.ApplicationLogger;
import net.locusworks.common.logger.ApplicationLoggerFactory;
/**
* Service that adds certain logs to the log database for viewing
* @author Isaac Parenteau
* @since 1.0.0-RELEASE
* @version 1.0
*/
@Service
public class ApplicationLogService {
private static ApplicationLogger logger = ApplicationLoggerFactory.getLogger(ApplicationLogService.class);
private static final String SYS_USER = "SYSTEM";
@Autowired
private ApplicationLogRepository appLogRepo;
@Autowired
private UserSecurityService userSecurityService;
/**
* Add a log entry stating the logs were purged between two dates
* Saves a log message of who purged the logs
* @param startDate the start date in which to start the purge at
* @param endDate the end date to purge the logs to
*/
public void addPurgedLogs(Date startDate, Date endDate) {
addLogEntry(EventType.LOGS_PURGED, String.format("Logs between %s and %s were purged",
DateService.defaultFormat(DateService.toMidnight(startDate)), DateService.defaultFormat(DateService.toMidnight(endDate))), LogLevel.INFO);
}
/**
* Add a log entry stating who just logged into the system
*/
public void addLoginEntry() {
addLogEntry(EventType.LOGIN, String.format("Logged in"), LogLevel.INFO);
}
/**
* Add a log entry stating a user logged out
* @param userLoggedOut the name of the user who logged out
*/
public void addLogoutEntry(String userLoggedOut) {
addLogEntry(EventType.LOGOUT, String.format("User %s logged out", userLoggedOut), LogLevel.INFO);
}
/**
* Add a log entry stating a user was added
* @param newUser name of the new user
* @param role role given to the new user
*/
public void addUserAddedEntry(String newUser, String role) {
addLogEntry(EventType.USER_ADDED, String.format("Created %s with role %s", newUser, role),
LogLevel.INFO);
}
/**
* Add a log entry stating a user was edited
* @param newUser name of the new user
*/
public void addUserEditedEntry(String newUser) {
addLogEntry(EventType.USER_EDITED, String.format("Edited %s", newUser),
LogLevel.INFO);
}
/**
* Add a log entry stating a user was deleted
* @param newUser name of hte new user
*/
public void addUserDeletedEntry(String newUser) {
addLogEntry(EventType.USER_DELETED, String.format("Deleted %s", newUser),
LogLevel.INFO);
}
/**
* Add a log entry stating a configuration value changed
* @param confValue name of the configuration value changed.
* @param from old configuration value
* @param to new configuration value
*/
public void addConfigurationUpdatedEntry(String confValue, String from, String to) {
String message = String.format("Changed hash type %s from %s to %s", confValue, from, to);
addLogEntry(EventType.CONFIGURATION_UPDATED, message, LogLevel.INFO);
}
/**
* Add a log entry stating a permission group was changed
* @param permissionGroup name of the permission group that was changed
*/
public void addPermissionsChangedEntry(String permissionGroup) {
String message = String.format("Permission group %s changed", permissionGroup);
addLogEntry(EventType.PERMISSIONS_CHANGED, message, LogLevel.INFO);
}
/**
* Add log entry stating a permission group was deleted
* @param permissionGroup name of the permission group that was deleted
*/
public void addPermissionsDeletedEntry(String permissionGroup) {
String message = String.format("Permission group %s deleted", permissionGroup);
addLogEntry(EventType.PERMISSIONS_DELETED, message, LogLevel.INFO);
}
/**
* Add a log entry stating a directory permission was changed
* @param directory name of the directory that was changed
*/
public void addDirectoryPermissionsChangedEntry(String directory) {
String message = String.format("Directory permissions were changed for directory %s", directory);
addLogEntry(EventType.DIRECTORY_PERMISSIONS_CHANGED, message, LogLevel.INFO);
}
/**
* Add a log entry stating a directory permission was removed
* @param directory name of the directory
*/
public void addDirectoryPermissionsRemovedEntry(String directory) {
String message = String.format("Directory permissions were removed for directory %s", directory);
addLogEntry(EventType.DIRECTORY_PERMISSIONS_REMOVED, message, LogLevel.INFO);
}
/**
* Add a log entry stating a script was added
* @param scriptName the name of the script added
*/
public void addScriptAddedEntry(String scriptName) {
addLogEntry(EventType.SCRIPT_ADDED, String.format("Created script %s", scriptName), LogLevel.INFO);
}
/**
* Add a log entry stating a script was edited
* @param scriptName the name of the script being edited
*/
public void addScriptEditedEntry(String scriptName) {
addLogEntry(EventType.SCRIPT_EDITED, String.format("Edited script %s", scriptName), LogLevel.INFO);
}
/**
* Add a log entry stating a script schedule was deleted
* @param scheduleUUID The job schedule id (uuid) of the schedule to be deleted
*/
public void addScriptScheduleDeletedEntry(String scheduleUUID) {
addLogEntry(EventType.SCRIPT_SCHEDULE_DELETED, String.format("Deleted script schedule with uuid of %s", scheduleUUID), LogLevel.INFO);
}
/**
* Add a log entry stating a script was deleted
* @param scriptName the name of the script deleted
*/
public void addScriptDeletedEntry(String scriptName) {
addLogEntry(EventType.SCRIPT_DELETED,
String.format("Deleted script %s", scriptName), LogLevel.INFO);
}
/**
* Add a log entry stating an environment variable was added
* @param envVar the name of the variable added
*/
public void addEnvVarAddedEntry(String envVar) {
addLogEntry(EventType.ENVVAR_ADDED,
String.format("Added environmenv variable %s", envVar), LogLevel.INFO);
}
/**
* Add a log entry stating an environment variable was edited
* @param envVar the name of the variable edited
*/
public void addEnvVarEditedEntry(String envVar) {
addLogEntry(EventType.ENVVAR_EDITED,
String.format("Edited environmenv variable %s", envVar), LogLevel.INFO);
}
/**
* Add a log entry stating an environment variable was deleted
* @param envVar the name of the variable deleted
*/
public void addEnvVarDeletedEntry(String envVar) {
addLogEntry(EventType.ENVVAR_DELETED,
String.format("Deleted environmenv variable %s", envVar), LogLevel.INFO);
}
/**
* Add a log entry stating that credentials were added
* @param credentialName the name of the credentials added
* @param credentialType the type of credentials added
*/
public void addCredentialsAddedEntry(String credentialName, String credentialType) {
addLogEntry(EventType.CREDENTIALS_ADDED,
String.format("Added credential %s as type %s", credentialName, credentialType), LogLevel.INFO);
}
/**
* Add a log entry stating a credential was deleted
* @param credentialName name of the credentials deleted
*/
public void addCredentialsDeletedEntry(String credentialName) {
addLogEntry(EventType.CREDENTIALS_REMOVED, String.format("Removed credential %s", credentialName), LogLevel.INFO);
}
/**
* Add a log entry stating a source control repository was added
* @param repoName the name of the repository added
*/
public void addRepositoryAddedEntry(String repoName) {
addLogEntry(EventType.REPOSITORY_ADDED, String.format("Added repository %s", repoName), LogLevel.INFO);
}
/**
* Add a log entry stating a pull request was issued
* @param repoName name of the repository the pull request was against
*/
public void addRepositoryPullEntry(String repoName) {
addLogEntry(EventType.REPOSITORY_PULL, String.format("Manual pull request for repository %s issued", repoName), LogLevel.INFO);
}
/**
* Add a log entry stating a repository was removed
* @param repoName name of the repository removed
*/
public void addRepositoryRemovedEntry(String repoName) {
addLogEntry(EventType.REPOSITORY_DELETED, String.format("Deleted repository %s", repoName), LogLevel.INFO);
}
/**
* Add a log entry stating when a script started execution
* @param scriptName name of the script that started
* @param date the date the script was started
*/
public void addScriptRunEntry(String scriptName, Date date) {
addLogEntry(EventType.SCRIPT_RUN,
String.format("Script %s executed at %s", scriptName, DateService.expandedFormat(date)),
LogLevel.INFO);
}
/**
* Add a log entry stating when a script finished execution
* @param scriptName the name of script that finished
* @param date the date/time the script finished running
* @param status the final status of the script execution
*/
public void addScriptEndEntry(String scriptName, Date date, Integer status) {
addLogEntry(EventType.SCRIPT_END,
String.format("Script %s ended at %s with a status of %d", scriptName, DateService.expandedFormat(date), status),
LogLevel.INFO);
}
/**
* Add a log entry stating there was an error when executing a script
* @param scriptName name of the script that had the error
* @param error the error message
* @param date Date/time of the error
* @param status exit status of the script
*/
public void addScriptErrorEntry(String scriptName, String error, Date date, Short status) {
addLogEntry(EventType.SCRIPT_ERROR,
String.format("Script %s ended at %s with status %d -> %s", scriptName, DateService.expandedFormat(date), status, error),
LogLevel.ERROR);
}
/**
* Add a log entry stating when a ssh command started execution
* @param scriptName name of the ssh command that started
* @param date the date the ssh was started
*/
public void addSshRunEntry(String scriptName, Date date) {
addLogEntry(EventType.SSH_RUN,
String.format("SSH command %s executed at %s", scriptName, DateService.expandedFormat(date)),
LogLevel.INFO);
}
/**
* Add a log entry stating when a ssh command finished execution
* @param scriptName the name of ssh command that finished
* @param date the date/time the ssh command finished running
* @param status the final status of the ssh command execution
*/
public void addSshEndEntry(String scriptName, Date date, Integer status) {
addLogEntry(EventType.SSH_END,
String.format("SSH command %s ended at %s with status %d", scriptName, DateService.expandedFormat(date), status),
LogLevel.INFO);
}
/**
* Add a log entry stating there was an error when executing a ssh command
* @param scriptName name of the ssh command that had the error
* @param error the error message
* @param date Date/time of the error
* @param status exit status of the ssh command
*/
public void addSshErrorEntry(String scriptName, String error, Date date, Short status) {
addLogEntry(EventType.SSH_ERROR,
String.format("SSH command %s ended at %s with status %d -> %s", scriptName, DateService.expandedFormat(date), status, error),
LogLevel.ERROR);
}
/**
* Add a log entry stating when a scp command started execution
* @param scriptName name of the scp command that started
* @param date the date the scp was started
*/
public void addScpRunEntry(String scriptName, Date date) {
addLogEntry(EventType.SCP_RUN,
String.format("SCP command %s executed at %s", scriptName, DateService.expandedFormat(date)),
LogLevel.INFO);
}
/**
* Add a log entry stating when a scp command finished execution
* @param scriptName the name of scp command that finished
* @param date the date/time the scp command finished running
* @param status the final status of the scp command execution
*/
public void addScpEndEntry(String scriptName, Date date, Integer status) {
addLogEntry(EventType.SCP_END,
String.format("SCP command %s ended at %s with status %d", scriptName, DateService.expandedFormat(date), status),
LogLevel.INFO);
}
/**
* Add a log entry stating there was an error when executing a scp command
* @param scriptName name of the scp command that had the error
* @param error the error message
* @param date Date/time of the error
* @param status exit status of the scp command
*/
public void addScpErrorEntry(String scriptName, String error, Date date, Short status) {
addLogEntry(EventType.SCP_ERROR,
String.format("SCP command %s ended at %s with status %d -> %s", scriptName, DateService.expandedFormat(date), status, error),
LogLevel.ERROR);
}
/**
* Add a log entry stating a file was downloaded from the server
* @param fileName name of the file
*/
public void addFileDownloadEntry(String fileName) {
String log = String.format("Downloaded File %s", fileName);
addLogEntry(EventType.FILE_DOWNLOADED, log, LogLevel.INFO);
}
/**
* Add a log entry stating a file was deleted from the server
* @param fileName name of the file deleted
*/
public void addFileDeletedEntry(String fileName) {
addLogEntry(EventType.FILE_DELETED, String.format("File %s deleted", fileName),
LogLevel.INFO);
}
/**
* Add a log entry stating an email template was added to the server
* @param templateName the name of the email template added
*/
public void addEmailTemplateAddedEntry(String templateName) {
addLogEntry(EventType.EMAIL_TEMPLATED_ADDED, String.format("Email template %s was added", templateName),
LogLevel.INFO);
}
/**
* Add a log entry stating an email template was edited
* @param templateName the name of the email template edited
*/
public void addEmailTemplateEditedEntry(String templateName) {
addLogEntry(EventType.EMAIL_TEMPLATED_EDITED, String.format("Email template %s was edited", templateName),
LogLevel.INFO);
}
/**
* Add a log entry stating an email template was deleted from the server
* @param templateName the name of the email template deleted
*/
public void addEmailTemplateDeletedEntry(String templateName) {
addLogEntry(EventType.EMAIL_TEMPLATED_DELETED, String.format("Email template %s was deleted", templateName),
LogLevel.INFO);
}
/**
* Add a log entry stating the smtp configuration was changed
*/
public void addSMTPConfigChangeEntry() {
addLogEntry(EventType.SMTP_CONFIG_CHANGED, "SMTP configuration modified", LogLevel.INFO);
}
/**
* Add a generic log message
* @param message message to log
*/
public void addGenericLogEntry(String message) {
addGenericLogEntry("%s", message);
}
/**
* Add a generic log entry
* @param fmt String format
* @param args arguments for string
*/
public void addGenericLogEntry(String fmt, Object... args) {
addLogEntry(EventType.MESSAGE, String.format(fmt, args), LogLevel.INFO);
}
/**
* Add an log entry stating an exception was thrown
* @param message error message
*/
public void addExceptionEntry(String message) {
addLogEntry(EventType.EXCEPTION, message, LogLevel.ERROR);
}
/**
* Add a log entry
* @param eventType The log event type
* @param event the event that happened
* @param severity The severity of the event
*/
public void addLogEntry(EventType eventType, String event, LogLevel severity) {
ApplicationUserRequest aur = userSecurityService.getCurrentUser();
String loggedBy = aur == null ? SYS_USER : aur.getFormalName();
ApplicationLog al = new ApplicationLog();
al.setEvent(event.length() > 2000 ? event.substring(0, 1999): event); //Make sure event log doesnt overflow the table
al.setEventType(eventType.getValue());
al.setLoggedBy(loggedBy == null ? SYS_USER : loggedBy);
al.setSeverity(severity == null ? LogLevel.INFO.name(): severity.name());
al.setTimestamp(new Date());
al.setIpAddress(getClientIp());
try {
appLogRepo.save(al);
Map<String, String> log = Utils.convertToStringMap(makeApplicationLogRequest(al));
severity = severity == null ? LogLevel.INFO : severity;
switch(severity) {
case ERROR:
case FATAL:
logger.error(log, new StringBuilder("[Application Error]"));
break;
case DEBUG:
case INFO:
default:
logger.info(log, new StringBuilder("[Application Info]"));
break;
}
CommService.sendAccountTrigger(al, TriggerType.APP_LOGS_UPDATED);
} catch (Exception ex) {
logger.error("Unable to save application log - > " + ex.getMessage());
}
}
/**
* Get all log entries
* @return list of log entries
*/
public List<ApplicationLogRequest> getAllLogs() {
return appLogRepo.findAllByOrderByTimestampDesc()
.stream()
.map(item -> makeApplicationLogRequest(item))
.collect(Collectors.toList());
}
/**
* Purge application logs between two dates
* @param request Request with the dates to purge between
* @return number of logs purged
*/
public Integer purgeLogs(PurgeLogRequest request) {
Date start = DateService.toMidnight(request.getPurgeStartDate());
Date end = DateService.toMidnight(request.getPurgeEndDate(), 1); //Need to add a day to get all of the end date
List<ApplicationLog> logsToPurge = appLogRepo.findByTimestampBetween(start, end);
Set<Integer> idsToPurge = logsToPurge.stream().map(func -> func.getId()).collect(Collectors.toSet());
if (!idsToPurge.isEmpty()) {
appLogRepo.delete(idsToPurge);
}
return idsToPurge.size();
}
/**
* End the current http request
* @throws Exception exception
*/
public void endHttpRequest() throws Exception {
HttpServletRequest request = getRequest();
if (request != null) {
request.logout();
SecurityContextHolder.clearContext();
HttpSession session = request.getSession(false);
if (session != null) {
session.invalidate();
}
}
}
/**
* Get the current http request
* @return httpServletRequest
*/
public HttpServletRequest getRequest() {
RequestAttributes attribs = RequestContextHolder.getRequestAttributes();
if (attribs instanceof ServletRequestAttributes) {
HttpServletRequest request = ((ServletRequestAttributes)attribs).getRequest();
return request;
}
return null;
}
/**
* Map an Application Log JPO entity to a ApplicationLogRequest POJO
* for JSON serialization
* @param al the application log entry to convert
* @return the converted values
*/
private static ApplicationLogRequest makeApplicationLogRequest(ApplicationLog al) {
ApplicationLogRequest arl = new ApplicationLogRequest();
arl.setEvent(al.getEvent());
arl.setEventName(al.getEventType());
arl.setId(al.getId());
arl.setIpAddress(al.getIpAddress());
arl.setLoggedBy(al.getLoggedBy());
arl.setSeverity(al.getSeverity());
arl.setTimestamp(al.getTimestamp());
return arl;
}
/**
* Get the current clients IP address
* @return client ip address
*/
private String getClientIp() {
String remoteAddr = "";
HttpServletRequest request = getRequest();
if (request != null) {
remoteAddr = request.getHeader("X-FORWARDED-FOR");
if (StringUtils.isBlank(remoteAddr)) {
remoteAddr = request.getRemoteAddr();
}
if (StringUtils.isBlank(remoteAddr)) {
remoteAddr = request.getLocalAddr();
}
}
return remoteAddr;
}
}
@@ -0,0 +1,274 @@
package net.locusworks.portal.services;
import org.springframework.context.ApplicationEvent;
import org.springframework.messaging.Message;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;
import net.locusworks.common.logger.ApplicationLogger;
import net.locusworks.common.logger.ApplicationLoggerFactory;
import net.locusworks.common.objectmapper.ObjectMapperError;
import net.locusworks.common.objectmapper.ObjectMapperHelper;
import net.locusworks.common.utils.Utils;
import net.locusworks.portal.common.enums.TriggerType;
import net.locusworks.portal.common.triggers.AccountTrigger;
import net.locusworks.portal.providers.ApplicationEventProvider;
import net.locusworks.portal.util.ApplicationContextHolder;
import static net.locusworks.common.Charsets.UTF_8;
import java.lang.management.ManagementFactory;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.List;
import java.util.Set;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.management.Query;
/**
* Service class to handle stop messages
* @author Isaac Parenteau
* @since 1.0.0-RELEASE
* @version 1.0
*/
@Service
public class CommService {
public static final String STOMP_TOPIC_PREFIX = "/topic/";
public static final String STOMP_QUEUE_PREFIX = "/queue/";
public static final String STOMP_ENDPOINT = "/stomp";
public static final String STOMP_ACCOUNT = "account|";
public static final String STOMP_SYSTEM_TOPIC_SUFFIX = "system";
private static SimpMessagingTemplate template;
private static ApplicationLogger logger = ApplicationLoggerFactory.getLogger(CommService.class);
private static ApplicationEventProvider accountEventProvider;
/**
* Send a stomp message
* @param msg Message to send
* @param topic topic (aka channel) to send the message to
* @return true if the message was sent, false otherwise
*/
private static boolean send(Object msg, String topic) {
try {
String destination = topic;
String payload = toJson(msg);
Message<byte[]> message = MessageBuilder.withPayload(payload.getBytes(UTF_8)).build();
template.send(destination, message);
return true;
} catch (Exception ex) {
logger.error(CommService.class.getName(), ex);
return false;
}
}
/**
* Set the message template to be used for messages
* @param tmplt Message template
*/
public static void setTemplate(SimpMessagingTemplate tmplt) {
template = tmplt;
}
/**
* Send an internal application event via Spring
* @param event Event to send
*/
public static void sendEvent(ApplicationEvent event) {
if (accountEventProvider == null) {
accountEventProvider = (ApplicationEventProvider) ApplicationContextHolder.getContext().getBean(ApplicationEventProvider.EVENT_PROVIDER_NAME);
}
accountEventProvider.publishEvent(event);
}
/**
* Send a message to all logged in accounts
* By default all users automatically listen to the system topic
* to receive system wide messages
* @param msg Message to send
* @return true of the message was sent false otherwise
*/
private static boolean sendAccount(Object msg) {
return sendAccount(msg, STOMP_SYSTEM_TOPIC_SUFFIX);
}
/**
* Send a message to a specific application user
* @param msg message to send
* @param appUserId id of the user to send message to
* @return true of the message was sent false otherwise
*/
private static boolean sendAccount(Object msg, Integer appUserId) {
return sendAccount(msg, String.valueOf(appUserId));
}
/**
* Send messages to subscribed systems
* @param msg message to send
* @param suffix suffix to send the message too
* @return true of the message was sent false otherwise
*/
private static boolean sendAccount(Object msg, String suffix) {
if (suffix == null) {
suffix = STOMP_SYSTEM_TOPIC_SUFFIX;
}
return send(msg, getStompAccountTopic(suffix));
}
/**
* Get the global stomp account topic
* which is the system wide topic suffix
* @return endpoint topic
*/
public static String getGlobalStompAccountTopic() {
return getStompAccountTopic(STOMP_SYSTEM_TOPIC_SUFFIX);
}
/**
* Get a stomp account topic
* @param suffix Suffix to add to the topic to send message to
* @return endpoint topic with a suffix
*/
public static String getStompAccountTopic(String suffix) {
return STOMP_TOPIC_PREFIX + STOMP_ACCOUNT + suffix;
}
/**
* Get a stomp account topic using the account user id
* Used to send messages to the individual user who is logged in
* @param id Id of the user
* @return endpoint topic with the id of the user appended to it
*/
public static String getStompAccountTopic(Integer id) {
return getStompAccountTopic(String.valueOf(id));
}
/**
* Send a system wide account stomp message trigger
* @param resource Resource to send
* @param triggers Trigger types to send
*/
public static void sendAccountTrigger(Object resource, TriggerType... triggers) {
for (TriggerType trigger : triggers) {
sendAccountTrigger(resource, trigger);
}
}
/**
* Send a system wide account stomp message trigger
* @param resource Resource to send
* @param type Trigger type
*/
public static void sendAccountTrigger(Object resource, TriggerType type) {
sendAccountTrigger(resource, type, 0);
}
/**
* Send accounts a stomp message trigger
* @param resource resource to send
* @param type trigger type
* @param userIds user account ids to send to
*/
public static void sendAccountTrigger(Object resource, TriggerType type, Set<Integer> userIds) {
if(userIds == null || userIds.isEmpty()) {
return; //No need to send trigger if there is no one to send it to
}
Utils.safeSet(userIds).forEach(id -> sendAccountTrigger(resource, type, id));
}
/**
* Send an account a stomp message trigger
* @param resource resource to send
* @param type trigger type
* @param userId user account id to send to
*/
@SuppressWarnings("unchecked")
public static void sendAccountTrigger(Object resource, TriggerType type, Integer userId) {
if(userId == null) {
return;
}
AccountTrigger at = new AccountTrigger();
at.setType(type);
at.setTimestampCreation(Calendar.getInstance().getTimeInMillis());
at.setUserId(userId);
if (resource != null && (resource instanceof List)) {
at.setResources((List<Object>) resource);
} else if (resource != null && resource.getClass().isArray()) {
at.setResources(Arrays.asList(resource));
} else {
at.getResources().add(resource);
}
sendAccountTrigger(at);
}
/**
* Send an account a stomp message trigger
* @param at the account trigger to send
*/
public static void sendAccountTrigger(AccountTrigger at) {
if(at.getUserId() == null) {
return; //No need to send trigger if there is no one to send it to and the trigger isnt a broadcast
}
if (at.getUserId().equals(0)) {
sendAccount(at);
} else {
sendAccount(at, at.getUserId());
}
}
/**
* Determine our server's http endpoints
* @return list of web socket endpoints
*/
public static List<EndPoint> getServerEndPoints() {
List<EndPoint> endPoints = new ArrayList<>();
try {
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
Set<ObjectName> objs = mbs.queryNames(new ObjectName("*:type=Connector,*"), Query.match(Query.attr("protocol"), Query.value("HTTP/1.1")));
for (ObjectName obj : objs) {
String protocol = mbs.getAttribute(obj, "scheme").toString();
String port = obj.getKeyProperty("port");
EndPoint ep = new EndPoint();
ep.port = port;
ep.protocol = protocol;
ep.host = "127.0.0.1";
endPoints.add(ep);
}
return endPoints;
} catch (Exception ex) {
logger.error("Unable to get endpoints: " + ex.getMessage(), ex);
}
return null;
}
/**
* Write an object as json
* @param data Object to convert to json
* @return json string representation of the object
*/
private static String toJson(Object data) {
return ObjectMapperHelper.writeValue(data)
.withErrorHandler(new ObjectMapperError() {
@Override
public void getError(Throwable e) {
logger.error("Unable to convert to json: " + e.getMessage(), e);
}
}).getResults();
}
public static class EndPoint {
public String port;
public String host;
public String protocol;
}
}
@@ -0,0 +1,450 @@
package net.locusworks.portal.services;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import net.locusworks.common.utils.Utils;
import net.locusworks.portal.common.enums.CredentialType;
import net.locusworks.portal.common.exceptions.PortalException;
import net.locusworks.portal.common.services.AESService;
import net.locusworks.portal.database.entities.Credentials;
import net.locusworks.portal.database.entities.Password;
import net.locusworks.portal.database.entities.SshKey;
import net.locusworks.portal.database.repos.CredentialTypeRepository;
import net.locusworks.portal.database.repos.CredentialsRepository;
import net.locusworks.portal.database.repos.PasswordRepository;
import net.locusworks.portal.database.repos.SshKeyRepository;
import net.locusworks.portal.json.CredentialRequest;
import net.locusworks.portal.json.ValueRequest;
import static net.locusworks.common.Charsets.UTF_8;
/**
* Service to handle operations for credentials. This credential service is not for user authentication
* but used for credentials in script and git execution
* @author Isaac Parenteau
* @since 1.0.0-RELEASE
* @version 1.0
*/
@Service
public class CredentialService {
@Autowired
private SshKeyRepository keyRepo;
@Autowired
private PasswordRepository passwdRepo;
@Autowired
private CredentialsRepository credRepo;
@Autowired
private CredentialTypeRepository credTypeRepo;
@Autowired
private AESService aesService;
/**
* Find a credentials entry
* @param uid the unique identify of the credential entry to get
* @return the credential JPO entity
*/
public Credentials findCredentials(String uid) {
return credRepo.findByUuid(uid);
}
/**
* Get a specified credential
* @param uuid the unique identifier of the credential to retrieve
* @return the retrieved credentials
*/
public CredentialRequest getCredentials(String uuid) {
try {
return getCredentials(uuid, false);
} catch (Exception ex) {
return null;
}
}
/**
* Get a specified credential
* @param uuid the unique identifier of the credential to retrieve
* @param withDetails retrieve the encrypted password if set to true; false will blank the password out
* @return the retrieved credentials
*/
public CredentialRequest getCredentials(String uuid, boolean withDetails) {
return getCredentials(uuid, withDetails, false);
}
/**
* Get a specified credential
* @param uuid the unique identifier of the credential to retrieve
* @param withDetails retrieve the encrypted password if set to true; false will blank the password out
* @param decrypt decrypt the credentials and store them in plain text (caution. only use this on special needs basis)
* @return the retrieved credentials
*/
public CredentialRequest getCredentials(String uuid, boolean withDetails, boolean decrypt) {
return getCredentials(new CredentialRequest(uuid), withDetails, decrypt);
}
/**
* Get a specified credential
* @param request The credential request to retrieve <br>
* The unique identifier field needs to be populated otherwise null will be returned
* @return The credential request populated with the requested information
*/
public CredentialRequest getCredentials(CredentialRequest request) {
return getCredentials(request, false);
}
/**
* Get a specified credential
* @param request The credential request to retrieve <br>
* The unique identifier field needs to be populated otherwise null will be returned
* @param withDetails retrieve the encrypted password if set to true; false will blank the password out
* @return The credential request populated with the requested information
*/
public CredentialRequest getCredentials(CredentialRequest request, boolean withDetails) {
return getCredentials(request, withDetails, false);
}
/**
* Get a specified credential
* @param request The credential request to retrieve <br>
* The unique identifier field needs to be populated otherwise null will be returned
* @param withDetails retrieve the encrypted password if set to true; false will blank the password out
* @param decrypt decrypt the credentials and store them in plain text (caution. only use this on special needs basis)
* @return The credential request populated with the requested information
*/
public CredentialRequest getCredentials(CredentialRequest request, boolean withDetails, boolean decrypt) {
if (request == null || Utils.isEmptyString(request.getUid())) {
return null;
}
return getCredentials(findCredentials(request.getUid()), withDetails, decrypt);
}
/**
* Get a specified credential
* @param creds The credential JPO entity to convert to the POJO object CredentailRequest
* @see net.locusworks.portal.database.entities.Credentials
* @see net.locusworks.portal.json.CredentialRequest
* @return The credential request populated with the requested information
*/
public CredentialRequest getCredentials(Credentials creds) {
return getCredentials(creds, false);
}
/**
* Get a specified credential
* @param creds The credential JPO entity to convert to the POJO object CredentailRequest
* @param withDetails retrieve the encrypted password if set to true; false will blank the password out
* @see net.locusworks.portal.database.entities.Credentials
* @see net.locusworks.portal.json.CredentialRequest
* @return The credential request populated with the requested information
*/
public CredentialRequest getCredentials(Credentials creds, boolean withDetails) {
return getCredentials(creds, withDetails, false);
}
/**
* Get a specified credential
* @param creds The credential JPO entity to convert to the POJO object CredentailRequest
* @param withDetails retrieve the encrypted password if set to true; false will blank the password out
* @param decryptCreds decrypt the credentials and store them in plain text (caution. only use this on special needs basis)
* @see net.locusworks.portal.database.entities.Credentials
* @see net.locusworks.portal.json.CredentialRequest
* @return The credential request populated with the requested information
*/
public CredentialRequest getCredentials(Credentials creds, boolean withDetails, boolean decryptCreds) {
if (creds == null) {
return null;
}
CredentialRequest cr = null;
CredentialType credType = CredentialType.getEnum(creds.getCredentialType().getName());
switch (credType) {
case PASSWD:
cr = makeCredentailRequestFromPassword(passwdRepo.findOne(creds.getReferenceId()), withDetails, decryptCreds);
break;
case SSH:
cr = makeCredentailRequestFromSshKey(keyRepo.findOne(creds.getReferenceId()), withDetails, decryptCreds);
break;
default:
return null;
}
cr.setId(creds.getId());
return cr;
}
/**
* Add credentials to the database
* @param request Credential request to add
* @return the newly added credentials
* @throws Exception thrown when the credentials cannot be added to the database
*/
public CredentialRequest addCredentials(CredentialRequest request) throws Exception {
try {
CredentialType type = CredentialType.getEnum(request.getCredentialType());
switch (type) {
case PASSWD:
return addPasswordCredentails(request);
case SSH:
return addSshKeyCredentails(request);
default:
return null;
}
} catch (Exception ex) {
throw PortalException.generic("Unable to save credentials: " + ex.getMessage());
}
}
/**
* Delete credentials from the database
* @param request the credentials to deleted
* @throws Exception thrown when an exception occurred when trying to delete the credentials
*/
public void deleteCredentials(CredentialRequest request) throws Exception {
if (StringUtils.isEmpty(request.getUid())) {
throw PortalException.noEntryExists("Unable to find credentials uid of %s", request.getUid());
}
Credentials creds = credRepo.findByUuid(request.getUid());
if (creds == null) {
throw PortalException.constraintViolation("Unable to find credentials uid of %s", request.getUid());
}
boolean inUse = credRepo.inUse(creds.getUuid());
if (inUse) {
throw PortalException.constraintViolation("Credential is in use. Please delete records that use this credentials then try again.");
}
credRepo.delete(creds.getId());
CredentialType credType = CredentialType.getEnum(creds.getCredentialType().getName());
switch(credType) {
case PASSWD:
passwdRepo.delete(creds.getReferenceId());
break;
case SSH:
keyRepo.delete(creds.getReferenceId());
break;
}
}
/**
* Get the list of available credentials
* @return list of available credentials
*/
public List<CredentialRequest> getCredentials() {
List<CredentialRequest> creds = credRepo.findAll()
.stream()
.map(cred -> getCredentials(cred))
.sorted((cred1, cred2) -> cred1.getId().compareTo(cred2.getId()))
.collect(Collectors.toList());
return creds;
}
/**
* Get the list of available credential types
* @return list of available credential types
*/
public List<ValueRequest>getCredentialTypes() {
return credTypeRepo.findAll()
.stream()
.map(cr -> new ValueRequest(cr.getId(), cr.getName(), cr.getDisplayName()))
.collect(Collectors.toList());
}
/**
* Update the sshkey and password encrypted parameters with the new password seed
* @param seed Seed to update the encrypted values with
*/
public void updateEncryptedCredentials(String seed) {
List<SshKey> sshKeyList = keyRepo.findAll()
.stream()
.map(key -> {
String newKey = aesService.changeEncryptionValue(key.getSshKey(), seed);
String newPassphrase = aesService.changeEncryptionValue(key.getPassphrase(), seed);
key.setPassphrase(newPassphrase);
key.setSshKey(newKey.getBytes(UTF_8));
return key;
})
.collect(Collectors.toList());
if (!sshKeyList.isEmpty()) {
keyRepo.saveAll(sshKeyList);
}
List<Password> passwordList = passwdRepo.findAll()
.stream()
.map(passwd -> {
String newPassword = aesService.changeEncryptionValue(passwd.getPassword(), seed);
passwd.setPassword(newPassword);
return passwd;
})
.collect(Collectors.toList());
if (!passwordList.isEmpty()) {
passwdRepo.saveAll(passwordList);
}
}
/**
* Add a ssh key to the database
* @param request the ssh key information to add to the database
* @return the Credential request with the credential information
* @throws Exception thrown when an error occurred when trying to create the database entry
*/
private CredentialRequest addSshKeyCredentails(CredentialRequest request) throws Exception {
SshKey key = StringUtils.isNotEmpty(request.getUid()) ? keyRepo.findByUuid(request.getUid()) : null;
if (key == null) {
key = new SshKey();
key.setUuid(UUID.randomUUID().toString());
}
String formattedKey = request.getKey();
key.setCreatedBy(UserSecurityService.getApplicationUser());
key.setDateCreated(new Date());
key.setName(request.getName());
key.setIdentity(request.getUsername());
key.setPassphrase(aesService.encrypt(request.getPassphrase())); //Encrypt the passphrase to be stored in the database
key.setSshKey(aesService.encrypt(formattedKey).getBytes(UTF_8)); //Encrypt the key to be stored in the database
keyRepo.save(key);
Integer id = addCredentialEntry(key.getUuid(), CredentialType.SSH, key.getId());
CredentialRequest creds = makeCredentailRequestFromSshKey(key);
creds.setId(id);
return creds;
}
/**
* Add a username/password entry to the database
* @param request the username/password information to add to the database
* @return the Credential request with the credential information
* @throws Exception thrown when an error occurred when trying to create the database entry
*/
private CredentialRequest addPasswordCredentails(CredentialRequest request) throws Exception {
Password passwd = StringUtils.isNotEmpty(request.getUid()) ? passwdRepo.findByUuid(request.getUid()) : null;
if (passwd == null) {
passwd = new Password();
passwd.setUuid(UUID.randomUUID().toString());
}
passwd.setCreatedBy(UserSecurityService.getApplicationUser());
passwd.setDateCreated(new Date());
passwd.setName(request.getName());
passwd.setUserName(request.getUsername());
passwd.setPassword(aesService.encrypt(request.getPassword())); //Encrypt the password
passwdRepo.save(passwd);
Integer id = addCredentialEntry(passwd.getUuid(), CredentialType.PASSWD, passwd.getId());
CredentialRequest creds = makeCredentailRequestFromPassword(passwd);
creds.setId(id);
return creds;
}
/**
* add common credential entry into the database that soft links to the ssh_key or username/password entry
* @param uuid the unique identifier of the credentials
* @param credType
* @param referenceId
*/
private Integer addCredentialEntry(String uuid, CredentialType credType, Integer referenceId) {
Credentials creds = credRepo.findByUuid(uuid);
if (creds == null) {
creds = new Credentials();
creds.setDateAdded(new Date());
}
creds.setUuid(uuid);
creds.setCredentialType(credTypeRepo.findByName(credType.toString()));
creds.setReferenceId(referenceId);
credRepo.save(creds);
return creds.getId();
}
/**
* Convert a SshKey JPO entity to a CredentialRequest
* @param key the ssh key to convert
* @return converted sshKey
* @see net.locusworks.portal.database.entities.SshKey
* @see net.locusworks.portal.json.CredentialRequest
*/
private CredentialRequest makeCredentailRequestFromSshKey(SshKey key) {
return makeCredentailRequestFromSshKey(key, false, false);
}
/**
* Convert a SshKey JPO entity to a CredentialRequest
* @param key the ssh key to convert
* @param addInfo Add the encrypted passphrase and ssh key to the request if set to true; blank otherwise
* @return converted sshKey
* @see net.locusworks.portal.database.entities.SshKey
* @see net.locusworks.portal.json.CredentialRequest
*/
private CredentialRequest makeCredentailRequestFromSshKey(SshKey key, boolean addInfo, boolean decryptCreds) {
if (key == null) {
return null;
}
CredentialRequest cr = makeCredentailRequest(key.getUuid(), key.getIdentity(), CredentialType.SSH);
cr.setName(key.getName());
if (addInfo) {
String passphrase = decryptCreds ? aesService.decrypt(key.getPassphrase()) : key.getPassphrase();
String sshKey = decryptCreds ? aesService.decrypt(key.getSshKey()) : new String(key.getSshKey(), UTF_8);
cr.setPassphrase(passphrase);
cr.setKey(sshKey);
}
return cr;
}
/**
* Convert a Password JPO entity to a CredentialRequest
* @param creds the Password to convert
* @return converted password
* @see net.locusworks.portal.database.entities.Password
* @see net.locusworks.portal.json.CredentialRequest
*/
private CredentialRequest makeCredentailRequestFromPassword(Password creds) {
return makeCredentailRequestFromPassword(creds, false, false);
}
/**
* Convert a Password JPO entity to a CredentialRequest
* @param creds the Password to convert
* @param addInfo Add the encrypted password if set to true; blank otherwise
* @return converted password
* @see net.locusworks.portal.database.entities.Password
* @see net.locusworks.portal.json.CredentialRequest
*/
private CredentialRequest makeCredentailRequestFromPassword(Password creds, boolean addInfo, boolean decryptCreds) {
if (creds == null) {
return null;
}
CredentialRequest cr = makeCredentailRequest(creds.getUuid(), creds.getUserName(), CredentialType.PASSWD);
cr.setName(creds.getName());
if (addInfo) {
String password = decryptCreds ? aesService.decrypt(creds.getPassword()) : creds.getPassword();
cr.setPassword(password);
}
return cr;
}
/**
* Make a standard CredentialRequest with basic information about the credentials
* @param uid the unique identifier for the credentials
* @param userName the name of the credentials
* @param credType the type of credentials it represents
* @return credentialRequest populated with the parameters passed in
*/
private CredentialRequest makeCredentailRequest(String uid, String userName, CredentialType credType) {
return new CredentialRequest(uid, userName, credType.toString());
}
}
@@ -0,0 +1,332 @@
package net.locusworks.portal.services;
import java.io.File;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
import javax.annotation.PostConstruct;
import org.apache.commons.io.FileUtils;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import net.locusworks.common.logger.ApplicationLogger;
import net.locusworks.common.logger.ApplicationLoggerFactory;
import net.locusworks.portal.common.enums.FrequencyType;
import net.locusworks.portal.common.exceptions.PortalException;
import net.locusworks.portal.common.services.ConfigurationService;
import net.locusworks.portal.common.utils.DateService;
import net.locusworks.portal.database.entities.GitRepo;
import net.locusworks.portal.database.repos.GitRepository;
import net.locusworks.portal.handlers.TaskSchedulerHandler;
import net.locusworks.portal.json.CredentialRequest;
import net.locusworks.portal.json.GitRequest;
import net.locusworks.portal.util.GitRepositoryHelper;
import net.locusworks.portal.util.TaskScheduler;
/**
* Service for dealing with Git repositories
* @author Isaac Parenteau
* @since 1.0.0-RELEASE
* @version 1.0
*/
@Service
public class GitService implements Job {
public static final String UPDATE_DAILY_JOB_NAME = "updateRepoDaily";
public static final String UPDATE_DAILY_TRIGGER_NAME = "updateRepoTriggerDaily";
public static final String UPDATE_JOB_NAME = "updateRepo";
public static final String UPDATE_TRIGGER_NAME = "updateRepoTrigger";
public static final String UPDATE_GROUP_NAME = "updateRepoGrup";
private static ApplicationLogger logger = ApplicationLoggerFactory.getLogger(GitService.class);
@Autowired
private GitRepository gitRepo;
@Autowired
private CredentialService credService;
@Autowired
private ConfigurationService confService;
@Autowired
private ApplicationLogService appLogService;
@Autowired
private SchedulerService scheduler;
/**
* Updates the current known repositories by doing a pull request from the respective repositories <br>
* Schedules future updates.
*/
@PostConstruct
public void init() {
//Update the current repositories
updateCurrentRepositories();
scheduleFutureUpdates();
}
/**
* Issue a pull request on a specified git repository
* @param request Information pertaining to the repository to do the pull request on
* @return The git request with the populated information about the pull request
* @throws PortalException thrown when an error occurred during the pull request
*/
public GitRequest pullGitRepository(GitRequest request) throws Exception {
if (request.getId() == null) {
throw PortalException.illegalArgument("No git id provided");
}
GitRepo gr = gitRepo.findOne(request.getId());
if (gr == null) {
throw PortalException.noEntryExists("No repository found for git id %d", request.getId());
}
if (gr.getCredentials() == null) {
throw PortalException.invalidCreds();
}
CredentialRequest creds = credService.getCredentials(gr.getCredentials(), true, true);
if (creds == null) {
throw PortalException.illegalArgument(String.format("Unable to find credentials for git repo %d", gr.getName()));
}
try (GitRepositoryHelper helper = new GitRepositoryHelper(gr.getDirectory(), creds, confService.getKeyDirectory())) {
logger.info("Results of pull request: %s", helper.pull());
} catch (Exception ex) {
logger.error(String.format("Unable to issue pull request: %s", ex.getMessage()), ex);
throw PortalException.fromException(ex);
}
return makeGitRequest(gr);
}
/**
* Add a git repository to the system
* @param request Git repository to add
* @return The added git repository information
* @throws Exception thrown when an error occurred when trying to add a git repository
*/
public GitRequest addGitRepository(GitRequest request) throws Exception {
if (request.getCredentials() == null || request.getCredentials().getUid() == null) {
throw PortalException.invalidCreds();
}
if (gitRepo.existsByName(request.getName())) {
throw PortalException.duplicateEntry("A repository with the name %s already exists", request.getName());
}
CredentialRequest creds = credService.getCredentials(request.getCredentials());
if (creds == null) {
throw PortalException.illegalArgument(String.format("Unable to find credentials with name of %s", request.getCredentials().getName()));
}
GitRepo pg = request.getId() == null ? null : gitRepo.findOne(request.getId());
boolean alreadyExist = pg != null;
if (!alreadyExist) {
pg = new GitRepo();
pg.setDatedCreated(new Date());
}
pg.setName(request.getName());
boolean newBranch = !String.valueOf(pg.getBranch()).trim().equals(String.valueOf(request.getBranch()).trim());
pg.setBranch(request.getBranch());
pg.setCreatedBy(UserSecurityService.getApplicationUser());
pg.setDirectory(request.getDirectory());
pg.setLastUpdated(new Date());
pg.setScm(request.getScm());
pg.setCredentials(credService.findCredentials(creds.getUid()));
gitRepo.save(pg);
if (!alreadyExist) {
createRepo(pg, creds);
} else if (alreadyExist && newBranch) {
switchBranch(pg, creds);
}
return makeGitRequest(pg);
}
/**
* Delete a git repository from the system
* @param request The git repository to delete
* @return the deleted repository
* @throws Exception thrown if an error occurred when trying to delete the repository
*/
public GitRequest deleteGitRepository(GitRequest request) throws Exception {
GitRepo gr = gitRepo.findOne(request.getId());
if (gr == null) {
throw PortalException.noEntryExists("No entry exist for git repo with id of %d", request.getId());
}
File gitDir = new File(request.getDirectory());
if (gitDir.exists()) {
FileUtils.deleteDirectory(gitDir);
}
gitRepo.delete(gr.getId());
return request;
}
/**
* Execution Quartz Scheduled tasks
* @param context The job execution context containing the information about the quartz
* schedule that triggered the execution
*/
@Override
public void execute(JobExecutionContext context) {
List<TaskScheduler> tasks = gitRepo.findAll()
.stream()
.map(gr -> updateRepo(gr))
.collect(Collectors.toList());
if (tasks.isEmpty()) {
logger.info("No repositories found to update");
return;
}
logger.info(String.format("%d %s scheduled for update", tasks.size(), tasks.size() == 1 ? "repository" : "repositories"));
scheduler.schedule(tasks);
}
/**
* Get all the repositories
* @return list of repositories
*/
public List<GitRequest> getRepositories() {
return gitRepo.findAll()
.stream()
.map(gr -> makeGitRequest(gr))
.filter(gr -> gr != null)
.collect(Collectors.toList());
}
/**
* Create a git repository on the system
* @param pg the Git repository to add
* @param creds The credential request to use with the git repository
* @throws Exception thrown when the repository cannot be created
*/
private void createRepo(GitRepo pg, CredentialRequest creds) throws Exception {
CredentialRequest credentials = credService.getCredentials(creds, true, true);
try (GitRepositoryHelper helper = new GitRepositoryHelper(credentials, confService.getKeyDirectory())) {
helper.createRepository(pg.getScm(), pg.getDirectory(), pg.getBranch());
}
}
/**
* Create a GitRequest POJO from a GitRepo JPO entity to be used in JSON serialization
* @param gr the git repository database entry to convert
* @return the converted entry
*/
private GitRequest makeGitRequest(GitRepo gr) {
GitRequest request = new GitRequest();
request.setId(gr.getId());
request.setBranch(gr.getBranch());
request.setDirectory(gr.getDirectory());
request.setName(gr.getName());
request.setScm(gr.getScm());
request.setCreatedBy(String.format("%s %s", gr.getCreatedBy().getUserAttribute().getFirstName(), gr.getCreatedBy().getUserAttribute().getLastName()));
request.setLastUpdated(gr.getLastUpdated());
request.setCreateDate(gr.getDatedCreated());
CredentialRequest creds = credService.getCredentials(gr.getCredentials().getUuid());
if (creds == null) {
logger.error("No credentials were found for repo %s", gr.getName());
return null;
}
request.setCredentials(creds);
return request;
}
/**
* Update the current git repositories
*/
private void updateCurrentRepositories() {
scheduler.addJob(GitService.class, UPDATE_JOB_NAME, UPDATE_TRIGGER_NAME, UPDATE_GROUP_NAME);
}
/**
* Schedule the git service to run every night ad midnight to do an update on each
* repository stored
*/
private void scheduleFutureUpdates() {
Date tomorrow = DateService.toMidnight(new Date(), 1);
scheduler.addJob(GitService.class, UPDATE_DAILY_JOB_NAME, UPDATE_DAILY_TRIGGER_NAME, UPDATE_GROUP_NAME, FrequencyType.DAILY, tomorrow);
}
/**
* switch to a different branch within the git repository
* @param gr The git repository to switch branches on
* @param creds The credentials to use for the git repository
* @throws Exception thrown when an error occurs during the switch branch process
*/
private void switchBranch(GitRepo gr, CredentialRequest creds) throws Exception {
CredentialRequest credentials = credService.getCredentials(creds, true, true);
File repoDir = new File(gr.getDirectory());
try (GitRepositoryHelper helper = new GitRepositoryHelper(repoDir, credentials, confService.getKeyDirectory())) {
helper.switchBranch(gr.getBranch());
} catch (InterruptedException ex) {
logger.warn("Switching branch task was interrupted: %s", ex.getMessage());
}
}
/**
* Create a task scheduler to update repositories in separate threads
* @param gr the git repository to update
* @return the task scheduler to execute
*/
private TaskScheduler updateRepo(final GitRepo gr) {
//Schedule the task scheduler to run 1 minute after scheduled
return new TaskScheduler(new TaskSchedulerHandler() {
@Override
public void runTask() {
try {
updateRepoTask(gr);
} catch (Exception ex) {
logger.error("Unable to update repository: " + ex.getMessage(), ex);
}
}
});
}
/**
* Update the git repository
* @param gr The git repository to update
* @throws Exception thrown when an error occurs during execution
*/
private void updateRepoTask(final GitRepo gr) throws Exception {
logger.info("Updating repository " + gr.getName());
CredentialRequest credentials = credService.getCredentials(gr.getCredentials().getUuid(), true, true);
if (credentials == null) {
throw PortalException.noEntryExists("No credentials found for repository %s", gr.getName());
}
File repoDir = new File(gr.getDirectory());
try (GitRepositoryHelper helper = new GitRepositoryHelper(repoDir, credentials, confService.getKeyDirectory())){
logger.info("Results of pull request: %s", helper.pull());
gr.setLastUpdated(new Date());
gitRepo.save(gr);
} catch (Exception ex) {
appLogService.addExceptionEntry("Unable to update repository: " + ex.getMessage());
}
logger.info("Done...");
}
}
@@ -0,0 +1,264 @@
package net.locusworks.portal.services;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import net.locusworks.portal.common.exceptions.PortalException;
import net.locusworks.portal.database.entities.Permission;
import net.locusworks.portal.database.entities.PermissionCategory;
import net.locusworks.portal.database.entities.PermissionGroup;
import net.locusworks.portal.database.entities.PermissionGroupPermission;
import net.locusworks.portal.database.repos.PermissionCategoryRepository;
import net.locusworks.portal.database.repos.PermissionGroupPermissionRepository;
import net.locusworks.portal.database.repos.PermissionGroupRepository;
import net.locusworks.portal.database.repos.PermissionRepository;
import net.locusworks.portal.json.PermissionCategoryRequest;
import net.locusworks.portal.json.PermissionGroupPermissionRequest;
import net.locusworks.portal.json.PermissionGroupRequest;
import net.locusworks.portal.json.PermissionRequest;
import static net.locusworks.common.utils.Constants.TRUE;
import static net.locusworks.common.utils.Constants.FALSE;
/**
* Service for handling all permissions
* @author Isaac Parenteau
* @since 1.0.0-RELEASE
* @version 1.0
*/
@Service
public class PermissionsService {
@Autowired
private PermissionGroupRepository permGroupRepo;
@Autowired
private PermissionGroupPermissionRepository pgpRepo;
@Autowired
private PermissionCategoryRepository permCatRepo;
@Autowired
private PermissionRepository permRepo;
/**
* Return all the permission groups and the permissions associated with it
* @return list of all the permission groups
*/
public List<PermissionGroupRequest> getPermissionGroups() {
return getPermissionGroups(true);
}
/**
* Get all the permissions groups
* @param addPermGroupPerms include the permissions
* @return list of all the permission groups with their associated permissions
*/
public List<PermissionGroupRequest> getPermissionGroups(final boolean addPermGroupPerms) {
return permGroupRepo.findAll()
.stream()
.map(item -> makePermissionsGroupRequest(item, addPermGroupPerms))
.collect(Collectors.toList());
}
/**
* Get the various permission categories that permissions belong to.
* @return list of permission categories
*/
public List<PermissionCategoryRequest> getPermissionCatgories() {
return permCatRepo.findAll()
.stream()
.map(pc -> makePermissionCategoryRequest(pc))
.collect(Collectors.toList());
}
/**
* Add a new permission group
* @param request Request with the information
* @return the new permission group that was added
* @throws Exception exception
*/
public PermissionGroupRequest addPermissionGroup(PermissionGroupRequest request) throws Exception {
//Trim out all white space and capitalize all to make and check for a unique name
String permGroupName = request.getName().replaceAll(" ", "").toUpperCase();
PermissionGroup pg = permGroupRepo.findByName(permGroupName);
if (pg != null) {
throw PortalException.duplicateEntry("Entry exist for group with name " + request.getName());
}
pg = new PermissionGroup();
pg.setName(permGroupName);
pg.setDateCreated(new Date());
return updatePermissionGroupCommon(pg, request);
}
/**
* Update an existing permission group
* @param request Request with the updated permission group
* @return the updated permission group
* @throws Exception exception
*/
public PermissionGroupRequest updatePermissionGroup(PermissionGroupRequest request) throws Exception {
PermissionGroup pg = permGroupRepo.findByName(request.getName());
if (pg == null) {
throw PortalException.noEntryExists("No entry exist for name " + request.getName());
}
return updatePermissionGroupCommon(pg, request);
}
/**
* Delete a permission group. Will throw an exception if the permission group doesn't exist
* or there are users still associated with the permission group
* @param request Request with the information to be deleted
* @return the request that was deleted
* @throws Exception exception
*/
public PermissionGroupRequest deletePermissionGroup(PermissionGroupRequest request) throws Exception {
PermissionGroup pg = permGroupRepo.findByName(request.getName());
if (pg == null) {
throw PortalException.noEntryExists("No permission group found with name: " + request.getName());
}
int count = permGroupRepo.countApplicationUsersInPermissionGroup(request.getName());
if (count > 0) {
throw PortalException.generic(String.format("There are users in permission group %s. Either remove the users or disable the group", request.getName()));
}
permGroupRepo.delete(pg.getId());
return request;
}
/**
* Get the account ids that are associated with a particular permission group
* @param name Permission group to check
* @return set of ids associated with the permission group accounts
*/
public Set<Integer> getPermissionGroupAccounts(String name) {
return permGroupRepo.findApplcationUserIdsFromPermissionGroup(name);
}
/**
* common Function to update the permission group
* @param pg the permission group to update
* @param request the request with the new information to use to update
* @return the updated information
*/
private PermissionGroupRequest updatePermissionGroupCommon(PermissionGroup pg, PermissionGroupRequest request) {
pg.setDisplayName(request.getDisplayName());
pg.setEnabled(request.getEnabled() ? TRUE : FALSE);
pg.setLastModified(new Date());
List<PermissionGroupPermission> pgpList = new ArrayList<>();
for(PermissionGroupPermissionRequest pgpr : request.getPermissions()) {
PermissionGroupPermission permission = pg.getId() == null ? null :
pgpRepo.findByPermissionGroupAndPermission(pgpr.getPermissionGroupId(), pgpr.getPermissionId());
//Need to create a new PermissionGroupPermission if it the permission group or permission is new
if (permission == null) {
permission = new PermissionGroupPermission();
permission.setDateCreated(new Date());
permission.setPermissionGroup(pg);
permission.setPermission(permRepo.findOne(pgpr.getPermissionId()));
}
permission.setAllowed(pgpr.getAllowed() ? TRUE : FALSE);
permission.setLastModified(new Date());
pgpList.add(permission);
}
pg.setPermissionGroupPermissionList(pgpList);
permGroupRepo.save(pg);
return makePermissionsGroupRequest(pg);
}
/**
* Make a permission category request (map a permission category db entry to a pojo)
* @param pc the permission category JPO Entity
* @return the request
*/
private PermissionCategoryRequest makePermissionCategoryRequest(PermissionCategory pc) {
PermissionCategoryRequest pcr = new PermissionCategoryRequest();
pcr.setId(pc.getId());
pcr.setName(pc.getName());
pcr.setDisplayName(pc.getDisplayName());
pcr.setPermissions(new ArrayList<>());
pcr.setPermissions(permRepo.findByPermissionCategory(pc)
.stream()
.map(p -> makePermissionRequest(p))
.collect(Collectors.toList()));
return pcr;
}
/**
* Make a permission group. (map a permission db entry to a pojo)
* @param p the permission JPO Entity
* @return the converted request
*/
private PermissionRequest makePermissionRequest(Permission p) {
PermissionRequest pr = new PermissionRequest();
pr.setId(p.getId());
pr.setName(p.getName());
pr.setDisplayName(p.getDisplayName());
pr.setPermissionCategory(p.getPermissionCategory().getName());
return pr;
}
/**
* Make a permission group request (Map a permission group database entry to a pojo)
* @param permGroup the JPA Entity
* @return the converted request
*/
private PermissionGroupRequest makePermissionsGroupRequest(PermissionGroup permGroup) {
return makePermissionsGroupRequest(permGroup, true);
}
/**
* Make a permission group request (map a permission group database entry into a pojo)
* @param permGroup Permission Group to convert
* @param addPermGroupPerms add the individual permissions to the request
* @return the converted request
*/
private PermissionGroupRequest makePermissionsGroupRequest(PermissionGroup permGroup, boolean addPermGroupPerms) {
PermissionGroupRequest pgr = new PermissionGroupRequest();
pgr.setId(permGroup.getId());
pgr.setName(permGroup.getName());
pgr.setDisplayName(permGroup.getDisplayName());
pgr.setEnabled(permGroup.getEnabled() == TRUE);
pgr.setPermissions(new ArrayList<>());
if (!addPermGroupPerms) {
return pgr;
}
pgr.setPermissions( pgpRepo.findByPermissionGroup(permGroup)
.stream()
.map(pgp -> makePermissionsGroupPermissionRequest(pgp))
.collect(Collectors.toList()));
return pgr;
}
/**
* Make a permission group permission request from a permission group permission database entry
* @param pgp the JPA Entity
* @return the converted request
*/
private PermissionGroupPermissionRequest makePermissionsGroupPermissionRequest(PermissionGroupPermission pgp) {
PermissionGroupPermissionRequest pgpRequest = new PermissionGroupPermissionRequest();
pgpRequest.setId(pgp.getId());
pgpRequest.setPermissionId(pgp.getPermission().getId());
pgpRequest.setPermissionGroupId(pgp.getPermissionGroup().getId());
pgpRequest.setName(pgp.getPermission().getName());
pgpRequest.setDisplayName(pgp.getPermission().getDisplayName());
pgpRequest.setPermissionCategory(pgp.getPermission().getPermissionCategory().getName());
pgpRequest.setAllowed(pgp.getAllowed() == TRUE);
return pgpRequest;
}
}
@@ -0,0 +1,151 @@
package net.locusworks.portal.services;
import java.sql.Driver;
import java.sql.DriverManager;
import java.util.Enumeration;
import javax.servlet.ServletContextEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.context.ContextLoaderListener;
import com.mysql.cj.jdbc.AbandonedConnectionCleanupThread;
import net.locusworks.common.logger.ApplicationLogger;
import net.locusworks.common.logger.ApplicationLoggerFactory;
import net.locusworks.portal.common.PortalDataSource;
import net.locusworks.portal.extendedlogger.ExtendedLoggerFactory;
import net.locusworks.portal.handlers.LoggerInitializer;
import net.locusworks.portal.util.ApplicationContextHolder;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderLeakPreventor;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderLeakPreventorFactory;
/**
* Context component class that handles context events upon server startup, shutdown and refresh events
* @author Isaac Parenteau
* @since 1.0.0-RELEASE
* @version 1.0
*/
@Component
public class PortalContextService extends ContextLoaderListener implements ApplicationListener<ContextRefreshedEvent> {
private ClassLoaderLeakPreventor leakPreventor;
@Autowired
private SimpMessagingTemplate template;
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
try {
//Initialize the template for web socket messages
CommService.setTemplate(template);
} catch (Exception ex) {
ApplicationLogger logger = ApplicationLoggerFactory.getLogger(PortalContextService.class);
logger.error(getClass().getName(), ex);
}
}
@Override
public void contextInitialized(ServletContextEvent sce) {
// Initialize the logger;
ApplicationLoggerFactory.init(new LoggerInitializer());
try {
ClassLoaderLeakPreventorFactory lpf = new ClassLoaderLeakPreventorFactory();
lpf.setLogger(ExtendedLoggerFactory.getExtendedLogger(ClassLoaderLeakPreventorFactory.class));
leakPreventor = lpf.newLeakPreventor();
} catch (Exception e) {}
super.contextInitialized(sce);
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
ApplicationLoggerFactory.saveLogLevels();
cleanUpJDBCDrivers();
cleanUpDatasources();
cleanUpSchedulerService();
cleanUpThreads();
resetSystemOut();
super.contextDestroyed(sce);
}
private void resetSystemOut() {
ApplicationLogger logger = ApplicationLoggerFactory.getLogger(PortalContextService.class);
try {
logger.info("Resetting system.out and system.err");
} catch (Exception ex) {
logger.error("Unable to reset system out: " + ex.getMessage(), ex);
}
}
/**
* JGit work queue stays open. try to find it and
* set the context class to null to allow for it to terminate
*/
private void cleanUpThreads() {
try {
leakPreventor.runCleanUps();
} catch (Exception ex) {
ApplicationLogger logger = ApplicationLoggerFactory.getLogger(PortalContextService.class);
logger.error("Unable to clean up threads: " + ex.getMessage(), ex);
}
}
/**
* Try to shutdown scheduled tasks to prevent memory leaks
*/
private void cleanUpSchedulerService() {
ApplicationLogger logger = ApplicationLoggerFactory.getLogger(PortalContextService.class);
try {
SchedulerService executor = ApplicationContextHolder.getContext().getBean(SchedulerService.class);
if (executor != null) {
logger.info("Shutting down scheduler service");
executor.shutdown();
}
} catch (Exception ex) {
logger.error("Unable to close scheduler Service: " + ex.getMessage(), ex);
}
}
/**
* Try to close datasource connections to the database
*/
private void cleanUpDatasources() {
ApplicationLogger logger = ApplicationLoggerFactory.getLogger(PortalContextService.class);
try {
PortalDataSource dataSource = ApplicationContextHolder.getContext().getBean(PortalDataSource.class);
if (dataSource != null) {
logger.info("Clearing database connection pools");
dataSource.closeDataSources();
}
} catch (Exception ex) {
logger.error("Unable to clean up datasource: " + ex.getMessage(), ex);
}
}
/**
* Try to clean up the jdbc drivers to prevent memory leakage
*/
private void cleanUpJDBCDrivers() {
ApplicationLogger logger = ApplicationLoggerFactory.getLogger(PortalContextService.class);
try {
AbandonedConnectionCleanupThread.checkedShutdown();
Enumeration<Driver> drivers = DriverManager.getDrivers();
while (drivers.hasMoreElements()) {
Driver driver = drivers.nextElement();
try {
DriverManager.deregisterDriver(driver);
logger.info(String.format("deregistering jdbc driver: %s", driver));
} catch (Exception ex) {
logger.error(String.format("Error deregistering jdbc driver: %s -> %s", driver, ex.getMessage()));
}
}
} catch (Exception ex) {
logger.error("Unable to clean up JDBC drivers: " + ex.getMessage(), ex);
}
}
}
@@ -0,0 +1,135 @@
package net.locusworks.portal.services;
import java.io.File;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import net.locusworks.common.logger.ApplicationLogger;
import net.locusworks.common.logger.ApplicationLoggerFactory;
import net.locusworks.portal.database.entities.ResourceFile;
import net.locusworks.portal.database.repos.ResourceFileRepository;
import net.locusworks.portal.json.ResourceFileRequest;
import static net.locusworks.common.utils.Constants.TRUE;
/**
* Service to handle resource files generated from the scheduled scripts
* @author Isaac Parenteau
* @since 1.0.0-RELEASE
* @version 1.0
*/
@Service
public class ResourceFileService {
private static ApplicationLogger logger = ApplicationLoggerFactory.getLogger(ResourceFileService.class);
@Autowired
private ResourceFileRepository resourceFileRepo;
/**
* Locate a file on the system
* @param fileId id of the file to find
* @return the resource file
*/
public ResourceFileRequest locateFile(Integer fileId) {
Optional<ResourceFile> rf = resourceFileRepo.findById(fileId);
if (!rf.isPresent()) {
logger.info("No file found with id of %s", fileId);
return null;
}
return makeResourceFileRequest(rf.get());
}
/**
* Delete a resource file on the file system and the database
* @param id of the file to delete
*/
public void deleteResourceFile(Integer id) {
resourceFileRepo.delete(id);
}
/**
* save a resource file information to the database
* @param files files to save
*/
public void saveResourceFiles(List<ResourceFile> files) {
if(files == null || files.isEmpty()) {
return;
}
resourceFileRepo.saveAll(files);
}
/**
* Find the resource files generated by a script
* @param scriptResultsId the script result id that generated the resource files
* @return list of generated files
*/
public List<ResourceFileRequest> findByScriptResultId(Integer scriptResultsId) {
return resourceFileRepo.findByScriptResultId(scriptResultsId)
.stream()
.map(resFile -> makeResourceFileRequest(resFile))
.collect(Collectors.toList());
}
/**
* Delete resource files associated with a script schedule
* @param scriptScheduleId script schedule id that generated the resource files
*/
public void deleteResourceFiles(Integer scriptScheduleId) {
Set<Integer> ids = new HashSet<>();
resourceFileRepo.findByScriptScheduleId(scriptScheduleId)
.stream()
.forEach(rf -> {
//Add the resource file ids to delete and delete the file from the file system
ids.add(rf.getId());
File file = new File(String.format("%s/%s", rf.getFileLocation(), rf.getFileName()));
FileUtils.deleteQuietly(file);
});
if (!ids.isEmpty()) {
resourceFileRepo.delete(ids);
}
}
/**
* Get the number of resource files associated with a certain script
* @param scriptId the script id to check against
* @return number of resource files associated to the script
*/
public int resourceFileCountByScript(Integer scriptId) {
return resourceFileRepo.resourceFileCountByScriptId(scriptId);
}
/**
* get the number of resource files associated with a certain script schedule
* @param scriptScheduleId the script schedule id
* @return number of resource files associated to the the script schedule
*/
public int resourceFileCountByScriptSchedule(Integer scriptScheduleId) {
return resourceFileRepo.resourceFileCountByScriptScheduleId(scriptScheduleId);
}
/**
* Converts a ResourceFile JPO Entity to a ResourceFileRequest that can be serialized to a json string later
* @param resFile the resource file to convert
* @return the converted request
*/
private ResourceFileRequest makeResourceFileRequest(ResourceFile resFile) {
ResourceFileRequest rfr = new ResourceFileRequest();
rfr.setId(resFile.getId());
rfr.setScriptResultId(resFile.getScriptResult().getId());
rfr.setDateCreated(resFile.getDateCreated());
rfr.setFileLocation(resFile.getFileLocation());
rfr.setFileName(resFile.getFileName());
rfr.setFileType(resFile.getFileType());
rfr.setValid(resFile.getValid() == TRUE);
return rfr;
}
}
@@ -0,0 +1,459 @@
package net.locusworks.portal.services;
import static net.locusworks.portal.common.utils.Constants.SCHEDULER_GROUP_NAME;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javax.annotation.PostConstruct;
import org.quartz.CalendarIntervalScheduleBuilder;
import org.quartz.Job;
import org.quartz.JobBuilder;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.stereotype.Service;
import net.locusworks.common.logger.ApplicationLogger;
import net.locusworks.common.logger.ApplicationLoggerFactory;
import net.locusworks.portal.common.enums.FrequencyType;
import net.locusworks.portal.common.utils.DateService;
import net.locusworks.portal.util.TaskScheduler;
/**
* Scheduler service that can schedule tasks through quartz or ExecutorService
* @see org.quartz.Scheduler
* @see java.util.concurrent.ScheduledExecutorService
* @author Isaac Parenteau
* @since 1.0.0-RELEASE
* @version 1.0
*/
@Service
public class SchedulerService {
private static ApplicationLogger logger = ApplicationLoggerFactory.getLogger(SchedulerService.class);
@Autowired
private SchedulerFactoryBean schedulerFactory;
private Scheduler scheduler;
private ScheduledExecutorService executor;
/**
* Initialize the scheduler service
*/
@PostConstruct
private void init() {
scheduler = schedulerFactory.getScheduler();
executor = Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors());
}
/**
* Add a job to the quartz scheduler
* @param job job to add
* @param key job key to reference the job
*/
public void addJob(Class<? extends Job> job, String key) {
addJob(job, key, key, SCHEDULER_GROUP_NAME);
}
/**
* Add a job to the quartz scheduler
* @param job job to add
* @param jobId job id to identify the job by
* @param frequency frequency on how often to schedule the task
* @param start start time for the task to start
*/
public void addJob(Class<? extends Job> job, String jobId, String frequency, Date start) {
FrequencyType freqType = FrequencyType.getEnum(frequency);
addJob(job, jobId, jobId, SCHEDULER_GROUP_NAME, null, freqType, start, null);
}
/**
* Add a job to the quartz scheduler
* @param job job to add
* @param jobName job name to identify the job by
* @param triggerName trigger name
* @param groupName group name
*/
public void addJob(Class<? extends Job> job, String jobName, String triggerName, String groupName) {
addJob(job, jobName, triggerName, groupName, null, FrequencyType.NOW, null, null);
}
/**
* Add a job to the quartz scheduler
* @param job job to add
* @param jobId job id to identify the job by
* @param frequency frequency on how often to schedule the task
* @param startTime start time for the task to start
* @param endTime end time for the task to end
*/
public void addJob(Class<? extends Job> job, String jobId, String frequency, Date startTime, Date endTime) {
addJob(job, jobId, jobId, SCHEDULER_GROUP_NAME, frequency, startTime, endTime);
}
/**
* Add a job to the quartz scheduler
* @param job job to add
* @param jobName job name to identify the job by
* @param triggerName trigger name to identify what trigger event fired
* @param groupName the group name the job belongs to
* @param frequency frequency on how often to schedule the task
* @param startTime start time for the task to start
*/
public void addJob(Class<? extends Job> job, String jobName, String triggerName, String groupName, FrequencyType frequency, Date startTime) {
addJob(job, jobName, triggerName, groupName, null, frequency, startTime, null);
}
/**
* Add a job to the quartz scheduler
* @param job job to add
* @param jobName job name to identify the job by
* @param triggerName trigger name to identify what trigger event fired
* @param groupName the group name the job belongs to
* @param frequency frequency on how often to schedule the task
* @param startTime start time for the task to start
* @param endTime end time for the task to end
*/
public void addJob(Class<? extends Job> job, String jobName, String triggerName, String groupName, String frequency, Date startTime, Date endTime) {
addJob(job, jobName, triggerName, groupName, null, frequency, startTime, endTime);
}
/**
* Add a job to the quartz scheduler
* @param job job to add
* @param jobName job name to identify the job by
* @param triggerName trigger name to identify what trigger event fired
* @param groupName the group name the job belongs to
* @param params parameters to pass to the job to use
* @param frequency frequency on how often to schedule the task
* @param startTime start time for the task to start
* @param endTime end time for the task to end
*/
public void addJob(Class<? extends Job> job, String jobName, String triggerName, String groupName, JobDataMap params, String frequency, Date startTime, Date endTime) {
FrequencyType freqType = FrequencyType.getEnum(frequency);
addJob(job, jobName, triggerName, groupName, params, freqType, startTime, endTime);
}
/**
* Add a job to the quartz scheduler
* @param job job to add
* @param jobName job name to identify the job by
* @param triggerName trigger name to identify what trigger event fired
* @param groupName the group name the job belongs to
* @param params parameters to pass to the job to use
* @param frequency frequency on how often to schedule the task
* @param startTime start time for the task to start
* @param endTime end time for the task to end
*/
public void addJob(Class<? extends Job> job, String jobName, String triggerName, String groupName, JobDataMap params, FrequencyType frequency, Date startTime, Date endTime) {
try {
//Check to see if the job already exists
JobDetail jd = null;
JobKey jk = new JobKey(jobName, groupName);
if (scheduler.checkExists(jk)) {
jd = scheduler.getJobDetail(jk);
} else {
JobBuilder jb = JobBuilder.newJob(job).withIdentity(jobName, groupName);
if (params != null) {
jb.usingJobData(params);
}
jd = jb.build();
}
//If the trigger already exists. Reschedule it
TriggerKey tk = new TriggerKey(triggerName, groupName);
Trigger oldTrigger = null;
if (scheduler.checkExists(tk)) {
oldTrigger = scheduler.getTrigger(tk);
}
Trigger trigger = null;
switch (frequency) {
case NOW:
case TRIGGERED:
if (startTime == null || (startTime.getTime() <= new Date().getTime())) {
trigger = getTriggerWithoutInterval(tk);
break;
}
trigger = getTriggerWithInterval(tk, frequency, startTime, endTime);
break;
default:
trigger = getTriggerWithInterval(tk, frequency, startTime, endTime);
}
addJob(jd, trigger, oldTrigger);
} catch (Exception ex) {
logger.error("Unable to add job: " + ex.getMessage(), ex);
}
}
/**
* add the job either be adding it if it doesnt exist or rescheduling it if it already does
* @param job details about the job
* @param newTrigger the new job trigger
* @param oldTrigger the old job trigger
*/
private void addJob(JobDetail job, Trigger newTrigger, Trigger oldTrigger) {
try {
if (oldTrigger != null) {
logger.info("Recheduling job %s from old start time of %s to new star time of %s", job.getKey(),
DateService.prettyFormat(oldTrigger.getStartTime()), DateService.prettyFormat(newTrigger.getStartTime()));
scheduler.rescheduleJob(oldTrigger.getKey(), newTrigger);
} else {
logger.info("Scheduling job %s with start time of %s", job.getKey(), DateService.prettyFormat(newTrigger.getStartTime()));
scheduler.scheduleJob(job, newTrigger);
}
} catch (Exception ex) {
logger.error("Unable to add job: " + ex.getMessage(), ex);
}
}
/**
* Unschedule a job from execution by removing the trigger
* The job remains but no longer fires since the trigger is removed
* @param triggerName name of the job trigger to remove
* @param groupName group the job belongs to
*/
public void unscheduleJob(String triggerName, String groupName) {
unscheduleJob(new TriggerKey(triggerName, groupName));
}
/**
* Unschedule a job from execution by removing the trigger
* The job remains but no longer fires since the trigger is removed
* @param key the jobs trigger key
*/
public void unscheduleJob(TriggerKey key) {
try {
if (!scheduler.checkExists(key)) {
logger.info("No trigger found for %s", formatKey(key));
return;
}
logger.info("Unscheduling trigger %s", formatKey(key));
scheduler.unscheduleJob(key);
} catch (Exception ex) {
logger.error(String.format("Unable to unschedule trigger %s: " + ex.getMessage(), formatKey(key)), ex);
}
}
/**
* Delete a job from quartz scheduler. This removes all instances
* from the scheduler and needs to be recreated if the job is to be rescheduled
* @param jobName the name of the job to delete
* @param groupName group the job belongs to
*/
public void deleteJob(String jobName, String groupName) {
deleteJob(new JobKey(jobName, groupName));
}
/**
* Delete a job from quartz scheduler. This removes all instances
* from the scheduler and needs to be recreated if the job is to be rescheduled
* @param key the job key to remove
*/
public void deleteJob(JobKey key) {
try {
if (!scheduler.checkExists(key)) {
logger.info("No job found for %s", formatKey(key));
return;
}
logger.info("Deleting job %s", formatKey(key));
scheduler.deleteJob(key);
} catch (Exception ex) {
logger.error(String.format("Unable to delete job %s: " + ex.getMessage(), formatKey(key)), ex);
}
}
/**
* Schedule a task to execute
* @param task Task to execute
*/
public void schedule(Callable<Void> task) {
schedule(task, 1, TimeUnit.SECONDS);
}
/**
* Schedule a task to execute with a delay
* @param task task to execute
* @param delay Delay length
* @param unit Time unit the delay is based on
*/
public void schedule(Callable<Void> task, Integer delay, TimeUnit unit) {
if (executor == null) {
init();
}
executor.schedule(task, delay, unit);
}
/**
* Schedule a runnable class to execute with a delay
* @param runnable runnable class to execute
* @param delay delay length
* @param unit Time unit the delay is based on
*/
public void schedule(Runnable runnable, Integer delay, TimeUnit unit) {
if (executor == null) {
init();
}
executor.schedule(runnable, delay, unit);
}
/**
* Schedule runnable tasks to execute with a delay
* @param runnables runnables to execute
* @param delay delay length
* @param unit time unit delay is based on.
*/
public void schedule(List<Runnable> runnables, Integer delay, TimeUnit unit) {
runnables
.stream()
.forEach(run -> schedule(run, delay, unit));
}
/**
* Schedule Tasks to execute
* @param tasks List of task Executors
*/
public void schedule(List<TaskScheduler> tasks) {
tasks
.stream()
.forEach(task -> schedule(task, task.getDelay(), task.getUnit()));
}
public void schedule(TaskScheduler task) {
List<TaskScheduler> list = new ArrayList<>();
list.add(task);
schedule(list);
}
/**
* Schedule a runnable class to execute
* @param runnable Runnable class to execute
*/
public void schedule(Runnable runnable) {
schedule(runnable, 1, TimeUnit.MICROSECONDS);
}
/**
* Shutdown the executor service
* @throws SchedulerException thrown when there was an error occurred during scheduler shutdown
*/
public void shutdown() throws SchedulerException {
if (executor != null && !executor.isShutdown()) {
executor.shutdown();
}
if (scheduler != null && !scheduler.isShutdown()) {
scheduler.shutdown(true);
}
}
/**
* Close the executor service and the quartz scheduler
*/
public void close() {
try {
shutdown();
} catch (SchedulerException ie) {
logger.warn("Scheduler was unable to shutdown gracefully: " + ie.getMessage(), ie);
}
}
/**
* Create a trigger without any intervals. <br>
* This is a fire once and done trigger
* @param triggerKey the trigger key comprised of the trigger name and group name
* @return the created trigger
*/
private Trigger getTriggerWithoutInterval(TriggerKey triggerKey) {
TriggerBuilder<?> triggerBuilder = TriggerBuilder
.newTrigger()
.withIdentity(triggerKey);
return triggerBuilder.build();
}
/**
* Create a trigger with an interval
* @param triggerKey the trigger key comprised of the trigger name and group name
* @param frequency how often to execute the job
* @param startTime start time of the job
* @param endTime end time of the job
* @return
*/
private Trigger getTriggerWithInterval(TriggerKey triggerKey, FrequencyType frequency, Date startTime, Date endTime) {
CalendarIntervalScheduleBuilder scheduler = CalendarIntervalScheduleBuilder
.calendarIntervalSchedule()
.withMisfireHandlingInstructionDoNothing();
switch(frequency) {
case HOURLY:
scheduler.withIntervalInHours(1);
break;
case DAILY:
scheduler.withIntervalInDays(1);
break;
case MONTHLY:
scheduler.withIntervalInMonths(1);
break;
case SPECIFIC:
scheduler.withIntervalInHours(1);
break;
case WEEKLY:
scheduler.withIntervalInWeeks(1);
break;
case YEARLY:
scheduler.withIntervalInYears(1);
break;
default:
return getTriggerWithoutInterval(triggerKey);
}
TriggerBuilder<?> triggerBuilder = TriggerBuilder
.newTrigger()
.withIdentity(triggerKey)
.withSchedule(scheduler);
if (startTime != null) {
triggerBuilder.startAt(startTime);
}
if (endTime != null) {
triggerBuilder.endAt(endTime);
}
return triggerBuilder.build();
}
/**
* Create a human readable string of the key
* @param key the key to convert
* @return string representation of the key
*/
private static String formatKey(Object key) {
if (key instanceof JobKey || key instanceof TriggerKey) {
if (key instanceof JobKey) {
JobKey jk = (JobKey)key;
return String.format("%s.%s", jk.getGroup(), jk.getName());
}
TriggerKey tk = (TriggerKey)key;
return String.format("%s.%s", tk.getGroup(), tk.getName());
}
return "";
}
}
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,254 @@
package net.locusworks.portal.services;
import java.security.SecureRandom;
import java.util.Date;
import javax.annotation.PostConstruct;
import org.springframework.stereotype.Service;
import static net.locusworks.common.Charsets.UTF_8;
/**
* Service implementation of random number generator
* @author Isaac Parenteau
* @since 1.0.0-RELEASE
* @version 1.0
*/
@Service
public class SecureRandomService {
private SecureRandom random;
@PostConstruct
public void init() {
random = new SecureRandom(String.valueOf(new Date().getTime()).getBytes(UTF_8));
}
/**
* Generates a user-specified number of random bytes.
* @param data the array to be filled in with random bytes.
*/
public void nextBytes(byte[] data) {
random.nextBytes(data);
}
/**
* Generates a user-specified number of random bytes
* @param arraySize the size of the byte array to be filled with random bytes
* @return bytes filled with random bytes the size of arraySize
*/
public byte[] nextBytes(Integer arraySize) {
byte[] bytes = new byte[arraySize];
nextBytes(bytes);
return bytes;
}
/**
* Returns a pseudorandom, uniformly distributed {@code int} value
* between 0 (inclusive) and Integer.MAX_VALUE, drawn from
* this random number generator's sequence.
*
* @return the next pseudorandom, uniformly distributed {@code int}
* value between zero (inclusive) and {@code bound} (exclusive)
* from this random number generator's sequence
*
*/
public int nextInt() {
return nextInt(Integer.MAX_VALUE);
}
/**
* Returns a pseudorandom, uniformly distributed {@code int} value
* between 0 (inclusive) and the specified value (exclusive), drawn from
* this random number generator's sequence.
*
* @param bound the upper bound (exclusive). Must be positive.
* @return the next pseudorandom, uniformly distributed {@code int}
* value between zero (inclusive) and {@code bound} (exclusive)
* from this random number generator's sequence
*
*/
public int nextInt(int bound) {
return random.nextInt(bound);
}
/**
* Returns the next pseudorandom, uniformly distributed {@code long}
* value from this random number generator's sequence. The general
* contract of {@code nextLong} is that one {@code long} value is
* pseudorandomly generated and returned.
*
* <p>The method {@code nextLong} is implemented by class {@code Random}
* as if by:
* <pre> {@code
* public long nextLong() {
* return ((long)next(32) << 32) + next(32);
* }}</pre>
*
* Because class {@code Random} uses a seed with only 48 bits,
* this algorithm will not return all possible {@code long} values.
*
* @return the next pseudorandom, uniformly distributed {@code long}
* value from this random number generator's sequence
*/
public long nextLong() {
return random.nextLong();
}
/**
* Returns the next pseudorandom, uniformly distributed
* {@code boolean} value from this random number generator's
* sequence. The general contract of {@code nextBoolean} is that one
* {@code boolean} value is pseudorandomly generated and returned. The
* values {@code true} and {@code false} are produced with
* (approximately) equal probability.
*
* <p>The method {@code nextBoolean} is implemented by class {@code Random}
* as if by:
* <pre> {@code
* public boolean nextBoolean() {
* return next(1) != 0;
* }}</pre>
*
* @return the next pseudorandom, uniformly distributed
* {@code boolean} value from this random number generator's
* sequence
*/
public boolean nextBoolean() {
return random.nextBoolean();
}
/**
* Returns the next pseudorandom, uniformly distributed {@code float}
* value between {@code 0.0} and {@code 1.0} from this random
* number generator's sequence.
*
* <p>The general contract of {@code nextFloat} is that one
* {@code float} value, chosen (approximately) uniformly from the
* range {@code 0.0f} (inclusive) to {@code 1.0f} (exclusive), is
* pseudorandomly generated and returned. All 2<sup>24</sup> possible
* {@code float} values of the form <i>m&nbsp;x&nbsp;</i>2<sup>-24</sup>,
* where <i>m</i> is a positive integer less than 2<sup>24</sup>, are
* produced with (approximately) equal probability.
*
* <p>The method {@code nextFloat} is implemented by class {@code Random}
* as if by:
* <pre> {@code
* public float nextFloat() {
* return next(24) / ((float)(1 << 24));
* }}</pre>
*
* <p>The hedge "approximately" is used in the foregoing description only
* because the next method is only approximately an unbiased source of
* independently chosen bits. If it were a perfect source of randomly
* chosen bits, then the algorithm shown would choose {@code float}
* values from the stated range with perfect uniformity.<p>
* [In early versions of Java, the result was incorrectly calculated as:
* <pre> {@code
* return next(30) / ((float)(1 << 30));}</pre>
* This might seem to be equivalent, if not better, but in fact it
* introduced a slight nonuniformity because of the bias in the rounding
* of floating-point numbers: it was slightly more likely that the
* low-order bit of the significand would be 0 than that it would be 1.]
*
* @return the next pseudorandom, uniformly distributed {@code float}
* value between {@code 0.0} and {@code 1.0} from this
* random number generator's sequence
*/
public float nextFloat() {
return random.nextFloat();
}
/**
* Returns the next pseudorandom, uniformly distributed
* {@code double} value between {@code 0.0} and
* {@code 1.0} from this random number generator's sequence.
*
* <p>The general contract of {@code nextDouble} is that one
* {@code double} value, chosen (approximately) uniformly from the
* range {@code 0.0d} (inclusive) to {@code 1.0d} (exclusive), is
* pseudorandomly generated and returned.
*
* <p>The method {@code nextDouble} is implemented by class {@code Random}
* as if by:
* <pre> {@code
* public double nextDouble() {
* return (((long)next(26) << 27) + next(27))
* / (double)(1L << 53);
* }}</pre>
*
* <p>The hedge "approximately" is used in the foregoing description only
* because the {@code next} method is only approximately an unbiased
* source of independently chosen bits. If it were a perfect source of
* randomly chosen bits, then the algorithm shown would choose
* {@code double} values from the stated range with perfect uniformity.
* <p>[In early versions of Java, the result was incorrectly calculated as:
* <pre> {@code
* return (((long)next(27) << 27) + next(27))
* / (double)(1L << 54);}</pre>
* This might seem to be equivalent, if not better, but in fact it
* introduced a large nonuniformity because of the bias in the rounding
* of floating-point numbers: it was three times as likely that the
* low-order bit of the significand would be 0 than that it would be 1!
* This nonuniformity probably doesn't matter much in practice, but we
* strive for perfection.]
*
* @return the next pseudorandom, uniformly distributed {@code double}
* value between {@code 0.0} and {@code 1.0} from this
* random number generator's sequence
* @see Math#random
*/
public double nextDouble() {
return random.nextDouble();
}
/**
* Returns the next pseudorandom, Gaussian ("normally") distributed
* {@code double} value with mean {@code 0.0} and standard
* deviation {@code 1.0} from this random number generator's sequence.
* <p>
* The general contract of {@code nextGaussian} is that one
* {@code double} value, chosen from (approximately) the usual
* normal distribution with mean {@code 0.0} and standard deviation
* {@code 1.0}, is pseudorandomly generated and returned.
*
* <p>The method {@code nextGaussian} is implemented by class
* {@code Random} as if by a threadsafe version of the following:
* <pre> {@code
* private double nextNextGaussian;
* private boolean haveNextNextGaussian = false;
*
* public double nextGaussian() {
* if (haveNextNextGaussian) {
* haveNextNextGaussian = false;
* return nextNextGaussian;
* } else {
* double v1, v2, s;
* do {
* v1 = 2 * nextDouble() - 1; // between -1.0 and 1.0
* v2 = 2 * nextDouble() - 1; // between -1.0 and 1.0
* s = v1 * v1 + v2 * v2;
* } while (s >= 1 || s == 0);
* double multiplier = StrictMath.sqrt(-2 * StrictMath.log(s)/s);
* nextNextGaussian = v2 * multiplier;
* haveNextNextGaussian = true;
* return v1 * multiplier;
* }
* }}</pre>
* This uses the <i>polar method</i> of G. E. P. Box, M. E. Muller, and
* G. Marsaglia, as described by Donald E. Knuth in <i>The Art of
* Computer Programming</i>, Volume 3: <i>Seminumerical Algorithms</i>,
* section 3.4.1, subsection C, algorithm P. Note that it generates two
* independent values at the cost of only one call to {@code StrictMath.log}
* and one call to {@code StrictMath.sqrt}.
*
* @return the next pseudorandom, Gaussian ("normally") distributed
* {@code double} value with mean {@code 0.0} and
* standard deviation {@code 1.0} from this random number
* generator's sequence
*/
public double nextGaussian() {
return random.nextGaussian();
}
}
@@ -0,0 +1,219 @@
package net.locusworks.portal.services;
import java.util.List;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import java.util.TreeMap;
import java.util.stream.Collectors;
import javax.mail.Message;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.MailSendException;
import org.springframework.stereotype.Service;
import net.locusworks.common.logger.ApplicationLogger;
import net.locusworks.common.logger.ApplicationLoggerFactory;
import net.locusworks.common.logger.SystemDebugLogger;
import net.locusworks.common.utils.Utils;
import net.locusworks.portal.common.exceptions.PortalException;
import net.locusworks.portal.common.services.AESService;
import net.locusworks.portal.database.repos.KeyValueRepository;
import net.locusworks.portal.json.ApplicationUserRequest;
import net.locusworks.portal.json.ValueRequest;
/**
* Service to send emails to recipients
* @author Isaac Parenteau
* @since 1.0.0-RELEASE
* @version 1.0
*/
@Service
public class SendMailService {
private static ApplicationLogger logger = ApplicationLoggerFactory.getLogger(SendMailService.class);
//List of smtp properties to set when sending an email
public static final String SMTP_ENABLED = "smtp.enabled";
public static final String MAIL_TRANSPORT = "mail.transport.protocol";
public static final String SMTP_PORT = "mail.smtp.port";
public static final String SMTP_HOST = "mail.smtp.host";
public static final String SMTP_AUTH = "mail.smtp.auth";
public static final String SMTP_START_TLS = "mail.smtp.starttls.enable";
public static final String SMTP_SSL_TRUST = "mail.smtp.ssl.trust";
public static final String SMTP_START_TLS_REQ = "mail.smtp.starttls.required";
public static final String SMTP_FROM_USER = "smtp.from.user";
public static final String SMTP_USERNAME = "smtp.username";
public static final String SMTP_PASSWORD = "smtp.password";
public static final String MAIL_DEBUG = "mail.debug";
//collect the properties into a set
public static final Set<String> SMTP_PARAM_LIST = Utils.toSet(SMTP_ENABLED, MAIL_TRANSPORT,
SMTP_PORT, SMTP_HOST, SMTP_AUTH, SMTP_SSL_TRUST,
SMTP_START_TLS, SMTP_START_TLS_REQ, SMTP_FROM_USER,
SMTP_USERNAME, SMTP_PASSWORD, MAIL_DEBUG);
@Autowired
private KeyValueRepository kvRepo;
@Autowired
private AESService aesService;
private Properties mailProperties;
/**
* Initialize the properties with the values from the database
*/
private void init() {
logger.debug("Initializing SendMailService");
mailProperties = new Properties();
kvRepo.findByReferenceKeyIn(SMTP_PARAM_LIST)
.stream()
.map(item -> new ValueRequest(item.getId(), item.getReferenceKey(), item.getReferenceValue()))
.forEach(kv -> {
mailProperties.setProperty(kv.getName(), kv.getValue());
});
}
/**
* Send an email to the given recipients
* @param recipients list of user recipients to send the email to
* @param from email address of the user sending the email
* @param subject Subject of the email
* @param body email body content
* @return information about the message sent
* @throws Exception thrown when an exception occurs when trying to send an email
*/
public MimeMessage sendEmail(List<ApplicationUserRequest> recipients, ApplicationUserRequest from, String subject, String body) throws Exception {
return sendEmail(recipients, from.getEmail(), subject, body, null);
}
/**
* Send an email to the given recipients
* @param recipients list of user recipients to send the email to
* @param subject Subject of the email
* @param body email body content
* @return information about the message sent
* @throws Exception thrown when an exception occurs when trying to send an email
*/
public MimeMessage sendEmail(List<ApplicationUserRequest> recipients, String subject, String body) throws Exception {
return sendEmail(recipients, null, subject, body, null);
}
/**
* Send an email to the given recipients
* @param recipients list of user recipients to send the email to
* @param subject Subject of the email
* @param body email body content
* @param messageFormatter Formatter that holds key words to look for in the email body to replace with the content in the formatter
* @return information about the message sent
* @throws Exception thrown when an exception occurs when trying to send an email
*/
public MimeMessage sendEmail(List<ApplicationUserRequest> recipients, String subject, String body, EmailMessageFormatter messageFormatter) throws Exception {
return sendEmail(recipients, null, subject, body, messageFormatter);
}
/**
* Send an email to the given recipients
* @param recipients list of user recipients to send the email to
* @param fromEmail email address of the user sending the email
* @param subject Subject of the email
* @param body email body content
* @param messageFormatter Formatter that holds key words to look for in the email body to replace with the content in the formatter
* @return information about the message sent
* @throws Exception thrown when an exception occurs when trying to send an email
*/
public MimeMessage sendEmail(List<ApplicationUserRequest> recipients, String fromEmail, String subject, String body, EmailMessageFormatter messageFormatter) throws Exception {
init();
boolean enabled = Boolean.parseBoolean(mailProperties.getProperty(SMTP_ENABLED));
if(!enabled) {
throw new MailSendException("Sending messages not enabled");
}
if (Utils.isEmptyString(fromEmail)) {
Object obj = mailProperties.get(SMTP_FROM_USER);
if (obj == null) {
throw PortalException.generic("Unable to send email. No from email address provided.");
}
fromEmail = String.valueOf(obj);
}
Session session = Session.getDefaultInstance(mailProperties);
session.setDebugOut(new SystemDebugLogger(ApplicationLoggerFactory.getLogger("Javax.Mail")));
MimeMessage message = new MimeMessage(session);
message.setFrom(new InternetAddress(fromEmail));
//Create the array of Internet addresses from the recipients email addresses
InternetAddress[] recipList = recipients
.stream()
.map(item -> {
try {
return new InternetAddress(item.getEmail());
} catch (AddressException e) {
logger.warn(String.format("Unable to convert %s to internet address", item.getEmail()), e);
return null;
}
})
.filter(item -> item != null)
.collect(Collectors.toList())
.toArray(new InternetAddress[recipients.size()]);
message.addRecipients(Message.RecipientType.TO, recipList);
message.setSubject(subject);
message.setContent(formatBody(body, messageFormatter), "text/html; charset=utf-8");
boolean auth = Boolean.parseBoolean(mailProperties.getProperty(SMTP_AUTH));
String username = mailProperties.getProperty(SMTP_USERNAME);
String password = aesService.decrypt(mailProperties.getProperty(SMTP_PASSWORD));
logger.info("Sending email...");
// Send message (optional login)
Transport transport = session.getTransport();
if (auth) {
transport.connect(username, password);
} else {
transport.connect();
}
transport.sendMessage(message, message.getAllRecipients());
transport.close();
return message;
}
/**
* Format the message body with content specified inthe message formatter
* @param body the body content to look at
* @param messageFormatter Formatter that holds key words to look for in the email body to replace with the content in the formatter
* @return body content
*/
private Object formatBody(String body, EmailMessageFormatter messageFormatter) {
if (messageFormatter == null) {
return body;
}
String newBody = new String(body);
for (Entry<String, String> entry : messageFormatter.entrySet()) {
newBody = newBody.replace(entry.getKey(), entry.getValue());
}
return newBody;
}
/**
* Helper class to format email body content
* @author Isaac Parenteau
* @since 1.0.0-RELEASE
* @version 1.0
*/
public static class EmailMessageFormatter extends TreeMap<String, String> {
private static final long serialVersionUID = 3879849923247246165L;
}
}
@@ -0,0 +1,280 @@
package net.locusworks.portal.services;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import net.locusworks.common.logger.ApplicationLogger;
import net.locusworks.common.logger.ApplicationLoggerFactory;
import net.locusworks.portal.common.exceptions.PortalException;
import net.locusworks.portal.common.services.AESService;
import net.locusworks.portal.database.entities.EmailTemplate;
import net.locusworks.portal.database.entities.EnvironmentVariable;
import net.locusworks.portal.database.entities.KeyValue;
import net.locusworks.portal.database.repos.EmailTemplateRepository;
import net.locusworks.portal.database.repos.EnvironmentVariableRepository;
import net.locusworks.portal.database.repos.KeyValueRepository;
import net.locusworks.portal.json.EmailTemplateRequest;
import net.locusworks.portal.json.EnvironmentVariableRequest;
import net.locusworks.portal.json.ValueRequest;
import static net.locusworks.common.Charsets.UTF_8;
/**
* Service for adding environment variables and email templates to the system
* @author Isaac Parenteau
* @since 1.0.0-RELEASE
* @version 1.0
*/
@Service
public class SystemService {
private static ApplicationLogger logger = ApplicationLoggerFactory.getLogger(SystemService.class);
@Autowired
private EnvironmentVariableRepository envRepo;
@Autowired
private EmailTemplateRepository emailTmplRepo;
@Autowired
private KeyValueRepository kvRepo;
@Autowired
private AESService aesService;
/**
* Add environment variables to the system
* @param envVars list of environment variables to add
* @return list of newly created EnvironmentVariableRequest
* @throws Exception thrown when an error occurs when trying to add the environment variables
*/
public List<EnvironmentVariableRequest> addEnvironmentVariables(List<EnvironmentVariableRequest> envVars) throws Exception {
if (envVars == null || envVars.isEmpty()) {
throw PortalException.generic("no environment variables specified");
}
List<EnvironmentVariable> variablesToAdd = envVars.stream()
.map(env -> addVariable(env))
.filter(env -> env != null)
.collect(Collectors.toList());
if (!variablesToAdd.isEmpty()) {
envRepo.saveAll(variablesToAdd);
}
return getAllEnvironmentVariables();
}
/**
* Delete environment variable from the system
* @param envVars list of environment variables to delete
* @throws Exception thrown when an error occurrs when trying to delete the variables
*/
public List<EnvironmentVariableRequest> deleteEnvironmentVariables(List<EnvironmentVariableRequest> envVars) throws Exception {
Set<Integer> idsToDelete = envVars.stream().map(ev -> ev.getId()).collect(Collectors.toSet());
List<EnvironmentVariableRequest> deletedEnvVars = envRepo.findAllById(idsToDelete)
.stream()
.map(ev -> makeEnvVarRequest(ev))
.collect(Collectors.toList());
if (!idsToDelete.isEmpty()) {
envRepo.delete(idsToDelete);
}
return deletedEnvVars;
}
/**
* Get all the system environment variables
* @return list of environment variables
*/
public List<EnvironmentVariableRequest> getAllEnvironmentVariables() {
return envRepo.findAll()
.stream()
.map(ev -> makeEnvVarRequest(ev))
.collect(Collectors.toList());
}
public void updateEncryptedCredentials(String seed) {
KeyValue kv = kvRepo.findByReferenceKey(SendMailService.SMTP_PASSWORD);
if (kv == null) return;
kv.setReferenceValue(aesService.changeEncryptionValue(kv.getReferenceValue(), seed).getBytes(UTF_8));
kvRepo.save(kv);
}
/**
* Update smtp configuration with new value
* @param request the smtp configuration to update with
* @return list of the new smtp configuration values
*/
public List<ValueRequest> updateSmtpConfig(List<ValueRequest> request) {
List<KeyValue> smtpConfig = request
.stream()
.map(item -> {
KeyValue kv = makeKeyValue(item);
if (kv.getReferenceKey().equals(SendMailService.SMTP_PASSWORD)) {
kv.setReferenceValue(aesService.encrypt(kv.getReferenceValue()));//If the name is the smtp password, lets encrypt it
}
return kv;
})
.collect(Collectors.toList());
kvRepo.saveAll(smtpConfig);
return getSmtpConfiguration();
}
/**
* Get the current smtp configuration
* @return smtp configuration list
*/
public List<ValueRequest> getSmtpConfiguration() {
return kvRepo.findByReferenceKeyIn(SendMailService.SMTP_PARAM_LIST)
.stream()
.map(item -> new ValueRequest(item.getId(), item.getReferenceKey(), item.getReferenceValue()))
//Filter out the smtp password from being displayed
.filter(item -> !item.getName().equals(SendMailService.SMTP_PASSWORD))
.collect(Collectors.toList());
}
/**
* Convert a EnvironmentVariableRequest to EnvironmentVariable JPO entity
* @param evr the request to convert
* @return EnvironmentVariable JPO entity
*/
private EnvironmentVariable addVariable(EnvironmentVariableRequest evr) {
EnvironmentVariable ev = null;
if (evr.getId() != null) {
ev = envRepo.findOne(evr.getId());
if (ev == null) {
logger.warn("Unable to find environment variable with id of %d", evr.getId());
return null;
}
}
if (ev == null) {
ev = new EnvironmentVariable();
ev.setCreatedBy(UserSecurityService.getApplicationUser());
ev.setDateCreated(new Date());
}
ev.setLastUpdated(new Date());
ev.setName(evr.getName());
ev.setValue(evr.getValue());
return ev;
}
/**
* Add or update an email template
* @param request information about the email template to use for the add/update
* @return the newly created or updated template
* @throws Exception thrown when an error occurs during the transaction
*/
public EmailTemplateRequest addEmailTemplate(EmailTemplateRequest request) throws Exception {
EmailTemplate et = null;
if (request.getId() == null) {
et = emailTmplRepo.findByEmailName(request.getEmailName());
if (et != null) {
throw PortalException.duplicateEntry("Email entry with name %s already exits", request.getEmailName());
}
} else {
et = emailTmplRepo.findOne(request.getId());
}
if (et == null) {
et = new EmailTemplate();
et.setEmailName(request.getEmailName());
et.setCreatedBy(UserSecurityService.getApplicationUser());
et.setDateCreated(new Date());
}
et.setEmailSubject(request.getEmailSubject());
et.setEmailMessage(request.getEmailMessage().getBytes(UTF_8));
emailTmplRepo.save(et);
return makeEmailTemplateRequest(et);
}
/**
* Delete an email template from the system
* @param request the template to delete
* @return the deleted template
* @throws Exception thrown when an error occurs during the delete process
*/
public EmailTemplateRequest deleteEmailTemplate(EmailTemplateRequest request) throws Exception {
if (request.getId() == null) {
throw PortalException.illegalArgument("No Id provided for the template to delete");
}
Optional<EmailTemplate> et = emailTmplRepo.findById(request.getId());
if (!et.isPresent()) {
throw PortalException.noEntryExists("No email template with id of %d", request.getId());
}
EmailTemplateRequest etr = makeEmailTemplateRequest(et.get());
emailTmplRepo.delete(etr.getId());
return etr;
}
/**
* Get a list of all the email templates
* @return list of email templates
*/
public List<EmailTemplateRequest> getEmailTemplates() {
return emailTmplRepo.findAll()
.stream()
.map(et -> makeEmailTemplateRequest(et))
.collect(Collectors.toList());
}
private EmailTemplateRequest makeEmailTemplateRequest(EmailTemplate et) {
EmailTemplateRequest etr = new EmailTemplateRequest(et.getId(), et.getEmailName(), et.getEmailSubject(), et.getEmailMessage());
etr.setId(et.getId());
etr.setCreatedBy(et.getCreatedBy().getId());
etr.setDateCreated(et.getDateCreated());
return etr;
}
/**
* Convert EnvironmentVariable JPO Entity to a EnvironmentVariableRequest object
* @param ev the entity to convert
* @return EnvironmentVariableRequest
*/
private EnvironmentVariableRequest makeEnvVarRequest(EnvironmentVariable ev) {
EnvironmentVariableRequest request = new EnvironmentVariableRequest();
request.setId(ev.getId());
request.setName(ev.getName());
request.setValue(ev.getValue());
request.setDateCreated(ev.getDateCreated());
request.setCreatedBy(ev.getCreatedBy().getId());
request.setLastUpdated(ev.getLastUpdated());
request.setIsNew(false);
return request;
}
/**
* Make a KeyValue JPO Entity object
* @param item the Value Request to convert
* @return the converted item
*/
private KeyValue makeKeyValue(ValueRequest item) {
KeyValue kv = kvRepo.findByReferenceKey(item.getName());
if (kv == null) {
kv = new KeyValue();
kv.setReferenceKey(item.getName());
kv.setDateCreated(new Date());
kv.setCreatedBy(UserSecurityService.getApplicationUser());
}
kv.setReferenceValue(item.getValue().getBytes(UTF_8));
return kv;
}
}
@@ -0,0 +1,503 @@
package net.locusworks.portal.services;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import net.locusworks.common.crypto.HashSalt;
import net.locusworks.common.logger.ApplicationLogger;
import net.locusworks.common.logger.ApplicationLoggerFactory;
import net.locusworks.common.utils.StreamUtils;
import net.locusworks.common.utils.Utils;
import net.locusworks.portal.common.exceptions.PortalException;
import net.locusworks.portal.common.utils.DateService;
import net.locusworks.portal.database.entities.ApplicationUser;
import net.locusworks.portal.database.entities.Permission;
import net.locusworks.portal.database.entities.PermissionGroup;
import net.locusworks.portal.database.entities.UserAttribute;
import net.locusworks.portal.database.entities.UserPasswordTracking;
import net.locusworks.portal.database.repos.ApplicationUserRepository;
import net.locusworks.portal.database.repos.PermissionGroupRepository;
import net.locusworks.portal.database.repos.PermissionRepository;
import net.locusworks.portal.database.repos.UserAttributeRepository;
import net.locusworks.portal.database.repos.UserPasswordTrackingRepository;
import net.locusworks.portal.json.ApplicationUserRequest;
import net.locusworks.portal.providers.CustomAuthenticationProvider;
import static net.locusworks.common.utils.Constants.TRUE;
/**
* Service handling user security. It keeps track of those who are logged in
* and the current user who is logged in via their authentication token.
* @author Isaac Parenteau
* @since 1.0.0-RELEASE
* @version 1.0
*/
@Service
public class UserSecurityService {
private static ApplicationLogger logger = ApplicationLoggerFactory.getLogger(UserSecurityService.class);
private static Map<String, Set<Integer>> SESSIONS = new HashMap<>();
private static final HashSet<String> PERMISSION_GROUPS = new HashSet<>();
@Autowired
private DateService dateService;
@Autowired
private ApplicationUserRepository appUserRepo;
@Autowired
private PermissionGroupRepository permGroupRepo;
@Autowired
private PermissionRepository permRepo;
@Autowired
private UserAttributeRepository userAtrRepo;
@Autowired
private UserPasswordTrackingRepository usrPwdTrkRepo;
@Autowired
private CustomAuthenticationProvider provider;
/**
* Initialize the user security service upon server startup
*/
@PostConstruct
public void init() {
//Load all the permission groups in a hash set for granted authority
for (Permission pg : permRepo.findAll()) {
PERMISSION_GROUPS.add(pg.getName());
}
}
/**
* Save the user session upon login to be retrieved later
* @param sessionId the session of the user
* @param auId the application user id
*/
public static void saveSession(String sessionId, Integer auId) {
synchronized (SESSIONS) {
if (!SESSIONS.containsKey(sessionId)) {
SESSIONS.put(sessionId, new HashSet<>());
}
SESSIONS.get(sessionId).add(auId);
}
}
/**
* Remove a session from the collection. This is done at user logout
* @param sessionId the session Id to remove
*/
public static void removeSessions(String sessionId) {
synchronized (SESSIONS) {
SESSIONS.remove(sessionId);
}
}
/**
* Get the current session user ids
* @return the set of ids logged in
*/
public static Set<Integer> getSessionUserIds() {
synchronized (SESSIONS) {
Set<Integer> ids = new HashSet<>();
for (Set<Integer> idSet : SESSIONS.values()) {
ids.addAll(idSet);
}
return ids;
}
}
/**
* Save a new user to the database
* @param request Request with new user information
* @return the saved user
* @throws Exception exception
*/
public ApplicationUserRequest saveApplicationUser(ApplicationUserRequest request) throws Exception {
return saveApplicationUser(request.getEmail(), request.getPassword(), request.getFirstName(), request.getLastName(),
request.getRole(), request.getEffectiveDate(), request.getExpirationDate());
}
/**
* Save a new user to the database
* @param email Email address of the new user
* @param password Password of the new user
* @param firstName First name of the user
* @param lastName Last name of the user
* @param permissionGroup Permission group the user belongs to
* @param effectiveDate effective date the user can start logging in
* @param expirationDate the end date on which the user can no longer log in
* @return the saved application user
* @throws Exception exception
*/
public ApplicationUserRequest saveApplicationUser(String email, String password, String firstName, String lastName, String permissionGroup,
Date effectiveDate, Date expirationDate) throws Exception {
ApplicationUser au = appUserRepo.findByEmailAddressTxt(email);
boolean isNew = false;
if (au == null) {
au = new ApplicationUser();
au.setEmailAddressTxt(email);
isNew = true;
}
au.setEffectiveDate(effectiveDate == null ? new Date() : effectiveDate);
au.setExpirationDate(expirationDate == null ? dateService.getUserExpirationDate() : expirationDate);
appUserRepo.save(au);
UserAttribute userAtr = userAtrRepo.findByApplicationUserId(au.getId());
if (userAtr == null) {
userAtr = new UserAttribute();
userAtr.setApplicationUser(au);
}
PermissionGroup role = permGroupRepo.findByName(permissionGroup);
userAtr.setFirstName(Utils.safeString(firstName));
userAtr.setLastName(Utils.safeString(lastName));
userAtr.setPermissionGroup(role);
userAtrRepo.save(userAtr);
if (au != null && password == null) {
return makeApplicationUserRequest(au, isNew);
}
List<UserPasswordTracking> usrPwdTrkgList = usrPwdTrkRepo.findByApplicationUserOrderByEffectiveDateDesc(au);
UserPasswordTracking usrPwdTrkg = null;
if (usrPwdTrkgList == null || usrPwdTrkgList.isEmpty()) {
usrPwdTrkg = new UserPasswordTracking();
usrPwdTrkg.setApplicationUser(au);
} else {
usrPwdTrkg = usrPwdTrkgList.get(0);
}
usrPwdTrkg.setPasswordTxt(HashSalt.createHash(password));
usrPwdTrkg.setEffectiveDate(effectiveDate == null ? new Date() : effectiveDate);
usrPwdTrkg.setExpirationDate(expirationDate == null ? dateService.getUserExpirationDate() : expirationDate);
usrPwdTrkRepo.save(usrPwdTrkg);
return makeApplicationUserRequest(au, isNew);
}
/**
* Delete an application user from the database
* @param request Request the data that needs to be deleted
* @throws Exception exception
*/
public void deleteApplicationUser(ApplicationUserRequest request) throws Exception {
ApplicationUser au = appUserRepo.findByEmailAddressTxt(request.getEmail());
if (au == null) {
throw PortalException.noEntryExists("No entry found for application user with email of " + request.getEmail());
}
ApplicationUser currentUser = getApplicationUser();
if (currentUser.getEmailAddressTxt().equals(au.getEmailAddressTxt())) {
throw PortalException.generic("Unable to delete own account");
}
int id = au.getId();
userAtrRepo.delete(au.getUserAttribute().getId());
appUserRepo.delete(id);
au = appUserRepo.findOne(id);
if (au != null) {
throw PortalException.generic("Unable to delete user");
}
}
/**
* Get the current application user from the auth token
* @return the current application user JPO Entity
*/
public Map<String, Object> getAuthJsonData() {
Map<String, Object> data = new HashMap<>();
try {
ApplicationUserRequest aur = getCurrentUser();
List<String> grantedAuthorities = UserSecurityService.getGrantedAuthority() //Get the list of granted authority names
.stream().map(ga -> ga.getAuthority()).collect(Collectors.toList());
data = Utils.convertToMap(aur);
data.put("authorities", grantedAuthorities);
} catch (Exception ex) {
logger.error("Error building authentication json data", ex);
}
return data;
}
/**
* Get the current user
* @return the current application user POJO
*/
public static ApplicationUser getApplicationUser() {
if (isLoggedIn()) {
UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
return (ApplicationUser) token.getDetails();
}
return null;
}
/**
* Get the current user
* @return the current application user POJO
*/
public ApplicationUserRequest getCurrentUser() {
ApplicationUser au = getApplicationUser();
return au == null ? null : makeApplicationUserRequest(au);
}
/**
* Check to see if the user is currently logged in
* @return true if logged in false otherwise
*/
public static boolean isLoggedIn() {
boolean isLoggedIn = false;
try {
for (GrantedAuthority ga : getGrantedAuthority()) {
if (PERMISSION_GROUPS.contains(ga.getAuthority())) {
isLoggedIn = true;
break;
}
}
} catch (Exception ex) { }
return isLoggedIn;
}
/**
* Checks to see if a user has a certain permission enabled
* @param permission the permission to check
* @return true if the user has permission false otherwise
*/
public boolean hasPermission(String permission) {
for (GrantedAuthority ga : getGrantedAuthority()) {
if (permission.equals(ga.getAuthority())) {
return true;
}
}
return false;
}
/**
* Checks to see if the user has at least one permission enabled in the list
* of provided permissions
* @param permissions Permissions to check against
* @return true if the user has at least one permission enabled, false otherwise
*/
public boolean hasAtleastOnePermission(String... permissions) {
for (String permission : permissions) {
if(hasPermission(permission)) return true;
}
return false;
}
/**
* Return the granted authorities of currently signed in user
* @see net.locusworks.portal.providers.patchrepo.config.CustomAuthenticationProvider
* @return auths ArrayList of grantedAuthorities
*/
private static Collection<GrantedAuthority> getGrantedAuthority() {
Collection<GrantedAuthority> auths = new ArrayList<>();
try {
auths = ((AbstractAuthenticationToken) SecurityContextHolder.getContext().getAuthentication()).getAuthorities();
} catch (Exception e) { }
return auths;
}
/**
* Get a specific user
* @param auId the application user id to get
* @return the specific user information
*/
public ApplicationUserRequest getApplicationUser(Integer auId) {
ApplicationUser au = appUserRepo.findOne(auId);
return au == null ? null : makeApplicationUserRequest(au);
}
/**
* Retrieve an application User POJO
* @param email email address of the user to find
* @return the application user pojo
*/
public ApplicationUserRequest getApplicationUser(String email) {
ApplicationUser au = findApplicationUser(email);
return au == null ? null : makeApplicationUserRequest(au);
}
/**
* Retrieve an application User entity
* @param email email address of the user to find
* @return the application user entity
*/
public ApplicationUser findApplicationUser(String email) {
return appUserRepo.findByEmailAddressTxt(email);
}
/**
* Find the application user
* @param username username of the application user
* @param password password for the application user
* @return the application user associated with the user name and password
*/
public ApplicationUserRequest getApplicationUser(String username, String password) {
ApplicationUser au = appUserRepo.findByEmailAddressTxt(username);
try {
if (au == null) {
return null;
}
UserPasswordTracking upt = findUserPassword(au);
if (HashSalt.validatePassword(password, upt.getPasswordTxt())) {
return makeApplicationUserRequest(au);
}
return null;
} catch (Exception ex) {
return null;
}
}
/**
* Checks to see if the current user is active
* @param user User to check
* @return true if the user is active false otherwise
*/
public boolean isActive(ApplicationUser user) {
Date now = new Date();
if ((now.after(user.getEffectiveDate()) && now.before(user.getExpirationDate()))) {
return true;
}
return false;
}
/**
* Get the current users password
* @param id Id of the user
* @return the password entry
*/
public UserPasswordTracking findUserPassword(Integer id) {
if (id == null) {
return null;
}
return findUserPassword(appUserRepo.findOne(id));
}
/**
* Find the current users password
* @param au The application user to get the password for
* @return the password entry
*/
public UserPasswordTracking findUserPassword(ApplicationUser au) {
if (au == null) {
return null;
}
List<UserPasswordTracking> uptList = usrPwdTrkRepo.findByApplicationUserOrderByEffectiveDateDesc(au);
if (uptList == null || uptList.isEmpty()) {
return null;
}
return uptList.get(0);
}
/**
* Log an application user in
* @param auId the application user id
* @return true if the user is logged in false otherwise
*/
public boolean loginAuId(Integer auId) {
return loginAuId(appUserRepo.findOne(auId));
}
/**
* Log an application user in
* @param user he application user
* @return true if the user is logged in false otherwise
*/
public boolean loginAuId(ApplicationUser user) {
boolean success = false;
try {
UsernamePasswordAuthenticationToken token = provider.authenticate(user);
if (token != null) {
SecurityContextHolder.getContext().setAuthentication(token);
success = true;
} else {
// some error message?
}
} catch (Exception e) {
logger.error("Problem logging in.", e);
}
return success;
}
/**
* Get all the users
* @return list of all the users
*/
public List<ApplicationUserRequest> getAllUsers() {
return StreamUtils.asStream(appUserRepo.findAll().iterator())
.map(user -> makeApplicationUserRequest(user))
.collect(Collectors.toList());
}
/**
* Convert the ApplicationUser JPO entity to a ApplicationUserRequest for json serialization
* @param au the application user to convert
* @return ApplicationUserRequest
*/
private ApplicationUserRequest makeApplicationUserRequest(ApplicationUser au) {
return makeApplicationUserRequest(au, false);
}
/**
* Convert the ApplicationUser JPO entity to a ApplicationUserRequest for json serialization
* @param au the application user to convert
* @param isNew mark the user as new
* @return ApplicationUserRequest
*/
public ApplicationUserRequest makeApplicationUserRequest(ApplicationUser au, boolean isNew) {
ApplicationUserRequest aur = new ApplicationUserRequest();
aur.setAuId(au.getId());
aur.setId(au.getId());
aur.setEmail(au.getEmailAddressTxt());
aur.setUsername(au.getEmailAddressTxt());
aur.setEffectiveDate(au.getEffectiveDate());
aur.setExpirationDate(au.getExpirationDate());
aur.setNew(isNew);
UserAttribute ua = userAtrRepo.findByApplicationUser(au);
aur.setFirstName(ua.getFirstName());
aur.setLastName(ua.getLastName());
aur.setRole(ua.getPermissionGroup().getName());
aur.setEnabled(ua.getPermissionGroup().getEnabled() == TRUE && isActive(au));
return aur;
}
}
@@ -0,0 +1,64 @@
package net.locusworks.portal.services;
import org.springframework.context.ApplicationEvent;
import org.springframework.stereotype.Service;
import org.springframework.web.context.WebApplicationContext;
import net.locusworks.common.logger.ApplicationLogger;
import net.locusworks.common.logger.ApplicationLoggerFactory;
import net.locusworks.portal.handlers.StompMessageHandler;
import net.locusworks.portal.handlers.SystemWebSocketHandler;
/**
* Web communication service for websockets.
* Allows for (un)subscribing from topics and sending events through
* the established socket
* @author Isaac Parenteau
* @since 1.0.0-RELEASE
* @version 1.0
*/
@Service
public class WebCommService extends CommService {
private static ApplicationLogger logger = ApplicationLoggerFactory.getLogger(WebCommService.class);
private static SystemWebSocketHandler webSocketHandler;
/**
* Send an internal application event via Spring
* @param event event to send
*/
public static void sendEvent(ApplicationEvent event) {
((WebApplicationContext)event.getSource()).publishEvent(event);
}
/**
* Subscribe to a websocket topic
* @param handler The stomp message handler to subscribe to
* @throws Exception exception
*/
public static void subscribeToTopic(StompMessageHandler handler) throws Exception {
// do we already have a connection open?
if (webSocketHandler == null) {
webSocketHandler = new SystemWebSocketHandler();
}
webSocketHandler.addSubscription(handler);
}
/**
* Unsubscribe from a topic
* @param topic Topic to unsubscribe from
*/
public static void unsubscribeFromTopic(String topic) {
try {
// if there is a SystemWebSocketHandler call unsubscribe it should send the unsubscribe
// message and remove the handler from the map
if (webSocketHandler != null) {
webSocketHandler.unsubscribe(topic);
}
} catch (Exception e) {
logger.error("Problem unsubscribing from " + topic, e);
}
}
}
@@ -0,0 +1,10 @@
/**
* The services packages work in conjunction with the controllers.
* The controllers call the services to perform various tasks in which
* the services will return the calculated results back to the controllers
* which in turn get sent back to the client.
* @author J65594
* @since 1.0.0-RELEASE
* @version 1.0
*/
package net.locusworks.portal.services;
@@ -0,0 +1,46 @@
package net.locusworks.portal.util;
import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.scheduling.quartz.SpringBeanJobFactory;
import org.springframework.stereotype.Component;
/**
* Component to hold the application context.
* To allow the ability to grab spring beans when needed
* @author Isaac Parenteau
* @since 1.0.0-RELEASE
* @version 1.0
*/
@Component
public final class ApplicationContextHolder extends SpringBeanJobFactory implements ApplicationContextAware {
private static ApplicationContext context;
private transient AutowireCapableBeanFactory beanFactory;
@Override
public void setApplicationContext(ApplicationContext ctx) throws BeansException {
if (context == null) {
beanFactory = ctx.getAutowireCapableBeanFactory();
context = ctx;
}
}
@Override
protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
final Object job = super.createJobInstance(bundle);
beanFactory.autowireBean(job);
return job;
}
/**
* Get the current application context
* @return the current context
*/
public static ApplicationContext getContext() {
return context;
}
}
@@ -0,0 +1,75 @@
package net.locusworks.portal.util;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import net.locusworks.common.logger.ApplicationLogger;
import net.locusworks.common.logger.ApplicationLoggerFactory;
/**
* Class to help download files
* @author Isaac Parenteau
* @since 1.0.0-RELEASE
* @version 1.0
*/
public class FileDownloadServletResponse extends HttpServletResponseWrapper implements AutoCloseable {
private static ApplicationLogger logger = ApplicationLoggerFactory.getLogger(FileDownloadServletResponse.class);
/**
* Constructor to handle file downloads
* @param response The response to send the data through
* @param fileName name of the file
*/
public FileDownloadServletResponse(HttpServletResponse response, String fileName) {
super(response);
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", String.format("attachment; filename=\"%s\"", fileName));
}
/**
* Send the file to the client requesting the download
* @param file The file to send
* @return true if the file was sent successfully, false otherwise
*/
public boolean sendFile(String file) {
return sendFile(Paths.get(file));
}
/**
* Send the file to the client request the download
* @param file the file to send
* @return true if the file was sent successfully, false otherwise
*/
public boolean sendFile(File file) {
Path filePath = Paths.get(file.getPath());
return sendFile(filePath);
}
/**
* Send the file to the client requesting the download.
* Stream the file instead of sending the whole file at once
* @param filePath The path of the file
* @return true if the file was sent successfully, false otherwise
*/
public boolean sendFile(Path filePath) {
try {
super.getResponse().setContentLengthLong(Files.size(filePath));
Files.copy(filePath, super.getResponse().getOutputStream());
super.getResponse().flushBuffer();
return true;
} catch (Exception ex) {
logger.error("Unable to send file: " + ex.getMessage());
}
return false;
}
@Override
public void close() throws Exception {
super.getResponse().getOutputStream().close();
}
}
@@ -0,0 +1,149 @@
package net.locusworks.portal.util;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.jgit.api.TransportConfigCallback;
import org.eclipse.jgit.transport.CredentialsProvider;
import org.eclipse.jgit.transport.JschConfigSessionFactory;
import org.eclipse.jgit.transport.SshTransport;
import org.eclipse.jgit.transport.Transport;
import org.eclipse.jgit.transport.OpenSshConfig.Host;
import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
import org.eclipse.jgit.util.FS;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import net.locusworks.portal.common.enums.CredentialType;
import net.locusworks.portal.json.CredentialRequest;
/**
* Creates a Git Configuration Session to manage
* credentials for git repositories
* @author Isaac Parenteau
* @since 1.0.0-RELEASE
* @version 1.0
*/
public class GitConfigSession extends JschConfigSessionFactory implements TransportConfigCallback {
private boolean useSSH;
private CredentialRequest creds;
private File privKeyFile;
private String passphrase;
private String privKeyFileName;
private String keyDirectory;
private CredentialsProvider credProvider;
/**
* Constructor to initialize the session
* @param creds Credentials to use for the session
*/
public GitConfigSession(CredentialRequest creds) {
this(creds, null);
}
/**
* Initialize the git config session
* @param creds credentials to use in this git session
* @param keyDirectory Directory to store the private key if using ssh
*/
public GitConfigSession(CredentialRequest creds, String keyDirectory) {
this.useSSH = CredentialType.in(creds.getCredentialType(), CredentialType.SSH);
this.creds = creds;
this.keyDirectory = keyDirectory;
if (useSSH) {
if (keyDirectory == null) {
throw new IllegalArgumentException("Key directory cannot be null when using ssh");
}
setupPrivKeyFile();
} else {
this.passphrase = this.creds.getPassword();
this.credProvider = new UsernamePasswordCredentialsProvider(this.creds.getUsername(), this.passphrase);
}
}
/**
* Set up the private key to use in the git session <br>
* Saves the private key locally for the session then deletes it once the session has ended
*/
private void setupPrivKeyFile() {
this.privKeyFileName = String.format("%s-%s", StringUtils.deleteWhitespace(this.creds.getName()), this.creds.getUid());
this.passphrase = creds.getPassphrase();
this.privKeyFile = new File(String.format("%s/%s", this.keyDirectory, this.privKeyFileName));
String privKey = creds.getKey();
try (FileWriter writer = new FileWriter(privKeyFile, false)) {
writer.write(privKey);
writer.flush();
} catch (IOException ex) {
throw new IllegalArgumentException(ex.getMessage(), ex);
}
}
@Override
protected JSch createDefaultJSch(FS fs) throws JSchException {
JSch defaultJSch = super.createDefaultJSch(fs);
String fileName = String.format("%s/%s", this.keyDirectory, this.privKeyFileName);
if (useSSH) {
if (StringUtils.isNotBlank(this.passphrase)) {
defaultJSch.addIdentity(fileName, passphrase);
} else {
defaultJSch.addIdentity(fileName);
}
}
return defaultJSch;
}
@Override
protected void configure(Host host, Session session) {
if (!useSSH) {
session.setPassword(this.passphrase);
}
}
/**
* Get the current private key
* @return the private key file
*/
public File getPrivKeyFile() {
return this.privKeyFile;
}
/**
* Delete the private key
*/
public void removePrivKeyFile() {
if (privKeyFile != null) {
FileUtils.deleteQuietly(privKeyFile);
}
}
/**
* Check to see if the session is using ssh
* @return useSsh
*/
public boolean useSSH() {
return useSSH;
}
/**
* Return the credential provider
* @return credentialProvider
*/
public CredentialsProvider getCredentials() {
return credProvider;
}
@Override
public void configure(Transport transport) {
if (transport instanceof SshTransport) {
SshTransport sshTransport = (SshTransport)transport;
sshTransport.setSshSessionFactory(this);
}
}
}
@@ -0,0 +1,142 @@
package net.locusworks.portal.util;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.eclipse.jgit.api.CloneCommand;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.PullCommand;
import org.eclipse.jgit.api.PullResult;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
import net.locusworks.common.utils.Utils;
import net.locusworks.portal.json.CredentialRequest;
/**
* Helper class to help with git repositories
* @author Isaac Parenteau
* @since 1.0.0-RELEASE
* @version 1.0
*/
public class GitRepositoryHelper implements AutoCloseable {
private Git git;
private GitConfigSession session;
/**
* Constructor to initialize the helper
* @param repoDir directory where the repository is located
* @param creds credentials to use for the git session
* @param keyDir directory to store the temporary private key if using ssh
*/
public GitRepositoryHelper(String repoDir, CredentialRequest creds, String keyDir) {
this(new File(repoDir), creds, keyDir);
}
/**
* Constructor to initialize the helper
* @param repoDir directory where the repository is located
* @param creds credentials to use for the git session
* @param keyDir directory to store the temporary private key if using ssh
*/
public GitRepositoryHelper(File repoDir, CredentialRequest creds, String keyDir) {
this(creds, keyDir);
try {
Repository repo = new FileRepositoryBuilder().setWorkTree(repoDir).readEnvironment().build();
this.git = new Git(repo);
} catch (IOException ex) {
throw new IllegalArgumentException(ex.getMessage(), ex);
}
}
/**
* Constructor to initialize the helper
* @param creds credentials to use for the git session
* @param keyDir directory to store the temporary private key if using ssh
*/
public GitRepositoryHelper(CredentialRequest creds, String keyDir) {
this.session = new GitConfigSession(creds, keyDir);
}
/**
* Perform a pull request on the repository
* @throws Exception thrown when an error occurs during the pull request
*/
public String pull() throws Exception {
PullCommand command = session.useSSH() ? this.git.pull().setTransportConfigCallback(this.session)
: this.git.pull().setCredentialsProvider(this.session.getCredentials());
PullResult results = command.call();
return results.getFetchResult().getMessages();
}
/**
* Switch the repository to a new branch
* @param branch the branch to switch to
* @throws Exception thrown when an error occurs when switching branches
*/
public void switchBranch(String branch) throws Exception {
this.git.checkout()
.setName(branch)
.setStartPoint(String.format("origin/%s", branch))
.setCreateBranch(!branchExistsLocally(branch))
.call();
pull();
}
/**
* Create a git repository
* @param scm the scm connection url to use
* @param directory the directory to check out the repository in
* @param branch the branch to use
* @throws Exception thrown when an error occurs during checkout
*/
public void createRepository(String scm, String directory, String branch) throws Exception {
CloneCommand command = Git.cloneRepository()
.setURI(scm)
.setDirectory(new File(directory))
.setBranchesToClone(Utils.toList(branch));
if (this.session.useSSH()) {
command.setTransportConfigCallback(this.session);
} else {
command.setCredentialsProvider(this.session.getCredentials());
}
this.git = command.call();
}
@Override
public void close() {
if (this.git == null) {
return;
}
if (this.git.getRepository() != null) {
this.git.getRepository().close();
}
this.git.close();
if(this.session != null) {
this.session.removePrivKeyFile();
}
}
/**
* Checks to see if the branch exits on the local file system
* @param git current git session
* @param remoteBranch branch to check
* @return true if the branch
* @throws Exception thrown when an error occurs when checking to see if the branch exists locally
*/
private boolean branchExistsLocally(String remoteBranch) throws Exception {
List<Ref> branches = git.branchList().call();
Set<String> branchNames = branches.stream().map(branch -> branch.getName()).collect(Collectors.toSet());
return branchNames.contains(String.format("refs/heads/%s", remoteBranch));
}
}
@@ -0,0 +1,469 @@
package net.locusworks.portal.util;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.InvalidPathException;
import java.util.ArrayList;
import java.util.Date;
import java.util.Properties;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.RegexFileFilter;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;
import net.locusworks.common.logger.ApplicationLogger;
import net.locusworks.common.logger.ApplicationLoggerFactory;
import net.locusworks.common.utils.Utils;
import net.locusworks.portal.common.enums.CredentialType;
import net.locusworks.portal.common.services.AESService;
import net.locusworks.portal.json.CredentialRequest;
import net.locusworks.portal.json.ScriptScheduleResultsRequest;
import static net.locusworks.common.utils.Constants.EXIT_FAIL;
import static net.locusworks.common.utils.Constants.EXIT_SUCCESS;
import static net.locusworks.common.Charsets.UTF_8;
/**
* Helper class for JSch execution
* @author Isaac Parenteau
* @since 1.0.0-RELEASE
* @version 1.0
*/
public class JSchHelper implements AutoCloseable {
private static final String GREP_COMMAND = "cd %s; ls | egrep '%s';";
private static final String SCP_TO_COMMAND = "scp -t %s;";
private static final String SCP_FROM_COMMAND = "scp -f %s;";
private File privKeyFile;
private CredentialType credType;
private Session session;
/**
* Enumeration defining the direction of the scp command
* @author Isaac Parenteau
* @since 1.0.0-RELEASE
* @version 1.0
*/
public enum SCP_DIRECTION {
TO,
FROM
}
/**
* Initialize the helper
* @param creds credentials to use for the session
* @param host ip address dns name to connect to
* @param port the port to connect on
* @param keyDir directory to store the temporary private key
* @param aesService AES service to encrypt and decrypt values
* @throws Exception thrown when initialization fails
*/
public JSchHelper(CredentialRequest creds, String host, Integer port, String keyDir, AESService aesService) throws Exception {
init(creds, host, port, keyDir, aesService);
}
/**
* Initialize the helper
* @param creds credentials to use for the session
* @param host ip address dns name to connect to
* @param port the port to connect on
* @param keyDir directory to store the temporary private key
* @throws Exception thrown when intialization fails
*/
private void init(CredentialRequest creds, String host, Integer port, String keyDir, AESService aesService) throws Exception {
String userName = creds.getUsername();
String password = "";
credType = CredentialType.getEnum(creds.getCredentialType());
JSch jsch = new JSch();
session = jsch.getSession(userName, host, port);
switch(credType) {
case PASSWD:
password = aesService.decrypt(creds.getPassword());
session.setUserInfo(new SshUserInfo(userName, password, true));
break;
case SSH:
String passphrase = aesService.decrypt(creds.getPassphrase());
String key = aesService.decrypt(creds.getKey());
Properties jschProps = new Properties();
jschProps.put("StrictHostKeyChecking", "no");
session.setConfig(jschProps);
String privKeyFileName = keyDir + File.separator + creds.getName().replace(" ", "") + "_rsa";
privKeyFile = new File(privKeyFileName);
FileUtils.writeByteArrayToFile(privKeyFile, key.getBytes(UTF_8), false);
if (Utils.isEmptyString(passphrase)) {
jsch.addIdentity(privKeyFileName);
} else {
jsch.addIdentity(privKeyFileName, passphrase.getBytes());
}
break;
}
}
/**
* Executes the scp command to send a file to a remote directory
* @param remoteDirectory remote directory to send the file to
* @param fileName name of the file to send
* @param localDirectory location of the file on the local system
* @return results of the scp command
* @throws Exception thrown when an error occurs during the scp command
*/
public JSchResults executeScpTo(String remoteDirectory, String fileName, String localDirectory) throws Exception {
File localDir = new File(localDirectory);
if (!localDir.isDirectory()) {
throw new InvalidPathException(localDirectory, "is not a directory or does not exist");
}
JSchResults results = new JSchResults();
results.setSentFiles(new ArrayList<>());
results.setStartTime(new Date());
FileFilter fileFilter = new RegexFileFilter(fileName);
File[] files = localDir.listFiles(fileFilter);
if (files == null || files.length < 1) {
results.setEndTime(new Date());
results.setExitStatus(EXIT_FAIL);
results.setResults(String.format("No files match %s in local directory %s", fileName, localDirectory));
return results;
}
for (File file : files) {
String localFile = file.getPath();
String remoteFile = String.format("%s/%s", remoteDirectory, file.getName());
if (!session.isConnected()) {
session.connect();
}
scpToHelper(localFile, remoteFile, results);
if (results.getExitStatus() == EXIT_FAIL) {
return results;
}
results.getSentFiles().add(localFile);
}
results.setEndTime(new Date());
results.setExitStatus(EXIT_SUCCESS);
return results;
}
/**
* Execute the scp command to retrieve a remote file
* @param remoteDirectory the remote directory to look in
* @param remoteFileRegEx the file name or regular expression for the file to find
* @param localOutputDir the local file system output directory to store the files in
* @return results of the scp command
* @throws Exception thrown when the scp command errors
*/
public JSchResults executeScpFrom(String remoteDirectory, String remoteFileRegEx, String localOutputDir) throws Exception {
//Grep for matching file names in the remote directory;
String fileFinderString = String.format(GREP_COMMAND, remoteDirectory, remoteFileRegEx);
JSchResults results = executeRemote(fileFinderString);
if (results.getExitStatus() == EXIT_FAIL) {
return results;
}
String[] files = results.getResults().split("\n");
results = new JSchResults();
results.setRetrievedFiles(new ArrayList<>());
results.setStartTime(new Date());
results.setResults("");
for(String file : files) {
String remoteFile = String.format("%s/%s", remoteDirectory, file);
String localFile = String.format("%s/%s", localOutputDir, file);
if (!session.isConnected()) {
session.connect();
}
scpFromHelper(remoteFile, localFile, results);
if (results.getExitStatus() == EXIT_FAIL) {
return results;
}
results.getRetrievedFiles().add(localFile);
}
results.setEndTime(new Date());
results.setExitStatus(EXIT_SUCCESS);
return results;
}
/**
* Execute a remote command
* @param params parameters to execute on the remote system
* @return results of the remote execution
* @throws Exception thrown when an error occurs during remote execution
*/
public JSchResults executeRemote(String params) throws Exception {
JSchResults results = new JSchResults();
results.setStartTime(new Date());
session.connect();
ChannelExec exec = (ChannelExec)session.openChannel("exec");
InputStream is = exec.getInputStream();
exec.setCommand(params);
exec.connect();
byte[] tmp = new byte[1024];
Integer exitStatus = -1;
StringBuilder sb = new StringBuilder();
while (true) {
while(is.available() > 0) {
int i = is.read(tmp, 0, 1024);
if (i < 0) break;
sb.append(new String(tmp, 0, i, UTF_8));
}
if (exec.isClosed()) {
if (is.available() > 0) continue;
exitStatus = exec.getExitStatus();
break;
}
Thread.sleep(1000);
}
exec.disconnect();
is.close();
results.setExitStatus(exitStatus.shortValue());
results.setResults(sb.toString());
results.setEndTime(new Date());
return results;
}
@Override
public void close() throws Exception {
if(session != null && session.isConnected()) {
session.disconnect();
}
if (privKeyFile != null) {
FileUtils.deleteQuietly(privKeyFile);
}
}
/**
* Helper method for the scp to command
* @param lfile the locate file name to send
* @param rfile the remote file name
* @param results the current results
* @throws Exception thrown when an error occurs
*/
private void scpToHelper(String lfile, String rfile, JSchResults results) throws Exception {
StringBuilder command = new StringBuilder(String.format(SCP_TO_COMMAND, rfile));
ChannelExec exec = (ChannelExec)session.openChannel("exec");
exec.setCommand(command.toString());
FileInputStream fis = null;
File localFile = new File(lfile);
OutputStream out = exec.getOutputStream();
InputStream in = exec.getInputStream();
exec.connect();
if (checkAck(in) != 0) {
results.setEndTime(new Date());
results.setExitStatus(EXIT_FAIL);
results.setResults("Unable to send remote file due to invalid read of remote file");
return;
}
long fileSize = localFile.length();
command = new StringBuilder("C0644 ").append(fileSize).append(" ");
if (lfile.lastIndexOf('/') >0 ) {
command.append(lfile.substring(lfile.lastIndexOf('/') + 1));
} else {
command.append(lfile);
}
command.append("\n");
out.write(command.toString().getBytes(UTF_8));
out.flush();
if (checkAck(in) != 0) {
results.setEndTime(new Date());
results.setExitStatus(EXIT_FAIL);
results.setResults("Unable to send remote file due to invalid read of remote file");
return;
}
fis = new FileInputStream(localFile);
byte[] buf = new byte[1024];
while (true) {
int len = fis.read(buf, 0, buf.length);
if (len <= 0) break;
out.write(buf, 0, len);
}
fis.close();
fis = null;
sendNull(out, buf);
if (checkAck(in) != 0) {
results.setEndTime(new Date());
results.setExitStatus(EXIT_FAIL);
results.setResults("Unable to send remote file due to invalid read of remote file");
return;
}
out.close();
exec.disconnect();
results.setExitStatus(EXIT_SUCCESS);
results.setResults(String.format("%sLocal file %s sent. Saved to %s%n", results.getResults(), lfile, rfile));
}
/**
* Helper method for scp from remote to local system
* @param remoteFileName remote file name to get
* @param lfile local file name to save as
* @param results current results
* @throws Exception thrown when an error occurs
*/
private void scpFromHelper(String remoteFileName, String lfile, ScriptScheduleResultsRequest results) throws Exception {
String command = String.format(SCP_FROM_COMMAND, remoteFileName);
ChannelExec channel = (ChannelExec)session.openChannel("exec");
channel.setCommand(command);
OutputStream out = channel.getOutputStream();
InputStream in = channel.getInputStream();
FileOutputStream fos = null;
String prefix=null;
if(new File(lfile).isDirectory()){
prefix = lfile + File.separator;
}
channel.connect();
//Send '\0'
byte[] buf = new byte[1024];
sendNull(out, buf);
while (checkAck(in) == 'C') {
// read '0644 '
int bytesRead = in.read(buf, 0, 5);
if (bytesRead < 0) {
throw new Exception("no bytes were read");
}
long fileSize = 0L;
while (in.read(buf, 0, 1) >= 0 && buf[0] != ' ') {
if (buf[0] == ' ') break;
fileSize = fileSize * 10L + (long)(buf[0] - '0');
}
String file = null;
for (int i = 0;; i++) {
in.read(buf, i, 1);
if (buf[i] == (byte)0x0a) {
file = new String(buf, 0, i, UTF_8);
break;
}
}
sendNull(out, buf);
// read a content of lfile
fos = new FileOutputStream(prefix==null ? lfile : prefix+file);
int fosReadCount;
while(true) {
if(buf.length < fileSize) {
fosReadCount = buf.length;
} else {
fosReadCount = (int)fileSize;
}
fosReadCount= in.read(buf, 0, fosReadCount);
if(fosReadCount < 0){
// error
break;
}
fos.write(buf, 0, fosReadCount);
fileSize -= fosReadCount;
if(fileSize == 0L) {
break;
}
}
fos.close();
fos = null;
if(checkAck(in) != 0){
results.setEndTime(new Date());
results.setExitStatus(EXIT_FAIL);
results.setResults("Unable to retrieve remote file due to invalid read of remote file");
out.close();
in.close();
return;
}
sendNull(out, buf);
}
out.close();
in.close();
channel.disconnect();
results.setExitStatus(EXIT_SUCCESS);
results.setResults(String.format("%sRemote file %s retrieved. Saved to %s%n", results.getResults(), remoteFileName, lfile));
}
/**
* Sends '\0' to output stream
* @param out output stream to send to
* @param buf current buffer
* @throws IOException
*/
private void sendNull(OutputStream out, byte[] buf) throws IOException {
buf[0] = 0;
out.write(buf, 0, 1);
out.flush();
}
/**
* Check the ack flag
* @param in the current input stream
* @return 0 - success <br>
* 1 - error <br>
* 2 - fatal error
* @throws Exception
*/
private static int checkAck(InputStream in) throws Exception {
int b = in.read();
// b may be 0 for success,
// 1 for error,
// 2 for fatal error,
// -1
if(b <= 0)
return b;
if(b==1 || b==2) {
//Only want to get the logger if there is an error when checking the ack
ApplicationLogger logger = ApplicationLoggerFactory.getLogger(JSchHelper.class);
StringBuffer sb=new StringBuffer();
int c;
do {
c = in.read();
sb.append((char)c);
} while(c != '\n');
if(b == 1 || b == 2) { // error
logger.error(sb.toString());
}
}
return b;
}
}
@@ -0,0 +1,94 @@
package net.locusworks.portal.util;
import java.util.List;
import net.locusworks.portal.json.ScriptScheduleResultsRequest;
/**
* JSchResults class handles the results
* from the jsch commands
* @author Isaac Parenteau
* @since 1.0.0-RELEASE
* @version 1.0
*/
public class JSchResults extends ScriptScheduleResultsRequest {
private boolean isLocalTo;
private boolean isLocalFrom;
private List<String> sentFiles;
private List<String> retrievedFiles;
/**
* Constructor
*/
public JSchResults() {
super();
}
/**
* Constructor
* @param exitStatus job execution status
* @param results results of the execution
*/
public JSchResults(Short exitStatus, String results) {
super(exitStatus, results);
}
/**
* @return the isLocalTo
*/
public boolean isLocalTo() {
return isLocalTo;
}
/**
* @param isLocalTo the isLocalTo to set
*/
public void setLocalTo(boolean isLocalTo) {
this.isLocalTo = isLocalTo;
}
/**
* @return the isLocalFrom
*/
public boolean isLocalFrom() {
return isLocalFrom;
}
/**
* @param isLocalFrom the isLocalFrom to set
*/
public void setLocalFrom(boolean isLocalFrom) {
this.isLocalFrom = isLocalFrom;
}
/**
* @return the sentFiles
*/
public List<String> getSentFiles() {
return sentFiles;
}
/**
* @param sentFiles the sentFiles to set
*/
public void setSentFiles(List<String> sentFiles) {
this.sentFiles = sentFiles;
}
/**
* @return the retrievedFiles
*/
public List<String> getRetrievedFiles() {
return retrievedFiles;
}
/**
* @param retrievedFiles the retrievedFiles to set
*/
public void setRetrievedFiles(List<String> retrievedFiles) {
this.retrievedFiles = retrievedFiles;
}
}
@@ -0,0 +1,188 @@
package net.locusworks.portal.util;
import com.jcraft.jsch.UserInfo;
/**
* User info to use for JSch Authentication
* @author Isaac Parenteau
* @since 1.0.0-RELEASE
* @version 1.0
*/
public class SshUserInfo implements UserInfo {
private String name;
private String password = null;
private String keyfile;
private String passphrase = null;
private boolean trustAllCertificates;
/**
* Constructor for SSHUserInfo.
*/
public SshUserInfo() {
super();
this.trustAllCertificates = false;
}
/**
* Constructor for SSHUserInfo.
* @param name the user's name
*/
public SshUserInfo(String name) {
super();
this.name = name;
this.trustAllCertificates = false;
}
/**
* Constructor for SSHUserInfo.
* @param name the user's name
* @param password the user's password
* @param trustAllCertificates if true trust hosts whose identity is unknown
*/
public SshUserInfo(String name, String password, boolean trustAllCertificates) {
super();
this.name = name;
this.password = password;
this.trustAllCertificates = trustAllCertificates;
}
/**
* Gets the user name.
* @return the user name
*/
public String getName() {
return name;
}
/**
* Gets the pass phrase of the user.
* @param message a message
* @return the passphrase
*/
public String getPassphrase(String message) {
return passphrase;
}
/**
* Gets the user's password.
* @return the user's password
*/
public String getPassword() {
return password;
}
/**
* Prompts a string.
* @param str the string
* @return whether the string was prompted
*/
public boolean prompt(String str) {
return false;
}
/**
* Indicates whether a retry was done.
* @return whether a retry was done
*/
public boolean retry() {
return false;
}
/**
* Sets the name.
* @param name The name to set
*/
public void setName(String name) {
this.name = name;
}
/**
* Sets the passphrase.
* @param passphrase The passphrase to set
*/
public void setPassphrase(String passphrase) {
this.passphrase = passphrase;
}
/**
* Sets the password.
* @param password The password to set
*/
public void setPassword(String password) {
this.password = password;
}
/**
* Sets the trust.
* @param trust whether to trust or not.
*/
public void setTrust(boolean trust) {
this.trustAllCertificates = trust;
}
/**
* @return whether to trust or not.
*/
public boolean getTrust() {
return this.trustAllCertificates;
}
/**
* Returns the passphrase.
* @return String
*/
public String getPassphrase() {
return passphrase;
}
/**
* Returns the keyfile.
* @return String
*/
public String getKeyfile() {
return keyfile;
}
/**
* Sets the keyfile.
* @param keyfile The keyfile to set
*/
public void setKeyfile(String keyfile) {
this.keyfile = keyfile;
}
/**
* Implement the UserInfo interface.
* @param message ignored
* @return true always
*/
public boolean promptPassphrase(String message) {
return true;
}
/**
* Implement the UserInfo interface.
* @param passwordPrompt ignored
* @return true the first time this is called, false otherwise
*/
public boolean promptPassword(String passwordPrompt) {
return true;
}
/**
* Implement the UserInfo interface.
* @param message ignored
* @return the value of trustAllCertificates
*/
public boolean promptYesNo(String message) {
return trustAllCertificates;
}
/**
* Implement the UserInfo interface (noop).
* @param message ignored
*/
public void showMessage(String message) {
}
}
@@ -0,0 +1,65 @@
package net.locusworks.portal.util;
import java.util.concurrent.TimeUnit;
import net.locusworks.portal.handlers.TaskSchedulerHandler;
/**
* Helper class to define for execution
* @author Isaac Parenteau
* @since 1.0.0-RELEASE
* @version 1.0
*/
public class TaskScheduler implements Runnable {
private TaskSchedulerHandler handler;
private Integer delay;
private TimeUnit unit;
public TaskScheduler() { }
public TaskScheduler(TaskSchedulerHandler handler) {
this(handler, 1, TimeUnit.MINUTES);
}
public TaskScheduler(TaskSchedulerHandler handler, Integer delay, TimeUnit unit) {
super();
this.handler = handler;
this.delay = delay;
this.unit = unit;
}
/**
* @return the delay
*/
public Integer getDelay() {
return delay;
}
/**
* @param delay the delay to set
*/
public void setDelay(Integer delay) {
this.delay = delay;
}
/**
* @return the unit
*/
public TimeUnit getUnit() {
return unit;
}
/**
* @param unit the unit to set
*/
public void setUnit(TimeUnit unit) {
this.unit = unit;
}
@Override
public void run() {
if (handler != null) {
handler.runTask();
}
}
}
@@ -0,0 +1,55 @@
package net.locusworks.portal.websocket;
import java.util.Date;
/**
* Class to help with sending ping pong messages to the server and client
* to establish if websockets can be used
* @author Isaac Parenteau
* @since 1.0.0-RELEASE
* @version 1.0
*/
public class PingPong {
private Date date;
private int pull;
private byte[] data;
/**
* @return the date
*/
public Date getDate() {
return date;
}
/**
* @param date the date to set
*/
public void setDate(Date date) {
this.date = date;
}
/**
* @return the pull
*/
public int getPull() {
return pull;
}
/**
* @param pull the pull to set
*/
public void setPull(int pull) {
this.pull = pull;
}
/**
* @return the data
*/
public byte[] getData() {
return data;
}
/**
* @param data the data to set
*/
public void setData(byte[] data) {
this.data = data;
}
}
@@ -0,0 +1,41 @@
package net.locusworks.portal.websocket;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.support.ChannelInterceptorAdapter;
/**
* class to handle websocket outbound connections
* @author Isaac Parenteau
* @since 1.0.0-RELEASE
* @version 1.0
*
*/
public class WebsocketOutboundChannelInterceptor extends ChannelInterceptorAdapter {
@Override
public Message<?> preSend(Message<?> message, MessageChannel channel) {
return message;
}
@Override
public void postSend(Message<?> message, MessageChannel channel, boolean sent) {
}
@Override
public void afterSendCompletion(Message<?> message, MessageChannel channel, boolean sent, Exception ex) {
}
public boolean preReceive(MessageChannel channel) {
return true;
}
@Override
public Message<?> postReceive(Message<?> message, MessageChannel channel) {
return message;
}
@Override
public void afterReceiveCompletion(Message<?> message, MessageChannel channel, Exception ex) {
}
}
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xml>
<Configuration status="WARN" shutdownHook="disable">
<Properties>
<Property name="logFormat">%d{dd-MMM-yyyy HH:mm:ss.SSS} %m%n</Property>
</Properties>
<Appenders>
<Console name="ConsoleAppender" target="SYSTEM_OUT">
<PatternLayout pattern="${logFormat}" />
</Console>
<RollingFile name="portalLogger" fileName="${sys:catalina.base}/logs/portal.log"
filePattern="${sys:catalina.base}/logs/portal-%d{yyyy-MM-dd}.log.gz">
<PatternLayout pattern="${logFormat}" />
<Policies>
<TimeBasedTriggeringPolicy />
<SizeBasedTriggeringPolicy size="10 MB" />
</Policies>
</RollingFile>
</Appenders>
<Loggers>
<Root level="TRACE">
<AppenderRef ref="ConsoleAppender" level="INFO"/>
<AppenderRef ref="portalLogger" level="INFO"/>
</Root>
</Loggers>
</Configuration>
@@ -0,0 +1,11 @@
userExpirationDays=3650
logLevel=INFO
dbHost=localhost
dbPort=3306
dbUsername=portalAdmin
dbPassword=VWUPJBKSje2S8J/YMdx15QhhRMRsy39URVR6c6ksrtE\=
dbRootUser=root
dbRootPassword=VvzxDy5E1o1Q8rL7XMO2mQ\=\=
caCertFile=${user.home}/cacerts.jks
caCertPassword=N/OQnJ9uQh1oYxadwGC3vA==
aesSeedFile=${catalina.base}/conf/portal.tomcat
@@ -0,0 +1 @@
thisIsASecretKey
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xml>
<Context antiJARLocking="true" antiResourceLocking="true" docBase="portal" path="/portal_webapp">
<!-- Default set of monitored resources -->
<WatchedResource>WEB-INF/web.xml</WatchedResource>
<!-- Uncomment this to disable session persistence across Tomcat restarts -->
<Manager pathname=""/>
<!-- Uncomment this to enable Comet connection tacking (provides events
on session expiration as well as webapp lifecycle) -->
<!--
<Valve className="org.apache.catalina.valves.CometConnectionManagerValve" />
-->
</Context>
@@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:websocket="http://www.springframework.org/schema/websocket"
xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:task="http://www.springframework.org/schema/task"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-3.2.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task-3.2.xsd
http://www.springframework.org/schema/websocket
http://www.springframework.org/schema/websocket/spring-websocket-4.0.xsd">
<!-- declares explicit support for annotation-driven MVC controllers (i.e.
@RequestMapping, @Controller, although support for those is the default behaviour),
as well as adding support for declarative validation via @Valid and message
body marshalling with @RequestBody/ResponseBody. -->
<mvc:annotation-driven />
<mvc:resources mapping="/assets/**" location="/assets/" />
<!-- If the client sends a defaultLocale cookie with a request, any error
messages generated by the request will be returned in the specified language -->
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver">
<property name="cookieName" value="locale" />
<property name="defaultLocale" value="en" />
</bean>
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix">
<value>/view/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="16777215" />
</bean>
<!-- <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" /> -->
<!-- Scans the classpath for annotated components that will be auto-registered
as Spring beans -->
<context:component-scan base-package="net.locusworks.portal" />
<!-- perform flyway migration -->
<bean id="migration" class="net.locusworks.portal.migration.util.MigrationManager" init-method="migrate"/>
</beans>
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:websocket="http://www.springframework.org/schema/websocket"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:task="http://www.springframework.org/schema/task"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-3.2.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task-3.2.xsd
http://www.springframework.org/schema/websocket
http://www.springframework.org/schema/websocket/spring-websocket-4.0.xsd">
</beans>
@@ -0,0 +1,59 @@
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<display-name>NG Portal</display-name>
<servlet>
<servlet-name>portal</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
<async-supported>true</async-supported>
</servlet>
<listener>
<listener-class>net.locusworks.portal.services.PortalContextService</listener-class>
</listener>
<!-- HttpSessionEventPublisher converts session events to a Spring event -->
<!-- and publishes the event to the ApplicationContext. -->
<listener>
<listener-class>
org.springframework.security.web.session.HttpSessionEventPublisher
</listener-class>
</listener>
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<async-supported>true</async-supported>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
<servlet-mapping>
<servlet-name>portal</servlet-name>
<url-pattern>/ws/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>portal</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>portal</servlet-name>
<url-pattern>*.view</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>portal</servlet-name>
<url-pattern>*.resf</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>portal</servlet-name>
<url-pattern>*.pdf</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>540</session-timeout>
</session-config>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/portal-config.xml</param-value>
</context-param>
</web-app>
+1
View File
@@ -0,0 +1 @@
<script>location.href="client/index.html";</script>