diff --git a/.project b/.project index b51ee30c..a867e904 100644 --- a/.project +++ b/.project @@ -20,4 +20,17 @@ org.eclipse.jdt.core.javanature org.eclipse.m2e.core.maven2Nature + + + + 1613690915857 + + + 30 + + org.eclipse.core.resources.regexFilterMatcher + node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ + + + diff --git a/src/main/java/edu/uark/registerapp/commands/activeUsers/ActiveUserDeleteCommand.java b/src/main/java/edu/uark/registerapp/commands/activeUsers/ActiveUserDeleteCommand.java new file mode 100644 index 00000000..367548e2 --- /dev/null +++ b/src/main/java/edu/uark/registerapp/commands/activeUsers/ActiveUserDeleteCommand.java @@ -0,0 +1,53 @@ +package edu.uark.registerapp.commands.activeUsers; + +import java.util.Optional; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import edu.uark.registerapp.commands.VoidCommandInterface; +import edu.uark.registerapp.commands.exceptions.UnprocessableEntityException; +import edu.uark.registerapp.models.entities.ActiveUserEntity; +import edu.uark.registerapp.models.repositories.ActiveUserRepository; + +@Service +public class ActiveUserDeleteCommand implements VoidCommandInterface { + + @Transactional + @Override + public void execute() { + final Optional activeUserEntity = + this.activeUserRepository.findBySessionKey(this.sessionKey); + + validateEmployeeRequestObject(activeUserEntity); + + //removes active user + if (activeUserEntity.isPresent()) { + this.activeUserRepository.delete(activeUserEntity.get()); + } + } + + private void validateEmployeeRequestObject(Optional activeUserEntity){ + if (StringUtils.isBlank(activeUserEntity.get().getName())) { + throw new UnprocessableEntityException("Name"); + } + String [] name = activeUserEntity.get().getName().split(" ", 2); + if (StringUtils.isBlank(name[0])) {throw new UnprocessableEntityException("First Name");} + if (StringUtils.isBlank(name[1])) {throw new UnprocessableEntityException("Last Name");} + } + + // Properties + private String sessionKey; + public String getSessionKey() { + return this.sessionKey; + } + public ActiveUserDeleteCommand setSessionKey(final String sessionKey) { + this.sessionKey = sessionKey; + return this; + } + + @Autowired + private ActiveUserRepository activeUserRepository; +} diff --git a/src/main/java/edu/uark/registerapp/commands/activeUsers/ValidateActiveUserCommand.java b/src/main/java/edu/uark/registerapp/commands/activeUsers/ValidateActiveUserCommand.java new file mode 100644 index 00000000..d04784cb --- /dev/null +++ b/src/main/java/edu/uark/registerapp/commands/activeUsers/ValidateActiveUserCommand.java @@ -0,0 +1,41 @@ +package edu.uark.registerapp.commands.activeUsers; + +import java.util.Optional; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import edu.uark.registerapp.commands.ResultCommandInterface; +import edu.uark.registerapp.commands.exceptions.UnauthorizedException; +import edu.uark.registerapp.models.entities.ActiveUserEntity; +import edu.uark.registerapp.models.repositories.ActiveUserRepository; + +@Service +public class ValidateActiveUserCommand implements ResultCommandInterface { + @Override + public ActiveUserEntity execute() { + final Optional activeUserEntity = + this.activeUserRepository.findBySessionKey(this.sessionKey); + + if (!activeUserEntity.isPresent()) { + throw new UnauthorizedException(); + } + + return activeUserEntity.get(); + } + + // Properties + private String sessionKey; + + public String getSessionKey() { + return this.sessionKey; + } + + public ValidateActiveUserCommand setSessionKey(final String sessionKey) { + this.sessionKey = sessionKey; + return this; + } + + @Autowired + private ActiveUserRepository activeUserRepository; +} diff --git a/src/main/java/edu/uark/registerapp/commands/employees/ActiveEmployeeExistsQuery.java b/src/main/java/edu/uark/registerapp/commands/employees/ActiveEmployeeExistsQuery.java new file mode 100644 index 00000000..d738f5e0 --- /dev/null +++ b/src/main/java/edu/uark/registerapp/commands/employees/ActiveEmployeeExistsQuery.java @@ -0,0 +1,21 @@ +package edu.uark.registerapp.commands.employees; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import edu.uark.registerapp.commands.VoidCommandInterface; +import edu.uark.registerapp.models.repositories.EmployeeRepository; +import edu.uark.registerapp.commands.exceptions.NotFoundException; + +@Service +public class ActiveEmployeeExistsQuery implements VoidCommandInterface{ + @Override + public void execute() { + if (!this.employeerepository.existsByIsActive(true)) { + throw new NotFoundException("Employee"); + } + } + + @Autowired + private EmployeeRepository employeerepository; +} diff --git a/src/main/java/edu/uark/registerapp/commands/employees/EmployeeSignInCommand.java b/src/main/java/edu/uark/registerapp/commands/employees/EmployeeSignInCommand.java new file mode 100644 index 00000000..d6e122a1 --- /dev/null +++ b/src/main/java/edu/uark/registerapp/commands/employees/EmployeeSignInCommand.java @@ -0,0 +1,112 @@ +package edu.uark.registerapp.commands.employees; + +import java.util.Arrays; +import java.util.Optional; + +import javax.transaction.Transactional; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import edu.uark.registerapp.commands.ResultCommandInterface; +import edu.uark.registerapp.commands.employees.helpers.EmployeeHelper; +import edu.uark.registerapp.commands.exceptions.UnauthorizedException; +import edu.uark.registerapp.commands.exceptions.UnprocessableEntityException; +import edu.uark.registerapp.models.api.Employee; +import edu.uark.registerapp.models.api.EmployeeSignIn; +import edu.uark.registerapp.models.entities.ActiveUserEntity; +import edu.uark.registerapp.models.entities.EmployeeEntity; +import edu.uark.registerapp.models.repositories.ActiveUserRepository; +import edu.uark.registerapp.models.repositories.EmployeeRepository; + +@Service +public class EmployeeSignInCommand implements ResultCommandInterface { + @Override + public Employee execute() { + this.validateProperties(); + return new Employee(this.SignInEmployee()); + } + + // Helper methods + private void validateProperties() { + //check if employeeID is blank + if (StringUtils.isBlank(this.employeeSignIn.getEmployeeId())) { + throw new UnprocessableEntityException("employee ID"); + } + //check if you can get only a number out of the ID + try { + Integer.parseInt(this.employeeSignIn.getEmployeeId()); + } catch (final NumberFormatException e) { + //throw an exception if the ID is not just numbers + throw new UnprocessableEntityException("employee ID"); + } + if (StringUtils.isBlank(this.employeeSignIn.getPassword())) { + //throw exception if the password is blank + throw new UnprocessableEntityException("password"); + } + } + + @Transactional + private EmployeeEntity SignInEmployee() { + //tries to find existing employee using the id + final Optional employeeEntity = + this.employeeRepository.findByEmployeeId( + Integer.parseInt(this.employeeSignIn.getEmployeeId())); + //verifies ifthe employee exists + if (!employeeEntity.isPresent() + || !Arrays.equals( + employeeEntity.get().getPassword(), + EmployeeHelper.hashPassword(this.employeeSignIn.getPassword())) + ) { + + throw new UnauthorizedException(); + } + + final Optional activeUserEntity = + this.activeUserRepository + .findByEmployeeId(employeeEntity.get().getId()); + + if (!activeUserEntity.isPresent()) { + this.activeUserRepository.save( + (new ActiveUserEntity()) + .setSessionKey(this.sessionId) + .setEmployeeId(employeeEntity.get().getId()) + .setClassification( + employeeEntity.get().getClassification()) + .setName( + employeeEntity.get().getFirstName() + .concat(" ") + .concat(employeeEntity.get().getLastName()))); + } else { + this.activeUserRepository.save( + activeUserEntity.get().setSessionKey(this.sessionId)); + } + + return employeeEntity.get(); + } + + // Properties + private EmployeeSignIn employeeSignIn; + public EmployeeSignIn getEmployeeSignIn() { + return this.employeeSignIn; + } + public EmployeeSignInCommand setEmployeeSignIn(final EmployeeSignIn employeeSignIn) { + this.employeeSignIn = employeeSignIn; + return this; + } + + private String sessionId; + public String getSessionId() { + return this.sessionId; + } + public EmployeeSignInCommand setSessionId(final String sessionId) { + this.sessionId = sessionId; + return this; + } + + @Autowired + private EmployeeRepository employeeRepository; + @Autowired + private ActiveUserRepository activeUserRepository; +} diff --git a/src/main/java/edu/uark/registerapp/commands/employees/helpers/EmployeeHelper.java b/src/main/java/edu/uark/registerapp/commands/employees/helpers/EmployeeHelper.java new file mode 100644 index 00000000..e6f54d73 --- /dev/null +++ b/src/main/java/edu/uark/registerapp/commands/employees/helpers/EmployeeHelper.java @@ -0,0 +1,34 @@ +package edu.uark.registerapp.commands.employees.helpers; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +import org.apache.commons.lang3.StringUtils; + +public class EmployeeHelper { + public static String padEmployeeId(final int employeeId) { + final String employeeIdAsString = Integer.toString(employeeId); + + return ((employeeIdAsString.length() < EMPLOYEE_ID_MAXIMUM_LENGTH) + ? StringUtils.leftPad( + employeeIdAsString, + EMPLOYEE_ID_MAXIMUM_LENGTH, + "0") + : employeeIdAsString); + } + + public static byte[] hashPassword(final String password) { + try { + final MessageDigest messageDigest = + MessageDigest.getInstance("SHA-256"); + + messageDigest.update(password.getBytes()); + + return messageDigest.digest(); + } catch (final NoSuchAlgorithmException e) { + return new byte[0]; + } + } + + private static final int EMPLOYEE_ID_MAXIMUM_LENGTH = 5; +} diff --git a/src/main/java/edu/uark/registerapp/controllers/BaseRestController.java b/src/main/java/edu/uark/registerapp/controllers/BaseRestController.java new file mode 100644 index 00000000..0f39f55f --- /dev/null +++ b/src/main/java/edu/uark/registerapp/controllers/BaseRestController.java @@ -0,0 +1,89 @@ +package edu.uark.registerapp.controllers; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseBody; + +import edu.uark.registerapp.commands.activeUsers.ValidateActiveUserCommand; +import edu.uark.registerapp.commands.exceptions.ConflictException; +import edu.uark.registerapp.commands.exceptions.NotFoundException; +import edu.uark.registerapp.commands.exceptions.UnauthorizedException; +import edu.uark.registerapp.commands.exceptions.UnprocessableEntityException; +import edu.uark.registerapp.controllers.enums.QueryParameterMessages; +import edu.uark.registerapp.controllers.enums.QueryParameterNames; +import edu.uark.registerapp.controllers.enums.ViewNames; +import edu.uark.registerapp.models.api.ApiResponse; +import edu.uark.registerapp.models.entities.ActiveUserEntity; +import edu.uark.registerapp.models.enums.EmployeeClassification; + +public class BaseRestController extends BaseController { + protected ApiResponse redirectSessionNotActive( + final HttpServletResponse response + ) { + + response.setStatus(HttpStatus.FOUND.value()); + return (new ApiResponse()) + .setRedirectUrl( + ViewNames.SIGN_IN.getRoute().concat( + this.buildInitialQueryParameter( + QueryParameterNames.ERROR_CODE.getValue(), + QueryParameterMessages.SESSION_NOT_ACTIVE.getKeyAsString()))); + } + + protected ApiResponse redirectUserNotElevated( + final HttpServletRequest request, + final HttpServletResponse response + ) { + + return this.redirectUserNotElevated(request, response, ViewNames.MAIN_MENU.getRoute()); + } + + protected ApiResponse redirectUserNotElevated( + final HttpServletRequest request, + final HttpServletResponse response, + final String redirectRoute + ) { + + try { + final ActiveUserEntity activeUserEntity = + this.validateActiveUserCommand + .setSessionKey(request.getSession().getId()) + .execute(); + + if (activeUserEntity == null) { + return this.redirectSessionNotActive(response); + } else if (!EmployeeClassification.isElevatedUser(activeUserEntity.getClassification())) { + response.setStatus(HttpStatus.FOUND.value()); + + return (new ApiResponse()) + .setRedirectUrl( + redirectRoute.concat( + this.buildInitialQueryParameter( + QueryParameterNames.ERROR_CODE.getValue(), + QueryParameterMessages.NO_PERMISSIONS_FOR_ACTION.getKeyAsString()))); + } + } catch (final UnauthorizedException e) { + return this.redirectSessionNotActive(response); + } + + return new ApiResponse(); + } + + @ExceptionHandler({ + ConflictException.class, + NotFoundException.class, + UnauthorizedException.class, + UnprocessableEntityException.class + }) + public @ResponseBody ApiResponse handleError(final Exception e) { + return (new ApiResponse()).setErrorMessage(e.getMessage()); + } + + // Properties + @Autowired + private ValidateActiveUserCommand validateActiveUserCommand; +} diff --git a/src/main/java/edu/uark/registerapp/controllers/BaseRouteController.java b/src/main/java/edu/uark/registerapp/controllers/BaseRouteController.java new file mode 100644 index 00000000..58774f84 --- /dev/null +++ b/src/main/java/edu/uark/registerapp/controllers/BaseRouteController.java @@ -0,0 +1,117 @@ +package edu.uark.registerapp.controllers; + +import java.util.Map; +import java.util.Optional; + +import javax.servlet.http.HttpServletRequest; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.servlet.ModelAndView; + +import edu.uark.registerapp.commands.activeUsers.ValidateActiveUserCommand; +import edu.uark.registerapp.commands.exceptions.UnauthorizedException; +import edu.uark.registerapp.controllers.enums.ViewModelNames; +import edu.uark.registerapp.controllers.enums.QueryParameterMessages; +import edu.uark.registerapp.controllers.enums.QueryParameterNames; +import edu.uark.registerapp.controllers.enums.ViewNames; +import edu.uark.registerapp.models.entities.ActiveUserEntity; +import edu.uark.registerapp.models.enums.EmployeeClassification; + +public abstract class BaseRouteController extends BaseController { + protected ModelAndView setErrorMessageFromQueryString( + ModelAndView modelAndView, + final Map queryParameters + ) { + + if (!queryParameters.containsKey(QueryParameterNames.ERROR_CODE.getValue())) { + return modelAndView; + } + + try { + modelAndView = + this.setErrorMessageFromQueryString( + modelAndView, + Integer.parseInt( + queryParameters.get( + QueryParameterNames.ERROR_CODE.getValue()))); + } catch (final NumberFormatException e) { } + + return modelAndView; + } + protected ModelAndView setErrorMessageFromQueryString( + final ModelAndView modelAndView, + final Optional errorCode + ) { + + if (!errorCode.isPresent()) { + return modelAndView; + } + + return this.setErrorMessageFromQueryString(modelAndView, errorCode.get()); + } + + protected Optional getCurrentUser( + final HttpServletRequest request + ) { + + try { + return Optional.of( + this.validateActiveUserCommand + .setSessionKey(request.getSession().getId()) + .execute()); + } catch (final UnauthorizedException e) { + return Optional.ofNullable(null); + } + } + + protected ModelAndView buildInvalidSessionResponse() { + return new ModelAndView( + REDIRECT_PREPEND.concat( + ViewNames.SIGN_IN.getRoute().concat( + this.buildInitialQueryParameter( + QueryParameterNames.ERROR_CODE.getValue(), + QueryParameterMessages.SESSION_NOT_ACTIVE.getKeyAsString())))); + } + + protected boolean isElevatedUser(final ActiveUserEntity activeUserEntity) { + return EmployeeClassification.isElevatedUser( + activeUserEntity.getClassification()); + } + + protected ModelAndView buildNoPermissionsResponse() { + return this.buildNoPermissionsResponse(ViewNames.MAIN_MENU.getRoute()); + } + + protected ModelAndView buildNoPermissionsResponse(final String redirectRoute) { + return new ModelAndView( + REDIRECT_PREPEND.concat( + redirectRoute.concat( + this.buildInitialQueryParameter( + QueryParameterNames.ERROR_CODE.getValue(), + QueryParameterMessages.NO_PERMISSIONS_TO_VIEW.getKeyAsString())))); + } + + protected static final String REDIRECT_PREPEND = "redirect:"; + + // Helper methods + private ModelAndView setErrorMessageFromQueryString( + final ModelAndView modelAndView, + final int errorCode + ) { + + final String errorMessage = QueryParameterMessages.mapMessage(errorCode); + + if (!StringUtils.isBlank(errorMessage)) { + modelAndView.addObject( + ViewModelNames.ERROR_MESSAGE.getValue(), + errorMessage); + } + + return modelAndView; + } + + // Properties + @Autowired + private ValidateActiveUserCommand validateActiveUserCommand; +} diff --git a/src/main/java/edu/uark/registerapp/controllers/MainMenuRouteController.java b/src/main/java/edu/uark/registerapp/controllers/MainMenuRouteController.java new file mode 100644 index 00000000..af408a6c --- /dev/null +++ b/src/main/java/edu/uark/registerapp/controllers/MainMenuRouteController.java @@ -0,0 +1,45 @@ +package edu.uark.registerapp.controllers; + +import java.util.UUID; +import javax.servlet.http.HttpServletRequest; +import java.util.Map; +import java.util.Optional; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PathVariable; +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.servlet.ModelAndView; + +import edu.uark.registerapp.commands.exceptions.UnauthorizedException; +import edu.uark.registerapp.controllers.enums.ViewNames; + +@Controller +@RequestMapping(value="/mainMenu") +public class MainMenuRouteController { + @RequestMapping(method = RequestMethod.GET) + public ModelAndView start( + @RequestParam Map queryParameters, + final HttpServletRequest httpServletRequest + ) { + ModelAndView modelAndView = new ModelAndView(ViewNames.MAIN_MENU.getViewName(), queryParameters); + final Optional ActiveUserEntity = + this.getCurrentUser(httpServletRequest); + return modelAndView; + } + + protected Optional getCurrentUser(final HttpServletRequest request) + { + try { + return Optional.of( + this.validateActiveUserCommand + .setSessionKey(request.getSession().getId()) + .execute()); + } catch (final UnauthorizedException e) { + return Optional.ofNullable(null); + } + } +} diff --git a/src/main/java/edu/uark/registerapp/controllers/ProductListingRouteController.java b/src/main/java/edu/uark/registerapp/controllers/ProductListingRouteController.java index 3c2a1178..e96a45fb 100644 --- a/src/main/java/edu/uark/registerapp/controllers/ProductListingRouteController.java +++ b/src/main/java/edu/uark/registerapp/controllers/ProductListingRouteController.java @@ -12,7 +12,7 @@ import edu.uark.registerapp.models.api.Product; @Controller -@RequestMapping(value = "/") +@RequestMapping(value = "/productListing") public class ProductListingRouteController { @RequestMapping(method = RequestMethod.GET) public ModelAndView showProductListing() { diff --git a/src/main/java/edu/uark/registerapp/controllers/SignInRestController.java b/src/main/java/edu/uark/registerapp/controllers/SignInRestController.java new file mode 100644 index 00000000..8ec7ef25 --- /dev/null +++ b/src/main/java/edu/uark/registerapp/controllers/SignInRestController.java @@ -0,0 +1,34 @@ +package edu.uark.registerapp.controllers; + +import javax.servlet.http.HttpServletRequest; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +import edu.uark.registerapp.commands.activeUsers.ActiveUserDeleteCommand; +import edu.uark.registerapp.controllers.enums.ViewNames; +import edu.uark.registerapp.models.api.ApiResponse; + +@RestController +@RequestMapping(value = "/api") +public class SignInRestController extends BaseRestController { + @RequestMapping(value="/signOut", method = RequestMethod.DELETE) + public @ResponseBody ApiResponse removeActiveUser( + final HttpServletRequest request + ) { + + this.activeUserDeleteCommand + .setSessionKey(request.getSession().getId()) + .execute(); + + return (new ApiResponse()) + .setRedirectUrl(ViewNames.SIGN_IN.getRoute()); + } + + // Properties + @Autowired + private ActiveUserDeleteCommand activeUserDeleteCommand; +} diff --git a/src/main/java/edu/uark/registerapp/controllers/SignInRouteController.java b/src/main/java/edu/uark/registerapp/controllers/SignInRouteController.java new file mode 100644 index 00000000..0f033b67 --- /dev/null +++ b/src/main/java/edu/uark/registerapp/controllers/SignInRouteController.java @@ -0,0 +1,88 @@ +package edu.uark.registerapp.controllers; + +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Controller; +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.servlet.ModelAndView; + +import edu.uark.registerapp.commands.employees.ActiveEmployeeExistsQuery; +import edu.uark.registerapp.commands.employees.EmployeeSignInCommand; +import edu.uark.registerapp.commands.exceptions.NotFoundException; +import edu.uark.registerapp.controllers.enums.QueryParameterNames; +import edu.uark.registerapp.controllers.enums.ViewModelNames; +import edu.uark.registerapp.controllers.enums.ViewNames; +import edu.uark.registerapp.models.api.EmployeeSignIn; + +@Controller +@RequestMapping(value = "/") +public class SignInRouteController extends BaseRouteController { + @RequestMapping(method = RequestMethod.GET) + public ModelAndView showSignIn( + @RequestParam final Map queryParameters) + { + + try { + this.activeEmployeeExistsQuery.execute(); + } catch (NotFoundException e) { + return new ModelAndView( + REDIRECT_PREPEND.concat( + ViewNames.EMPLOYEE_DETAIL.getRoute())); + } + + ModelAndView modelAndView = + this.setErrorMessageFromQueryString( + new ModelAndView(ViewNames.SIGN_IN.getViewName()), + queryParameters); + + if (queryParameters.containsKey(QueryParameterNames.EMPLOYEE_ID.getValue())) { + modelAndView.addObject( + ViewModelNames.EMPLOYEE_ID.getValue(), + queryParameters.get(QueryParameterNames.EMPLOYEE_ID.getValue())); + } + + return modelAndView; + } + + @RequestMapping(method = RequestMethod.POST, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) + public ModelAndView performSignIn( + EmployeeSignIn employeeSignIn, + HttpServletRequest request + ) { + try { + this.employeeSignInCommand + .setSessionId(request.getSession().getId()) + .setEmployeeSignIn(employeeSignIn) + .execute(); + } catch (Exception e) { + ModelAndView modelAndView = + new ModelAndView(ViewNames.SIGN_IN.getViewName()); + + modelAndView.addObject( + ViewModelNames.ERROR_MESSAGE.getValue(), + e.getMessage()); + modelAndView.addObject( + ViewModelNames.EMPLOYEE_ID.getValue(), + employeeSignIn.getEmployeeId()); + + return modelAndView; + } + + return new ModelAndView( + REDIRECT_PREPEND.concat( + ViewNames.MAIN_MENU.getRoute())); + } + + // Properties + @Autowired + private EmployeeSignInCommand employeeSignInCommand; + + @Autowired + private ActiveEmployeeExistsQuery activeEmployeeExistsQuery; +} diff --git a/src/main/java/edu/uark/registerapp/controllers/enums/QueryParameterMessages.java b/src/main/java/edu/uark/registerapp/controllers/enums/QueryParameterMessages.java new file mode 100644 index 00000000..3f1bed87 --- /dev/null +++ b/src/main/java/edu/uark/registerapp/controllers/enums/QueryParameterMessages.java @@ -0,0 +1,45 @@ +package edu.uark.registerapp.controllers.enums; + +import java.util.HashMap; +import java.util.Map; + +public enum QueryParameterMessages { + NOT_DEFINED(-1, ""), + SESSION_NOT_ACTIVE(1001, "The current user's session is no longer active."), + NO_PERMISSIONS_TO_VIEW(1101, "You do not have permission to view this resource."), + NO_PERMISSIONS_FOR_ACTION(1102, "You do not have permission to perform this action."); + + public int getKey() { + return this.key; + } + public String getKeyAsString() { + return Integer.toString(this.key); + } + public String getMessage() { + return this.message; + } + + public static String mapMessage(final int key) { + if (valueMap == null) { + valueMap = new HashMap(); + + for (final QueryParameterMessages status : QueryParameterMessages.values()) { + valueMap.put(status.getKey(), status.getMessage()); + } + } + + return (valueMap.containsKey(key) + ? valueMap.get(key) + : QueryParameterMessages.NOT_DEFINED.getMessage()); + } + + private int key; + private String message; + + private static Map valueMap = null; + + private QueryParameterMessages(final int key, final String message) { + this.key = key; + this.message = message; + } +} diff --git a/src/main/java/edu/uark/registerapp/controllers/enums/QueryParameterNames.java b/src/main/java/edu/uark/registerapp/controllers/enums/QueryParameterNames.java index 988a9c79..deb1ea41 100644 --- a/src/main/java/edu/uark/registerapp/controllers/enums/QueryParameterNames.java +++ b/src/main/java/edu/uark/registerapp/controllers/enums/QueryParameterNames.java @@ -2,8 +2,9 @@ public enum QueryParameterNames { NOT_DEFINED(""), - ERROR_CODE("errorCode"); - + ERROR_CODE("errorCode"), + EMPLOYEE_ID("employeeId"); + public String getValue() { return value; } diff --git a/src/main/java/edu/uark/registerapp/controllers/enums/ViewModelNames.java b/src/main/java/edu/uark/registerapp/controllers/enums/ViewModelNames.java index 7c39b102..cfb6d841 100644 --- a/src/main/java/edu/uark/registerapp/controllers/enums/ViewModelNames.java +++ b/src/main/java/edu/uark/registerapp/controllers/enums/ViewModelNames.java @@ -4,7 +4,10 @@ public enum ViewModelNames { NOT_DEFINED(""), ERROR_MESSAGE("errorMessage"), PRODUCTS("products"), // Product listing - PRODUCT("product"); // Product detail + PRODUCT("product"), // Product detail + EMPLOYEE_ID("employeeId"), // Sign in + EMPLOYEE("employee"), // Employee detail + EMPLOYEE_TYPES("employeeTypes"); public String getValue() { return value; diff --git a/src/main/java/edu/uark/registerapp/controllers/enums/ViewNames.java b/src/main/java/edu/uark/registerapp/controllers/enums/ViewNames.java index cd1a1246..9c68f8d5 100644 --- a/src/main/java/edu/uark/registerapp/controllers/enums/ViewNames.java +++ b/src/main/java/edu/uark/registerapp/controllers/enums/ViewNames.java @@ -1,8 +1,11 @@ package edu.uark.registerapp.controllers.enums; public enum ViewNames { - PRODUCT_DETAIL("productDetail"), - PRODUCT_LISTING("productListing", "/"); + SIGN_IN("signIn", "/"), + MAIN_MENU("mainMenu"), + PRODUCT_DETAIL("productDetail"), + EMPLOYEE_DETAIL("employeeDetail"), + PRODUCT_LISTING("productListing"); public String getRoute() { return this.route; diff --git a/src/main/java/edu/uark/registerapp/models/api/Employee.java b/src/main/java/edu/uark/registerapp/models/api/Employee.java new file mode 100644 index 00000000..6e313bfc --- /dev/null +++ b/src/main/java/edu/uark/registerapp/models/api/Employee.java @@ -0,0 +1,145 @@ +package edu.uark.registerapp.models.api; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.UUID; + +import org.apache.commons.lang3.StringUtils; + +import edu.uark.registerapp.commands.employees.helpers.EmployeeHelper; +import edu.uark.registerapp.models.entities.EmployeeEntity; + +public class Employee extends ApiResponse { + private UUID id; + public UUID getId() { + return this.id; + } + public Employee setId(final UUID id) { + this.id = id; + return this; + } + + private String employeeId; + public String getEmployeeId() { + return this.employeeId; + } + public Employee setEmployeeId(final int employeeId) { + this.employeeId = EmployeeHelper.padEmployeeId(employeeId); + return this; + } + public Employee setEmployeeId(final String employeeId) { + this.employeeId = employeeId; + return this; + } + + private String firstName; + public String getFirstName() { + return this.firstName; + } + public Employee setFirstName(final String firstName) { + this.firstName = firstName; + return this; + } + + private String lastName; + public String getLastName() { + return this.lastName; + } + public Employee setLastName(final String lastName) { + this.lastName = lastName; + return this; + } + + private String password; + public String getPassword() { + return this.password; + } + public Employee setPassword(final String password) { + this.password = password; + return this; + } + + private boolean isActive; + public boolean getIsActive() { + return this.isActive; + } + public Employee setIsActive(final boolean isActive) { + this.isActive = isActive; + return this; + } + + private int classification; + public int getClassification() { + return this.classification; + } + public Employee setClassification(final int classification) { + this.classification = classification; + return this; + } + + private UUID managerId; + public UUID getManagerId() { + return this.managerId; + } + public Employee setManagerId(final UUID managerId) { + this.managerId = managerId; + return this; + } + + private String createdOn; + public String getCreatedOn() { + return this.createdOn; + } + public Employee setCreatedOn(final String createdOn) { + this.createdOn = createdOn; + return this; + } + public Employee setCreatedOn(final LocalDateTime createdOn) { + this.createdOn = + createdOn.format(DateTimeFormatter.ofPattern("MM/dd/yyyy")); + + return this; + } + + private boolean isInitialEmployee; + public boolean getIsInitialEmployee() { + return this.isInitialEmployee; + } + public Employee setIsInitialEmployee(final boolean isInitialEmployee) { + this.isInitialEmployee = isInitialEmployee; + return this; + } + + public Employee() { + super(); + + this.isActive = true; + this.id = new UUID(0, 0); + this.classification = -1; + this.isInitialEmployee = false; + this.managerId = new UUID(0, 0); + this.lastName = StringUtils.EMPTY; + this.password = StringUtils.EMPTY; + this.firstName = StringUtils.EMPTY; + this.employeeId = StringUtils.EMPTY; + + this.setCreatedOn(LocalDateTime.now()); + } + + public Employee(final EmployeeEntity employeeEntity) { + super(false); + + this.isInitialEmployee = false; + this.id = employeeEntity.getId(); + this.password = StringUtils.EMPTY; + this.isActive = employeeEntity.getIsActive(); + this.lastName = employeeEntity.getLastName(); + this.firstName = employeeEntity.getFirstName(); + this.managerId = employeeEntity.getManagerId(); + this.classification = employeeEntity.getClassification(); + this.employeeId = + EmployeeHelper.padEmployeeId(employeeEntity.getEmployeeId()); + + this.setCreatedOn(employeeEntity.getCreatedOn()); + } +} diff --git a/src/main/java/edu/uark/registerapp/models/api/EmployeeSignIn.java b/src/main/java/edu/uark/registerapp/models/api/EmployeeSignIn.java new file mode 100644 index 00000000..fbefe176 --- /dev/null +++ b/src/main/java/edu/uark/registerapp/models/api/EmployeeSignIn.java @@ -0,0 +1,30 @@ +package edu.uark.registerapp.models.api; + +import org.apache.commons.lang3.StringUtils; + +public class EmployeeSignIn { + + public EmployeeSignIn() { + this.password = StringUtils.EMPTY; + this.employeeId = StringUtils.EMPTY; + } + + private String employeeId; + public String getEmployeeId() { + return this.employeeId; + } + public EmployeeSignIn setEmployeeId(final String employeeId) { + this.employeeId = employeeId; + return this; + } + + private String password; + public String getPassword() { + return this.password; + } + public EmployeeSignIn setPassword(final String password) { + this.password = password; + return this; + } + +} diff --git a/src/main/java/edu/uark/registerapp/models/entities/ActiveUserEntity.java b/src/main/java/edu/uark/registerapp/models/entities/ActiveUserEntity.java new file mode 100644 index 00000000..6eba3528 --- /dev/null +++ b/src/main/java/edu/uark/registerapp/models/entities/ActiveUserEntity.java @@ -0,0 +1,91 @@ +package edu.uark.registerapp.models.entities; + +import java.time.LocalDateTime; +import java.util.UUID; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +import org.apache.commons.lang3.StringUtils; +import org.hibernate.annotations.Generated; +import org.hibernate.annotations.GenerationTime; + +@Entity +@Table(name="activeuser") +public class ActiveUserEntity { + @Id + @Column(name="id", updatable = false) + @GeneratedValue(strategy=GenerationType.AUTO) + private final UUID id; + + public UUID getId() { + return this.id; + } + + @Column(name = "employeeid") + private UUID employeeId; + + public UUID getEmployeeId() { + return this.employeeId; + } + + public ActiveUserEntity setEmployeeId(final UUID employeeId) { + this.employeeId = employeeId; + return this; + } + + @Column(name = "name") + private String name; + + public String getName() { + return this.name; + } + + public ActiveUserEntity setName(final String name) { + this.name = name; + return this; + } + + @Column(name = "classification") + private int classification; + + public int getClassification() { + return this.classification; + } + + public ActiveUserEntity setClassification(final int classification) { + this.classification = classification; + return this; + } + + @Column(name = "sessionkey") + private String sessionKey; + + public String getSessionKey() { + return this.sessionKey; + } + + public ActiveUserEntity setSessionKey(final String sessionKey) { + this.sessionKey = sessionKey; + return this; + } + + @Column(name="createdon", insertable=false, updatable = false) + @Generated(GenerationTime.INSERT) + private LocalDateTime createdOn; + public LocalDateTime getCreatedOn() { + return this.createdOn; + } + + public ActiveUserEntity() { + this.id = new UUID(0, 0); + this.classification = -1; + this.name = StringUtils.EMPTY; + this.employeeId = new UUID(0, 0); + this.sessionKey = StringUtils.EMPTY; + } +} diff --git a/src/main/java/edu/uark/registerapp/models/entities/EmployeeEntity.java b/src/main/java/edu/uark/registerapp/models/entities/EmployeeEntity.java new file mode 100644 index 00000000..324e5b31 --- /dev/null +++ b/src/main/java/edu/uark/registerapp/models/entities/EmployeeEntity.java @@ -0,0 +1,164 @@ +package edu.uark.registerapp.models.entities; + +import java.time.LocalDateTime; +import java.util.UUID; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +import org.apache.commons.lang3.StringUtils; +import org.hibernate.annotations.Generated; +import org.hibernate.annotations.GenerationTime; + +import edu.uark.registerapp.commands.employees.helpers.EmployeeHelper; +import edu.uark.registerapp.models.api.Employee; + +@Entity +@Table(name="employee") +public class EmployeeEntity { + @Id + @Column(name="id", updatable = false) + @GeneratedValue(strategy=GenerationType.AUTO) + private final UUID id; + + public UUID getId() { + return this.id; + } + + @Column(name = "employeeid", insertable = false, updatable = false) + @Generated(GenerationTime.INSERT) + private int employeeId; + + public int getEmployeeId() { + return this.employeeId; + } + + @Column(name = "firstname") + private String firstName; + + public String getFirstName() { + return this.firstName; + } + + public EmployeeEntity setFirstName(final String firstName) { + this.firstName = firstName; + return this; + } + + @Column(name = "lastname") + private String lastName; + + public String getLastName() { + return this.lastName; + } + + public EmployeeEntity setLastName(final String lastName) { + this.lastName = lastName; + return this; + } + + @Column(name = "password") + private byte[] password; + + public byte[] getPassword() { + return this.password; + } + + public EmployeeEntity setPassword(final byte[] password) { + this.password = password; + return this; + } + + @Column(name = "active") + private boolean isActive; + + public boolean getIsActive() { + return this.isActive; + } + + public EmployeeEntity setIsActive(final boolean isActive) { + this.isActive = isActive; + return this; + } + + @Column(name = "classification") + private int classification; + + public int getClassification() { + return this.classification; + } + + public EmployeeEntity setClassification(final int classification) { + this.classification = classification; + return this; + } + + @Column(name = "managerid") + private UUID managerId; + + public UUID getManagerId() { + return this.managerId; + } + + public EmployeeEntity setManagerId(final UUID managerId) { + this.managerId = managerId; + return this; + } + + @Column(name = "createdon", insertable = false, updatable = false) + @Generated(GenerationTime.INSERT) + private LocalDateTime createdOn; + + public LocalDateTime getCreatedOn() { + return this.createdOn; + } + + public Employee synchronize(final Employee apiEmployee) { + this.setIsActive(apiEmployee.getIsActive()); + this.setLastName(apiEmployee.getLastName()); + this.setFirstName(apiEmployee.getFirstName()); + this.setClassification(apiEmployee.getClassification()); + if (apiEmployee.getManagerId() != null) { + this.setManagerId(apiEmployee.getManagerId()); + } + if (!StringUtils.isBlank(apiEmployee.getPassword())) { + this.setPassword( + EmployeeHelper.hashPassword( + apiEmployee.getPassword())); + } + + apiEmployee.setId(this.getId()); + apiEmployee.setCreatedOn(this.getCreatedOn()); + apiEmployee.setEmployeeId(this.getEmployeeId()); + + return apiEmployee; + } + + public EmployeeEntity() { + this.employeeId = -1; + this.isActive = false; + this.id = new UUID(0, 0); + this.classification = -1; + this.password = new byte[0]; + this.managerId = new UUID(0, 0); + this.lastName = StringUtils.EMPTY; + this.firstName = StringUtils.EMPTY; + } + + public EmployeeEntity(final Employee apiEmployee) { + this.id = new UUID(0, 0); + this.isActive = apiEmployee.getIsActive(); + this.lastName = apiEmployee.getLastName(); + this.firstName = apiEmployee.getFirstName(); + this.classification = apiEmployee.getClassification(); + this.password = EmployeeHelper.hashPassword(apiEmployee.getPassword()); + this.managerId = ( + (apiEmployee.getManagerId() != null) + ? apiEmployee.getManagerId() + : new UUID(0, 0)); + } +} diff --git a/src/main/java/edu/uark/registerapp/models/enums/EmployeeClassification.java b/src/main/java/edu/uark/registerapp/models/enums/EmployeeClassification.java new file mode 100644 index 00000000..751c80ef --- /dev/null +++ b/src/main/java/edu/uark/registerapp/models/enums/EmployeeClassification.java @@ -0,0 +1,58 @@ +package edu.uark.registerapp.models.enums; + +import java.util.HashMap; +import java.util.Map; + +public enum EmployeeClassification { + NOT_DEFINED(-1, "Not Selected"), + CASHIER(101, "Cashier"), + SHIFT_MANAGER(501, "Shift Manager"), + GENERAL_MANAGER(701, "General Manager"); + + public int getClassification() { + return this.classification; + } + + public String getDisplayLabel() { + return this.displayLabel; + } + + public static EmployeeClassification map(final int key) { + if (valueMap == null) { + valueMap = new HashMap(); + + for (final EmployeeClassification employeeClassification : EmployeeClassification.values()) { + valueMap.put( + employeeClassification.getClassification(), + employeeClassification); + } + } + + return ((valueMap.containsKey(key) + ? valueMap.get(key) + : EmployeeClassification.NOT_DEFINED)); + } + + public static boolean isElevatedUser(final int classification) { + final EmployeeClassification employeeClassification = + EmployeeClassification.map(classification); + + return ( + (employeeClassification == EmployeeClassification.GENERAL_MANAGER) + || (employeeClassification == EmployeeClassification.SHIFT_MANAGER)); + } + + private int classification; + private String displayLabel; + + private static Map valueMap = null; + + private EmployeeClassification( + final int classification, + final String displayLabel + ) { + + this.displayLabel = displayLabel; + this.classification = classification; + } +} diff --git a/src/main/java/edu/uark/registerapp/models/repositories/ActiveUserRepository.java b/src/main/java/edu/uark/registerapp/models/repositories/ActiveUserRepository.java new file mode 100644 index 00000000..53746cee --- /dev/null +++ b/src/main/java/edu/uark/registerapp/models/repositories/ActiveUserRepository.java @@ -0,0 +1,13 @@ +package edu.uark.registerapp.models.repositories; + +import java.util.Optional; +import java.util.UUID; + +import org.springframework.data.repository.CrudRepository; + +import edu.uark.registerapp.models.entities.ActiveUserEntity; + +public interface ActiveUserRepository extends CrudRepository { + Optional findByEmployeeId(UUID employeeId); + Optional findBySessionKey(String sessionKey); +} diff --git a/src/main/java/edu/uark/registerapp/models/repositories/EmployeeRepository.java b/src/main/java/edu/uark/registerapp/models/repositories/EmployeeRepository.java new file mode 100644 index 00000000..8ba4b81f --- /dev/null +++ b/src/main/java/edu/uark/registerapp/models/repositories/EmployeeRepository.java @@ -0,0 +1,15 @@ +package edu.uark.registerapp.models.repositories; + +import java.util.Optional; +import java.util.UUID; + +import org.springframework.data.repository.CrudRepository; + +import edu.uark.registerapp.models.entities.EmployeeEntity; + +public interface EmployeeRepository extends CrudRepository { + boolean existsByIsActive(boolean isActive); + boolean existsByEmployeeId(int employeeId); + Optional findById(UUID id); + Optional findByEmployeeId(int employeeId); +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 301f32d2..90024166 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,6 +1,9 @@ spring.session.store-type=HAZELCAST -spring.datasource.url=${JDBC_DATABASE_URL} + +# spring.datasource.url=${JDBC_DATABASE_URL} + +spring.datasource.url=jdbc:postgresql://ec2-3-213-102-175.compute-1.amazonaws.com:5432/dn9o10t6ampfu?password=70e6b479d4d3508904613d71173b12e78a6e3bf0fe2de09816397d3f88069102&sslmode=require&user=sdikxqjdmelzwx spring.datasource.username=${JDBC_DATABASE_USERNAME} spring.datasource.password=${JDBC_DATABASE_PASSWORD} spring.datasource.driverClassName=org.postgresql.Driver diff --git a/src/main/resources/static/scripts/mainMenu.js b/src/main/resources/static/scripts/mainMenu.js new file mode 100644 index 00000000..3b16c3f7 --- /dev/null +++ b/src/main/resources/static/scripts/mainMenu.js @@ -0,0 +1,43 @@ +document.addEventListener("DOMContentLoaded", function(event) { + getStartTransactionActionElement().addEventListener( + "click", + () => { displayError("Functionality has not yet been implemented."); }); + + getViewProductsActionElement().addEventListener( + "click", + () => { window.location.assign("/productListing"); }); + + getCreateEmployeeActionElement().addEventListener( + "click", + () => { window.location.assign("/employeeDetail"); }); + + getProductSalesReportActionElement().addEventListener( + "click", + () => { displayError("Functionality has not yet been implemented."); }); + + getCashierSalesReportActionElement().addEventListener( + "click", + () => { displayError("Functionality has not yet been implemented."); }); +}); + +// Getters and setters +function getViewProductsActionElement() { + return document.getElementById("viewProductsButton"); +} + +function getCreateEmployeeActionElement() { + return document.getElementById("createEmployeeButton"); +} + +function getStartTransactionActionElement() { + return document.getElementById("startTransactionButton"); +} + +function getProductSalesReportActionElement() { + return document.getElementById("productSalesReportButton"); +} + +function getCashierSalesReportActionElement() { + return document.getElementById("cashierSalesReportButton"); +} +// End getters and setters diff --git a/src/main/resources/static/scripts/signIn.js b/src/main/resources/static/scripts/signIn.js new file mode 100644 index 00000000..8872d9fc --- /dev/null +++ b/src/main/resources/static/scripts/signIn.js @@ -0,0 +1,88 @@ +// document.addEventListener("DOMContentLoaded", function(event) { +// const employeeIdEditElement = document.getElementById('employeeId'); + +// employeeIdEditElement.focus(); +// employeeIdEditElement.select(); +// }); + + + +// function validateForm(){ + +// const id = document.getElementById('employeeId') //sets the variable to the employeeId +// const pwd = document.getElementById('password') //sets the variable to the password +// console.log(id.value); + +// if(id != null && pwd != null ) //makes sure that the employeeId and password are not blank +// { +// console.log("!isNaN(Number(id.value)" + (!isNaN(Number(id.value)))); +// if(!isNaN(Number(id.value))) //checks to see if the emplyeeId is a number +// { +// console.log("id.value" + Number(id.value)); +// if(Number(id.value) > 0) //checks to see if the id value is greater than 0 +// { +// if(pwd.value.trim() !=='') //checks to see if the pwd value doesn't equal a blank after triming the whitespaces +// { +// return true; +// }else{ +// alert("Please fill out a valid Password."); //alerts the user if the password they've typed in doesn't meet the requirements. +// return false; +// } +// } +// else{ +// alert("Please fill out a valid EmployeeId1."); //alerts the user if the EmployeeId they've typed in doesn't meet the requirements. +// return false; +// } +// }else{ +// alert("Please fill out a valid EmployeeId."); //alerts the user if the EmployeeId they've typed in doesn't meet the requirements. +// return false; +// } +// }else{ +// alert("Please fill out a valid EmployeeId and Password."); //alerts the user if the password & employeeId they've typed in doesn't meet the requirements. +// return false; +// } +// } + +document.addEventListener("DOMContentLoaded", function(event) { + const employeeIdEditElement = getEmployeeIdEditElement(); + employeeIdEditElement.focus(); + employeeIdEditElement.select(); +}); + +function validateForm() { + const employeeIdEditElement = getEmployeeIdEditElement(); + if (isNaN(Number(employeeIdEditElement.value)) + || (Number(employeeIdEditElement.value) <= 0)) { + + displayError("Please provide a valid employee ID."); + + employeeIdEditElement.focus(); + employeeIdEditElement.select(); + + return false; + } + + const passwordEditElement = getPasswordEditElement(); + if ((passwordEditElement.value == null) + || (passwordEditElement.value.trim() === "")) { + + displayError("Please provide a valid password. It may not be blank."); + + passwordEditElement.focus(); + passwordEditElement.select(); + + return false; + } + + return true; +} + +//Getters and setters +function getPasswordEditElement() { + return document.getElementById("password"); +} + +function getEmployeeIdEditElement() { + return document.getElementById("employeeId"); +} +//End getters and setters \ No newline at end of file diff --git a/src/main/resources/templates/mainMenu.html b/src/main/resources/templates/mainMenu.html new file mode 100644 index 00000000..706fc531 --- /dev/null +++ b/src/main/resources/templates/mainMenu.html @@ -0,0 +1,62 @@ + + + + Register - Main Menu + + + + + + + + + + + + Main Menu + + + + + + + + + + Start Transaction + + View Products + + + Create Employee + + Product Sales + + Cashier Sales + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/templates/signIn.html b/src/main/resources/templates/signIn.html new file mode 100644 index 00000000..b66ac151 --- /dev/null +++ b/src/main/resources/templates/signIn.html @@ -0,0 +1,33 @@ + + + Register - Sign In + + + + + + + + Employee Sign In + + + + + + + + + + EmployeeID: + + + Password: + + + + + + + + + \ No newline at end of file