commit
535c0856e8
21 changed files with 1613 additions and 0 deletions
-
18pom.xml
-
11src/main/java/in/gov/secureapi/SecureApiApplication.java
-
84src/main/java/in/gov/secureapi/config/DataSourceConfig.java
-
77src/main/java/in/gov/secureapi/config/SecurityConfig.java
-
35src/main/java/in/gov/secureapi/controller/AuthController.java
-
50src/main/java/in/gov/secureapi/controller/CustomerController.java
-
113src/main/java/in/gov/secureapi/controller/DBFOT_controller.java
-
32src/main/java/in/gov/secureapi/controller/MainController.java
-
34src/main/java/in/gov/secureapi/exception/GlobalExceptionHandler.java
-
81src/main/java/in/gov/secureapi/filter/BearerTokenFilter.java
-
30src/main/java/in/gov/secureapi/repository/CustomerRepository.java
-
47src/main/java/in/gov/secureapi/repository/TokenRepository.java
-
55src/main/java/in/gov/secureapi/repository/UserRepository.java
-
712src/main/java/in/gov/secureapi/service/DBFOT_service.java
-
34src/main/java/in/gov/secureapi/service/DateTimeUtil.java
-
97src/main/java/in/gov/secureapi/service/TokenService.java
-
30src/main/java/in/gov/secureapi/util/TokenUtil.java
-
3src/main/resources/application-local.properties
-
3src/main/resources/application-production.properties
-
67src/main/resources/application.properties
-
0src/main/resources/schema.sql
@ -0,0 +1,18 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> |
|||
<modelVersion>4.0.0</modelVersion> |
|||
<parent> |
|||
<groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.5.6</version><relativePath/> |
|||
</parent> |
|||
<groupId>in.gov.gcc</groupId><artifactId>secure-api</artifactId><version>1.0.0</version> |
|||
<properties><java.version>17</java.version></properties> |
|||
<dependencies> |
|||
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency> |
|||
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency> |
|||
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency> |
|||
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency> |
|||
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId></dependency> |
|||
<dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><scope>runtime</scope></dependency> |
|||
</dependencies> |
|||
<build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build> |
|||
</project> |
|||
@ -0,0 +1,11 @@ |
|||
package in.gov.secureapi; |
|||
|
|||
import org.springframework.boot.SpringApplication; |
|||
import org.springframework.boot.autoconfigure.SpringBootApplication; |
|||
|
|||
@SpringBootApplication |
|||
public class SecureApiApplication { |
|||
public static void main(String[] args) { |
|||
SpringApplication.run(SecureApiApplication.class, args); |
|||
} |
|||
} |
|||
@ -0,0 +1,84 @@ |
|||
package in.gov.secureapi.config; |
|||
|
|||
import java.util.Properties; |
|||
|
|||
import javax.sql.DataSource; |
|||
|
|||
import org.hibernate.jpa.HibernatePersistenceProvider; |
|||
import org.springframework.beans.factory.annotation.Qualifier; |
|||
import org.springframework.context.annotation.Bean; |
|||
import org.springframework.context.annotation.Configuration; |
|||
import org.springframework.context.annotation.Primary; |
|||
import org.springframework.data.jpa.repository.config.EnableJpaRepositories; |
|||
import org.springframework.jdbc.datasource.DriverManagerDataSource; |
|||
import org.springframework.orm.jpa.JpaTransactionManager; |
|||
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; |
|||
import org.springframework.transaction.PlatformTransactionManager; |
|||
import org.springframework.transaction.annotation.EnableTransactionManagement; |
|||
|
|||
@Configuration |
|||
@EnableTransactionManagement |
|||
public class DataSourceConfig { |
|||
|
|||
// private static String host = "localhost:3306"; |
|||
// private static String dbpassword = "root"; |
|||
// private static String dbpassword = "gccroot"; |
|||
|
|||
// AWS |
|||
private static String host = |
|||
"gcc-facial-db-instance-1.cf48eqcciziq.ap-south-1.rds.amazonaws.com:3306"; |
|||
private static String dbpassword = "gcc-facial-password"; |
|||
|
|||
////////////////////////////// (For GCC APP) //////////////////////// |
|||
@Configuration |
|||
@EnableTransactionManagement |
|||
@EnableJpaRepositories(entityManagerFactoryRef = "appEntityManagerFactory", transactionManagerRef = "appTransactionManager", basePackages = { |
|||
"in.gov.chennaicorporation" }) |
|||
|
|||
public static class MysqlAppDataSourceConfig { |
|||
@Primary |
|||
@Bean(name = "mysqlAppDataSource") |
|||
public DataSource mysqlAppDataSource() { |
|||
DriverManagerDataSource dataSource = new DriverManagerDataSource(); |
|||
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver"); |
|||
dataSource.setUrl("jdbc:mysql://" + host + "/gcc_apps"); |
|||
dataSource.setUsername("root"); |
|||
dataSource.setPassword(dbpassword); |
|||
return dataSource; |
|||
} |
|||
|
|||
@Primary |
|||
@Bean(name = "appEntityManagerFactory") |
|||
public LocalContainerEntityManagerFactoryBean entityManagerFactory( |
|||
@Qualifier("mysqlAppDataSource") DataSource dataSource) { |
|||
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean(); |
|||
entityManagerFactoryBean.setDataSource(dataSource); |
|||
entityManagerFactoryBean.setPackagesToScan("in.gov.chennaicorporation"); |
|||
entityManagerFactoryBean.setPersistenceProviderClass(HibernatePersistenceProvider.class); |
|||
Properties properties = new Properties(); |
|||
properties.setProperty("hibernate.hbm2ddl.auto", "update"); |
|||
entityManagerFactoryBean.setJpaProperties(properties); |
|||
|
|||
return entityManagerFactoryBean; |
|||
} |
|||
|
|||
@Primary |
|||
@Bean(name = "appTransactionManager") |
|||
public PlatformTransactionManager transactionManager( |
|||
@Qualifier("appEntityManagerFactory") jakarta.persistence.EntityManagerFactory entityManagerFactory) { |
|||
return new JpaTransactionManager(entityManagerFactory); |
|||
} |
|||
} |
|||
|
|||
////////// DBFOT_toilets ////////////////////// |
|||
@Bean(name = "mysqldbfottoiletsGccDataSource") |
|||
public DataSource mysqldbfottoiletsGccDataSource() { |
|||
DriverManagerDataSource dataSource = new DriverManagerDataSource(); |
|||
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver"); |
|||
dataSource.setUrl("jdbc:mysql://" + host + "/dbfot_toilets"); |
|||
dataSource.setUsername("root"); |
|||
dataSource.setPassword(dbpassword); |
|||
return dataSource; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,77 @@ |
|||
package in.gov.secureapi.config; |
|||
|
|||
import com.fasterxml.jackson.databind.ObjectMapper; |
|||
import jakarta.servlet.http.HttpServletResponse; |
|||
import org.springframework.context.annotation.Bean; |
|||
import org.springframework.context.annotation.Configuration; |
|||
import org.springframework.http.MediaType; |
|||
import org.springframework.security.config.annotation.web.builders.HttpSecurity; |
|||
import org.springframework.security.config.http.SessionCreationPolicy; |
|||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; |
|||
import org.springframework.security.crypto.password.PasswordEncoder; |
|||
import org.springframework.security.web.SecurityFilterChain; |
|||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; |
|||
|
|||
import in.gov.secureapi.filter.BearerTokenFilter; |
|||
|
|||
import java.time.LocalDateTime; |
|||
import java.time.format.DateTimeFormatter; |
|||
import java.util.LinkedHashMap; |
|||
import java.util.Map; |
|||
|
|||
@Configuration |
|||
public class SecurityConfig { |
|||
|
|||
private final ObjectMapper objectMapper = new ObjectMapper(); |
|||
|
|||
@Bean |
|||
public PasswordEncoder passwordEncoder() { |
|||
return new BCryptPasswordEncoder(); |
|||
} |
|||
|
|||
@Bean |
|||
SecurityFilterChain securityFilterChain(HttpSecurity http, BearerTokenFilter bearerTokenFilter) throws Exception { |
|||
http.csrf(csrf -> csrf.disable()) |
|||
.formLogin(form -> form.disable()) |
|||
.httpBasic(basic -> basic.disable()) |
|||
.sessionManagement(sm -> sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) |
|||
.exceptionHandling(ex -> ex |
|||
.authenticationEntryPoint((request, response, authException) -> { |
|||
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); |
|||
response.setContentType(MediaType.APPLICATION_JSON_VALUE); |
|||
response.setCharacterEncoding("UTF-8"); |
|||
|
|||
Map<String, Object> body = new LinkedHashMap<>(); |
|||
body.put("timestamp", LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)); |
|||
body.put("status", false); |
|||
body.put("statusCode", HttpServletResponse.SC_UNAUTHORIZED); |
|||
body.put("error", "Unauthorized"); |
|||
body.put("message", "Authentication required. Please provide a valid Bearer token in Authorization header."); |
|||
body.put("path", request.getRequestURI()); |
|||
|
|||
objectMapper.writeValue(response.getOutputStream(), body); |
|||
}) |
|||
.accessDeniedHandler((request, response, accessDeniedException) -> { |
|||
response.setStatus(HttpServletResponse.SC_FORBIDDEN); |
|||
response.setContentType(MediaType.APPLICATION_JSON_VALUE); |
|||
response.setCharacterEncoding("UTF-8"); |
|||
|
|||
Map<String, Object> body = new LinkedHashMap<>(); |
|||
body.put("timestamp", LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)); |
|||
body.put("status", false); |
|||
body.put("statusCode", HttpServletResponse.SC_FORBIDDEN); |
|||
body.put("error", "Forbidden"); |
|||
body.put("message", "Access denied: you do not have permission to access this resource."); |
|||
body.put("path", request.getRequestURI()); |
|||
|
|||
objectMapper.writeValue(response.getOutputStream(), body); |
|||
}) |
|||
) |
|||
.authorizeHttpRequests(auth -> auth |
|||
.requestMatchers("/secure/api/auth/token", "/api/auth/token", "/error", "/actuator/health").permitAll() |
|||
.requestMatchers("/secure/**", "/api/**").authenticated() |
|||
.anyRequest().permitAll()) |
|||
.addFilterBefore(bearerTokenFilter, UsernamePasswordAuthenticationFilter.class); |
|||
return http.build(); |
|||
} |
|||
} |
|||
@ -0,0 +1,35 @@ |
|||
package in.gov.secureapi.controller; |
|||
|
|||
import jakarta.validation.Valid; |
|||
import jakarta.validation.constraints.NotBlank; |
|||
import org.springframework.http.ResponseEntity; |
|||
import org.springframework.web.bind.annotation.*; |
|||
|
|||
import in.gov.secureapi.service.TokenService; |
|||
|
|||
import java.util.Map; |
|||
|
|||
@RestController |
|||
@RequestMapping("/secure/api/auth") |
|||
public class AuthController { |
|||
private final TokenService tokenService; |
|||
public AuthController(TokenService tokenService) { this.tokenService = tokenService; } |
|||
|
|||
@PostMapping("/token") |
|||
public ResponseEntity<?> createToken(@Valid @RequestBody TokenRequest request) { |
|||
var tokenResult = tokenService.authenticateAndCreate(request.username(), request.password()); |
|||
return ResponseEntity.ok(Map.of("status", true, "message", "Token generated successfully", "data", tokenResult)); |
|||
} |
|||
|
|||
@PostMapping("/revoke") |
|||
public ResponseEntity<?> revoke(@RequestHeader(value="Authorization", required=false) String header) { |
|||
if (header == null || !header.startsWith("Bearer ")) return ResponseEntity.badRequest().body(Map.of("status",false,"message","Bearer token is required")); |
|||
tokenService.revoke(header.substring(7).trim()); |
|||
return ResponseEntity.ok(Map.of("status",true,"message","Token revoked successfully")); |
|||
} |
|||
|
|||
public record TokenRequest( |
|||
@NotBlank(message = "username is required") String username, |
|||
@NotBlank(message = "password is required") String password |
|||
) {} |
|||
} |
|||
@ -0,0 +1,50 @@ |
|||
package in.gov.secureapi.controller; |
|||
|
|||
import jakarta.validation.Valid; |
|||
import jakarta.validation.constraints.Email; |
|||
import jakarta.validation.constraints.NotBlank; |
|||
import org.springframework.http.HttpStatus; |
|||
import org.springframework.http.ResponseEntity; |
|||
import org.springframework.web.bind.annotation.*; |
|||
|
|||
import in.gov.secureapi.repository.CustomerRepository; |
|||
|
|||
import java.util.Map; |
|||
|
|||
@RestController |
|||
@RequestMapping("/api/customers") |
|||
public class CustomerController { |
|||
private final CustomerRepository repo; |
|||
public CustomerController(CustomerRepository repo) { this.repo = repo; } |
|||
|
|||
@PostMapping |
|||
public ResponseEntity<?> create(@Valid @RequestBody CustomerRequest req) { |
|||
long id = repo.insert(req.name(), req.mobile(), req.email(), req.address()); |
|||
return ResponseEntity.status(HttpStatus.CREATED).body(Map.of("status",true,"message","Customer created successfully","id",id)); |
|||
} |
|||
|
|||
@GetMapping |
|||
public ResponseEntity<?> all() { return ResponseEntity.ok(Map.of("status",true,"data",repo.findAll())); } |
|||
|
|||
@GetMapping("/{id}") |
|||
public ResponseEntity<?> one(@PathVariable long id) { |
|||
return repo.findById(id).<ResponseEntity<?>>map(x -> ResponseEntity.ok(Map.of("status",true,"data",x))) |
|||
.orElseGet(() -> ResponseEntity.status(404).body(Map.of("status",false,"message","Customer not found"))); |
|||
} |
|||
|
|||
@PutMapping("/{id}") |
|||
public ResponseEntity<?> update(@PathVariable long id, @Valid @RequestBody CustomerRequest req) { |
|||
int n = repo.update(id, req.name(), req.mobile(), req.email(), req.address()); |
|||
if (n == 0) return ResponseEntity.status(404).body(Map.of("status",false,"message","Customer not found")); |
|||
return ResponseEntity.ok(Map.of("status",true,"message","Customer updated successfully")); |
|||
} |
|||
|
|||
@DeleteMapping("/{id}") |
|||
public ResponseEntity<?> delete(@PathVariable long id) { |
|||
int n = repo.delete(id); |
|||
if (n == 0) return ResponseEntity.status(404).body(Map.of("status",false,"message","Customer not found")); |
|||
return ResponseEntity.ok(Map.of("status",true,"message","Customer deleted successfully")); |
|||
} |
|||
|
|||
public record CustomerRequest(@NotBlank String name, String mobile, @Email String email, String address) {} |
|||
} |
|||
@ -0,0 +1,113 @@ |
|||
package in.gov.secureapi.controller; |
|||
|
|||
|
|||
import java.util.List; |
|||
import java.util.Map; |
|||
|
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.http.MediaType; |
|||
import org.springframework.web.bind.annotation.PostMapping; |
|||
import org.springframework.web.bind.annotation.RequestMapping; |
|||
import org.springframework.web.bind.annotation.RequestParam; |
|||
import org.springframework.web.bind.annotation.RequestPart; |
|||
import org.springframework.web.bind.annotation.RestController; |
|||
import org.springframework.web.multipart.MultipartFile; |
|||
|
|||
import in.gov.secureapi.service.DBFOT_service; |
|||
|
|||
@RestController |
|||
@RequestMapping("/secure/api/dbfotcontroller") |
|||
public class DBFOT_controller { |
|||
|
|||
@Autowired |
|||
private DBFOT_service dbfotService; |
|||
|
|||
@PostMapping("/savecomplaints") |
|||
public List<?> savecomplaints( |
|||
@RequestParam(value = "zone", required = true) String zone, |
|||
@RequestParam(value = "ward", required = true) String ward, |
|||
@RequestParam(value = "location", required = true) String location, |
|||
@RequestParam(value = "status", required = true) String status, |
|||
@RequestParam(value = "address", required = true) String address, |
|||
@RequestParam(value = "source", required = true) String source, |
|||
@RequestParam(value = "pct", required = true) String pct, |
|||
@RequestParam(value = "complaint_id", required = true) String complaint_id, |
|||
@RequestParam(value = "description", required = true) String description, |
|||
@RequestParam(value = "turn_around_time", required = true) String turn_around_time, |
|||
@RequestParam(value = "vendorid", required = true) String vendorid |
|||
) |
|||
{ |
|||
|
|||
return dbfotService.savecomplaints(zone,ward,location,status,address,source,pct,complaint_id,description,turn_around_time,vendorid); |
|||
|
|||
} |
|||
|
|||
@PostMapping("/savecompshistory") |
|||
public List<?> savecomplaints_history( |
|||
@RequestParam(value = "complaint_id", required = true) String complaint_id, |
|||
@RequestParam(value = "status", required = true) String status, |
|||
@RequestParam(value = "complaint_raised_on", required = true) String complaint_raised_on, |
|||
@RequestParam(value = "complaint_raised_by", required = true) String complaint_raised_by, |
|||
@RequestParam(value = "complaint_updated_on", required = true) String complaint_updated_on, |
|||
@RequestParam(value = "complaint_updated_by", required = true) String complaint_updated_by, |
|||
@RequestParam(value = "remarks", required = true) String remarks, |
|||
@RequestParam(value = "imgfile", required = false) MultipartFile imgfile, |
|||
@RequestParam(value = "cby", required = true) String cby |
|||
) |
|||
{ |
|||
|
|||
return dbfotService.savecomplaints_history(complaint_id,status,complaint_raised_on,complaint_raised_by,complaint_updated_on,complaint_updated_by,remarks,imgfile,cby); |
|||
|
|||
} |
|||
|
|||
@PostMapping("/savemanpower") |
|||
public List<?> savemanpower( |
|||
@RequestParam(value = "zone", required = true) String zone, |
|||
@RequestParam(value = "ward", required = true) String ward, |
|||
@RequestParam(value = "category", required = true) String category, |
|||
@RequestParam(value = "pct", required = true) String pct, |
|||
@RequestParam(value = "status", required = true) String status, |
|||
@RequestParam(value = "vendorid", required = true) String vendorid |
|||
) |
|||
{ |
|||
|
|||
return dbfotService.savemanpower(zone,ward,category,pct,status,vendorid); |
|||
|
|||
} |
|||
|
|||
@PostMapping(value = "/savemanpoweremps", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) |
|||
public List<?> savemanpoweremps( |
|||
@RequestParam(value = "pct", required = true) String pct, |
|||
@RequestPart(value = "employee_details", required = true) String employee_details, |
|||
@RequestParam Map<String, MultipartFile> files |
|||
){ |
|||
|
|||
return dbfotService.savemanpoweremps(pct,employee_details,files); |
|||
} |
|||
|
|||
@PostMapping("/savepctmaster") |
|||
public List<?> savepctmaster( |
|||
@RequestParam(value="zone",required = true) String zone, |
|||
@RequestParam(value="ward",required = true) String ward, |
|||
@RequestParam(value="address",required = true) String address, |
|||
@RequestParam(value="category",required = true) String category, |
|||
@RequestParam(value="pct",required = true) String pct, |
|||
@RequestParam(value="status",required = true) String status, |
|||
@RequestParam(value="vendorid",required = true) String vendorid |
|||
){ |
|||
|
|||
return dbfotService.savepctmaster(zone,ward,address,category,pct,status,vendorid); |
|||
} |
|||
|
|||
@PostMapping("/updatepctstatus") |
|||
public List<?> updatepctstatus( |
|||
@RequestParam(value="pct",required = true) String pct, |
|||
@RequestParam(value="status",required = true) String status, |
|||
@RequestParam(value="vendorid",required = true) String vendorid |
|||
){ |
|||
|
|||
return dbfotService.updatepctstatus(pct,status,vendorid); |
|||
} |
|||
|
|||
|
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
package in.gov.secureapi.controller; |
|||
|
|||
import org.springframework.http.ResponseEntity; |
|||
import org.springframework.security.core.Authentication; |
|||
import org.springframework.web.bind.annotation.GetMapping; |
|||
import org.springframework.web.bind.annotation.RequestMapping; |
|||
import org.springframework.web.bind.annotation.RestController; |
|||
|
|||
import java.time.LocalDateTime; |
|||
import java.time.format.DateTimeFormatter; |
|||
import java.util.LinkedHashMap; |
|||
import java.util.Map; |
|||
|
|||
@RestController |
|||
@RequestMapping("/secure") |
|||
public class MainController { |
|||
|
|||
@GetMapping |
|||
public ResponseEntity<?> index(Authentication authentication) { |
|||
String username = (authentication != null && authentication.isAuthenticated()) |
|||
? authentication.getName() |
|||
: "Anonymous"; |
|||
|
|||
Map<String, Object> response = new LinkedHashMap<>(); |
|||
response.put("status", true); |
|||
response.put("message", "Secure endpoint accessed successfully"); |
|||
response.put("user", username); |
|||
response.put("timestamp", LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)); |
|||
|
|||
return ResponseEntity.ok(response); |
|||
} |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
package in.gov.secureapi.exception; |
|||
|
|||
import org.springframework.http.HttpStatus; |
|||
import org.springframework.http.ResponseEntity; |
|||
import org.springframework.security.authentication.BadCredentialsException; |
|||
import org.springframework.security.authentication.DisabledException; |
|||
import org.springframework.web.bind.MethodArgumentNotValidException; |
|||
import org.springframework.web.bind.annotation.ExceptionHandler; |
|||
import org.springframework.web.bind.annotation.RestControllerAdvice; |
|||
import java.util.Map; |
|||
|
|||
@RestControllerAdvice |
|||
public class GlobalExceptionHandler { |
|||
@ExceptionHandler(MethodArgumentNotValidException.class) |
|||
public ResponseEntity<?> validation(MethodArgumentNotValidException ex) { |
|||
String message = ex.getBindingResult().getFieldErrors().stream() |
|||
.map(e -> e.getField() + ": " + e.getDefaultMessage()).findFirst().orElse("Validation failed"); |
|||
return ResponseEntity.badRequest().body(Map.of("status", false, "message", message)); |
|||
} |
|||
|
|||
@ExceptionHandler({BadCredentialsException.class, DisabledException.class}) |
|||
public ResponseEntity<?> authenticationError(Exception ex) { |
|||
return ResponseEntity.status(HttpStatus.UNAUTHORIZED) |
|||
.body(Map.of("status", false, "message", ex.getMessage())); |
|||
} |
|||
|
|||
@ExceptionHandler(Exception.class) |
|||
public ResponseEntity<?> other(Exception ex) { |
|||
ex.printStackTrace(); |
|||
|
|||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) |
|||
.body(Map.of("status", false, "message", "Internal server error: " + ex.getMessage())); |
|||
} |
|||
} |
|||
@ -0,0 +1,81 @@ |
|||
package in.gov.secureapi.filter; |
|||
|
|||
import com.fasterxml.jackson.databind.ObjectMapper; |
|||
import jakarta.servlet.FilterChain; |
|||
import jakarta.servlet.ServletException; |
|||
import jakarta.servlet.http.HttpServletRequest; |
|||
import jakarta.servlet.http.HttpServletResponse; |
|||
import org.springframework.http.MediaType; |
|||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; |
|||
import org.springframework.security.core.authority.AuthorityUtils; |
|||
import org.springframework.security.core.context.SecurityContextHolder; |
|||
import org.springframework.stereotype.Component; |
|||
import org.springframework.web.filter.OncePerRequestFilter; |
|||
|
|||
import in.gov.secureapi.service.TokenService; |
|||
import in.gov.secureapi.service.TokenService.TokenStatus; |
|||
|
|||
import java.io.IOException; |
|||
import java.time.LocalDateTime; |
|||
import java.time.format.DateTimeFormatter; |
|||
import java.util.LinkedHashMap; |
|||
import java.util.Map; |
|||
|
|||
@Component |
|||
public class BearerTokenFilter extends OncePerRequestFilter { |
|||
private final TokenService tokenService; |
|||
private final ObjectMapper objectMapper = new ObjectMapper(); |
|||
|
|||
public BearerTokenFilter(TokenService tokenService) { |
|||
this.tokenService = tokenService; |
|||
} |
|||
|
|||
@Override |
|||
protected boolean shouldNotFilter(HttpServletRequest request) { |
|||
String path = request.getRequestURI(); |
|||
return path.startsWith("/secure/api/auth/token") || path.startsWith("/api/auth/token") || path.startsWith("/error") || path.startsWith("/actuator"); |
|||
} |
|||
|
|||
@Override |
|||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) |
|||
throws ServletException, IOException { |
|||
String header = request.getHeader("Authorization"); |
|||
|
|||
if (header != null && header.startsWith("Bearer ")) { |
|||
String token = header.substring(7).trim(); |
|||
if (!token.isBlank()) { |
|||
var validation = tokenService.validateDetailed(token); |
|||
|
|||
if (validation.status() == TokenStatus.VALID) { |
|||
var auth = new UsernamePasswordAuthenticationToken(validation.info().username(), null, AuthorityUtils.NO_AUTHORITIES); |
|||
SecurityContextHolder.getContext().setAuthentication(auth); |
|||
} else { |
|||
sendErrorResponse(response, HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized", validation.message(), request.getRequestURI()); |
|||
return; |
|||
} |
|||
} else { |
|||
sendErrorResponse(response, HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized", "Bearer token is empty", request.getRequestURI()); |
|||
return; |
|||
} |
|||
} |
|||
|
|||
chain.doFilter(request, response); |
|||
} |
|||
|
|||
private void sendErrorResponse(HttpServletResponse response, int statusCode, String error, String message, String path) |
|||
throws IOException { |
|||
response.setStatus(statusCode); |
|||
response.setContentType(MediaType.APPLICATION_JSON_VALUE); |
|||
response.setCharacterEncoding("UTF-8"); |
|||
|
|||
Map<String, Object> body = new LinkedHashMap<>(); |
|||
body.put("timestamp", LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)); |
|||
body.put("status", false); |
|||
body.put("statusCode", statusCode); |
|||
body.put("error", error); |
|||
body.put("message", message); |
|||
body.put("path", path); |
|||
|
|||
objectMapper.writeValue(response.getOutputStream(), body); |
|||
} |
|||
} |
|||
@ -0,0 +1,30 @@ |
|||
package in.gov.secureapi.repository; |
|||
|
|||
import org.springframework.jdbc.core.JdbcTemplate; |
|||
import org.springframework.stereotype.Repository; |
|||
import java.util.List; |
|||
import java.util.Map; |
|||
import java.util.Optional; |
|||
|
|||
@Repository |
|||
public class CustomerRepository { |
|||
private final JdbcTemplate jdbc; |
|||
public CustomerRepository(JdbcTemplate jdbc) { this.jdbc = jdbc; } |
|||
|
|||
public long insert(String name, String mobile, String email, String address) { |
|||
jdbc.update("INSERT INTO customers(name,mobile,email,address) VALUES (?,?,?,?)", name, mobile, email, address); |
|||
Long id = jdbc.queryForObject("SELECT LAST_INSERT_ID()", Long.class); |
|||
return id; |
|||
} |
|||
public List<Map<String,Object>> findAll() { |
|||
return jdbc.queryForList("SELECT id,name,mobile,email,address,created_at,updated_at FROM customers ORDER BY id DESC"); |
|||
} |
|||
public Optional<Map<String,Object>> findById(long id) { |
|||
var list = jdbc.queryForList("SELECT id,name,mobile,email,address,created_at,updated_at FROM customers WHERE id=?", id); |
|||
return list.stream().findFirst(); |
|||
} |
|||
public int update(long id, String name, String mobile, String email, String address) { |
|||
return jdbc.update("UPDATE customers SET name=?,mobile=?,email=?,address=? WHERE id=?", name,mobile,email,address,id); |
|||
} |
|||
public int delete(long id) { return jdbc.update("DELETE FROM customers WHERE id=?", id); } |
|||
} |
|||
@ -0,0 +1,47 @@ |
|||
package in.gov.secureapi.repository; |
|||
|
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.beans.factory.annotation.Qualifier; |
|||
import org.springframework.jdbc.core.JdbcTemplate; |
|||
import org.springframework.stereotype.Repository; |
|||
|
|||
import java.sql.Timestamp; |
|||
import java.time.LocalDateTime; |
|||
import java.util.Optional; |
|||
import javax.sql.DataSource; |
|||
|
|||
@Repository |
|||
public class TokenRepository { |
|||
private JdbcTemplate jdbc; |
|||
|
|||
@Autowired |
|||
public void setDataSource(@Qualifier("mysqldbfottoiletsGccDataSource") DataSource DBFOTTOILETSSource) { |
|||
this.jdbc = new JdbcTemplate(DBFOTTOILETSSource); |
|||
} |
|||
|
|||
public void save(String username, String tokenHash, LocalDateTime expiresAt) { |
|||
// Step 1: Deactivate any previous active tokens for this client |
|||
jdbc.update("UPDATE api_users SET active = FALSE WHERE username = ? AND active = TRUE", username); |
|||
|
|||
// Step 2: Insert the newly generated active token |
|||
jdbc.update("INSERT INTO api_users(username, token_hash, expires_at, active) VALUES (?, ?, ?, TRUE)", |
|||
username, tokenHash, Timestamp.valueOf(expiresAt)); |
|||
} |
|||
|
|||
public Optional<TokenInfo> findByTokenHash(String tokenHash) { |
|||
var list = jdbc.query("SELECT username, expires_at, active FROM api_users WHERE token_hash=? ORDER BY id DESC LIMIT 1", |
|||
(rs, row) -> new TokenInfo(rs.getString("username"), rs.getTimestamp("expires_at").toLocalDateTime(), rs.getBoolean("active")), tokenHash); |
|||
return list.stream().findFirst(); |
|||
} |
|||
|
|||
public Optional<TokenInfo> findValid(String tokenHash) { |
|||
return findByTokenHash(tokenHash) |
|||
.filter(info -> info.active() && info.expiresAt().isAfter(LocalDateTime.now())); |
|||
} |
|||
|
|||
public void revoke(String tokenHash) { |
|||
jdbc.update("UPDATE api_users SET active=FALSE WHERE token_hash=?", tokenHash); |
|||
} |
|||
|
|||
public record TokenInfo(String username, LocalDateTime expiresAt, boolean active) {} |
|||
} |
|||
@ -0,0 +1,55 @@ |
|||
package in.gov.secureapi.repository; |
|||
|
|||
import jakarta.annotation.PostConstruct; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.beans.factory.annotation.Qualifier; |
|||
import org.springframework.jdbc.core.JdbcTemplate; |
|||
import org.springframework.stereotype.Repository; |
|||
|
|||
import javax.sql.DataSource; |
|||
import java.util.Optional; |
|||
|
|||
@Repository |
|||
public class UserRepository { |
|||
|
|||
private JdbcTemplate jdbc; |
|||
|
|||
@Autowired |
|||
public void setDataSource(@Qualifier("mysqldbfottoiletsGccDataSource") DataSource dbfotToiletsSource) { |
|||
this.jdbc = new JdbcTemplate(dbfotToiletsSource); |
|||
} |
|||
|
|||
@PostConstruct |
|||
public void init() { |
|||
try { |
|||
jdbc.execute(""" |
|||
CREATE TABLE IF NOT EXISTS users ( |
|||
id BIGINT AUTO_INCREMENT PRIMARY KEY, |
|||
username VARCHAR(100) NOT NULL UNIQUE, |
|||
password VARCHAR(255) NOT NULL, |
|||
active TINYINT(1) NOT NULL DEFAULT 1, |
|||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP, |
|||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP |
|||
) |
|||
"""); |
|||
} catch (Exception e) { |
|||
// Log but don't fail if table or permissions already established |
|||
} |
|||
} |
|||
|
|||
public Optional<UserRecord> findByUsername(String username) { |
|||
var list = jdbc.query( |
|||
"SELECT id, username, password, active FROM users WHERE username = ? LIMIT 1", |
|||
(rs, rowNum) -> new UserRecord( |
|||
rs.getLong("id"), |
|||
rs.getString("username"), |
|||
rs.getString("password"), |
|||
rs.getBoolean("active") |
|||
), |
|||
username |
|||
); |
|||
return list.stream().findFirst(); |
|||
} |
|||
|
|||
public record UserRecord(long id, String username, String password, boolean active) {} |
|||
} |
|||
@ -0,0 +1,712 @@ |
|||
package in.gov.secureapi.service; |
|||
|
|||
import java.awt.image.BufferedImage; |
|||
import java.io.ByteArrayInputStream; |
|||
import java.io.ByteArrayOutputStream; |
|||
import java.io.IOException; |
|||
import java.nio.file.Files; |
|||
import java.nio.file.Path; |
|||
import java.nio.file.Paths; |
|||
import java.security.SecureRandom; |
|||
import java.sql.PreparedStatement; |
|||
import java.sql.Statement; |
|||
import java.util.Collections; |
|||
import java.util.HashMap; |
|||
import java.util.List; |
|||
import java.util.Map; |
|||
import java.util.Random; |
|||
import javax.imageio.ImageIO; |
|||
import javax.imageio.ImageWriteParam; |
|||
import javax.imageio.ImageWriter; |
|||
import javax.imageio.stream.ImageOutputStream; |
|||
import javax.sql.DataSource; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.beans.factory.annotation.Qualifier; |
|||
import org.springframework.core.env.Environment; |
|||
import org.springframework.jdbc.core.JdbcTemplate; |
|||
import org.springframework.jdbc.support.GeneratedKeyHolder; |
|||
import org.springframework.jdbc.support.KeyHolder; |
|||
import org.springframework.stereotype.Service; |
|||
import org.springframework.transaction.annotation.Transactional; |
|||
import org.springframework.web.multipart.MultipartFile; |
|||
|
|||
import com.fasterxml.jackson.core.type.TypeReference; |
|||
import com.fasterxml.jackson.databind.ObjectMapper; |
|||
|
|||
@Service |
|||
public class DBFOT_service { |
|||
|
|||
private JdbcTemplate jdbcTemplate; |
|||
|
|||
private final Environment environment; |
|||
private String fileBaseUrl; |
|||
|
|||
private static final String CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; |
|||
private static final int STRING_LENGTH = 15; |
|||
private static final Random RANDOM = new SecureRandom(); |
|||
|
|||
@Autowired |
|||
public void setDataSource(@Qualifier("mysqldbfottoiletsGccDataSource") DataSource DBFOTTOILETSSource) { |
|||
this.jdbcTemplate = new JdbcTemplate(DBFOTTOILETSSource); |
|||
} |
|||
|
|||
@Autowired |
|||
public DBFOT_service(Environment environment) { |
|||
this.environment = environment; |
|||
this.fileBaseUrl = environment.getProperty("fileBaseUrl"); |
|||
} |
|||
|
|||
public static String generateRandomString() { |
|||
StringBuilder result = new StringBuilder(STRING_LENGTH); |
|||
for (int i = 0; i < STRING_LENGTH; i++) { |
|||
result.append(CHARACTERS.charAt(RANDOM.nextInt(CHARACTERS.length()))); |
|||
} |
|||
return result.toString(); |
|||
} |
|||
|
|||
public static String generateRandomFileString(int lenthval) { |
|||
StringBuilder result = new StringBuilder(lenthval); |
|||
for (int i = 0; i < lenthval; i++) { |
|||
result.append(CHARACTERS.charAt(RANDOM.nextInt(CHARACTERS.length()))); |
|||
} |
|||
return result.toString(); |
|||
} |
|||
|
|||
private byte[] compressImage(BufferedImage image, float quality) throws IOException { |
|||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); |
|||
ImageOutputStream imageOutputStream = ImageIO.createImageOutputStream(byteArrayOutputStream); |
|||
|
|||
ImageWriter writer = ImageIO.getImageWritersByFormatName("jpg").next(); |
|||
writer.setOutput(imageOutputStream); |
|||
|
|||
ImageWriteParam param = writer.getDefaultWriteParam(); |
|||
if (param.canWriteCompressed()) { |
|||
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); |
|||
param.setCompressionQuality(quality); |
|||
} |
|||
|
|||
writer.write(null, new javax.imageio.IIOImage(image, null, null), param); |
|||
|
|||
writer.dispose(); |
|||
imageOutputStream.close(); |
|||
|
|||
return byteArrayOutputStream.toByteArray(); |
|||
} |
|||
|
|||
public String fileUpload(MultipartFile file, String name) { |
|||
|
|||
// Set the file path where you want to save it |
|||
String uploadDirectory = environment.getProperty("file.upload.directory"); |
|||
String serviceFolderName = environment.getProperty("dbfot_toilets"); |
|||
var year = DateTimeUtil.getCurrentYear(); |
|||
var month = DateTimeUtil.getCurrentMonth(); |
|||
|
|||
uploadDirectory = uploadDirectory + serviceFolderName + year + "/" + month; |
|||
|
|||
try { |
|||
// Create directory if it doesn't exist |
|||
Path directoryPath = Paths.get(uploadDirectory); |
|||
if (!Files.exists(directoryPath)) { |
|||
Files.createDirectories(directoryPath); |
|||
} |
|||
|
|||
// Datetime string |
|||
String datetimetxt = DateTimeUtil.getCurrentDateTime(); |
|||
|
|||
// File name |
|||
String fileName = name + "_" + datetimetxt + "_" + file.getOriginalFilename(); |
|||
fileName = fileName.replaceAll("\\s+", ""); // Remove space on filename |
|||
|
|||
String filePath = uploadDirectory + "/" + fileName; |
|||
|
|||
String filepath_txt = "/" + serviceFolderName + year + "/" + month + "/" + fileName; |
|||
|
|||
// Create a new Path object |
|||
Path path = Paths.get(filePath); |
|||
|
|||
// Get the bytes of the file |
|||
byte[] bytes = file.getBytes(); |
|||
|
|||
String originalFileName = file.getOriginalFilename(); |
|||
String extension = originalFileName.substring(originalFileName.lastIndexOf('.') + 1).toLowerCase(); |
|||
|
|||
if ("jpg".equals(extension) || "jpeg".equals(extension)) { |
|||
BufferedImage image = ImageIO.read(new ByteArrayInputStream(bytes)); |
|||
byte[] compressedBytes = compressImage(image, 0.5f); // Compress with 50% quality |
|||
|
|||
// Write the compressed bytes to the file |
|||
Files.write(path, compressedBytes); |
|||
} else { |
|||
// Write the original bytes to the file |
|||
Files.write(path, bytes); |
|||
} |
|||
|
|||
//System.out.println(filePath); |
|||
return filepath_txt; |
|||
|
|||
} catch (IOException e) { |
|||
e.printStackTrace(); |
|||
return "Failed to save file " + file.getOriginalFilename(); |
|||
} |
|||
} |
|||
|
|||
public List<?> savecomplaints(String zone, String ward, String location, String status, String address, |
|||
String source, String pct, String complaint_id, String description, String turn_around_time, |
|||
String vendorid) { |
|||
|
|||
Map<String, Object> response = new HashMap<>(); |
|||
|
|||
try { |
|||
|
|||
|
|||
String checkSql = |
|||
"SELECT id, zone, ward, location, status, address, source, pct, " + |
|||
"complaint_id, description, turn_around_time, vendorid, " + |
|||
"created_date, isactive, isdelete " + |
|||
"FROM complaints_details " + |
|||
"WHERE complaint_id = ? " + |
|||
"AND isactive = 1 " + |
|||
"AND isdelete = 0 "; |
|||
|
|||
List<Map<String, Object>> complaintList = |
|||
jdbcTemplate.queryForList(checkSql, complaint_id); |
|||
|
|||
|
|||
if (complaintList.isEmpty()) { |
|||
|
|||
String saveSql = "INSERT INTO complaints_details (zone,ward,location,status,address,source,pct,complaint_id,description,turn_around_time,vendorid) " + |
|||
"VALUES (?,?,?,?,?,?,?,?,?,?,?)"; |
|||
|
|||
KeyHolder keyHolder = new GeneratedKeyHolder(); |
|||
int affectedRows = jdbcTemplate.update(connection -> { |
|||
PreparedStatement ps = connection.prepareStatement(saveSql, Statement.RETURN_GENERATED_KEYS); |
|||
ps.setString(1, zone); |
|||
ps.setString(2, ward); |
|||
ps.setString(3, location); |
|||
ps.setString(4, status); |
|||
ps.setString(5, address); |
|||
ps.setString(6, source); |
|||
ps.setString(7, pct); |
|||
ps.setString(8, complaint_id); |
|||
ps.setString(9, description); |
|||
ps.setString(10, turn_around_time); |
|||
ps.setString(11, vendorid); |
|||
return ps; |
|||
}, keyHolder); |
|||
|
|||
if (affectedRows > 0) { |
|||
response.put("status", "Success"); |
|||
response.put("message", "Complaints Saved Successfully"); |
|||
} else { |
|||
response.put("status", "Failed"); |
|||
response.put("message", "Error while saving complaints"); |
|||
} |
|||
} |
|||
|
|||
else { |
|||
|
|||
response.put("status", "Failed"); |
|||
response.put("message", |
|||
"Duplicate Complaint ID " + complaint_id + |
|||
" already found in complaints_details"); |
|||
|
|||
return Collections.singletonList(response); |
|||
} |
|||
|
|||
|
|||
|
|||
|
|||
} catch (Exception e) { |
|||
e.printStackTrace(); |
|||
response.put("status", "Error"); |
|||
response.put("message", "Error while saving complaints"); |
|||
} |
|||
|
|||
return Collections.singletonList(response); |
|||
} |
|||
|
|||
|
|||
@Transactional |
|||
public List<?> savecomplaints_history(String complaint_id, String status, String complaint_raised_on, |
|||
String complaint_raised_by, String complaint_updated_on, String complaint_updated_by, String remarks, |
|||
MultipartFile imgfile, String cby) { |
|||
|
|||
Map<String, Object> response = new HashMap<>(); |
|||
|
|||
try { |
|||
|
|||
String checkSql = |
|||
"SELECT id, zone, ward, location, status, address, source, pct, " + |
|||
"complaint_id, description, turn_around_time, vendorid, " + |
|||
"created_date, isactive, isdelete " + |
|||
"FROM complaints_details " + |
|||
"WHERE complaint_id = ? " + |
|||
"AND isactive = 1 " + |
|||
"AND isdelete = 0 " + |
|||
"FOR UPDATE"; |
|||
|
|||
List<Map<String, Object>> complaintList = |
|||
jdbcTemplate.queryForList(checkSql, complaint_id); |
|||
|
|||
|
|||
if (complaintList.isEmpty()) { |
|||
|
|||
response.put("status", "Failed"); |
|||
response.put("message", |
|||
"Complaint ID " + complaint_id + |
|||
" not found in complaints_details"); |
|||
|
|||
return Collections.singletonList(response); |
|||
} |
|||
|
|||
Map<String, Object> complaint = complaintList.get(0); |
|||
|
|||
String imgFileValue = null; |
|||
|
|||
if (imgfile != null && !imgfile.isEmpty()) { |
|||
|
|||
|
|||
imgFileValue = fileUpload(imgfile, "dbfot_toilet_img"); |
|||
} |
|||
|
|||
String historySql = |
|||
"INSERT INTO complaints_history " + |
|||
"(complaint_id, status, complaint_raised_on, complaint_raised_by, " + |
|||
"complaint_updated_on, complaint_updated_by, remarks, imgfile, cby) " + |
|||
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"; |
|||
|
|||
|
|||
int historyAffectedRows = jdbcTemplate.update( |
|||
historySql, |
|||
complaint_id, |
|||
status, |
|||
complaint_raised_on, |
|||
complaint_raised_by, |
|||
complaint_updated_on, |
|||
complaint_updated_by, |
|||
remarks, |
|||
imgFileValue, |
|||
cby |
|||
); |
|||
|
|||
if (historyAffectedRows <= 0) { |
|||
|
|||
response.put("status", "Failed"); |
|||
response.put("message","Unable to save complaint history"); |
|||
|
|||
return Collections.singletonList(response); |
|||
} |
|||
|
|||
|
|||
String logSql = |
|||
"INSERT INTO complaints_details_log " + |
|||
"(cd_id, zone, ward, location, status, address, source, pct, " + |
|||
"complaint_id, description, turn_around_time, vendorid, " + |
|||
"created_date, isactive, isdelete) " + |
|||
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; |
|||
|
|||
|
|||
int logAffectedRows = jdbcTemplate.update( |
|||
logSql, |
|||
|
|||
complaint.get("id"), |
|||
complaint.get("zone"), |
|||
complaint.get("ward"), |
|||
complaint.get("location"), |
|||
complaint.get("status"), |
|||
complaint.get("address"), |
|||
complaint.get("source"), |
|||
complaint.get("pct"), |
|||
complaint.get("complaint_id"), |
|||
complaint.get("description"), |
|||
complaint.get("turn_around_time"), |
|||
complaint.get("vendorid"), |
|||
complaint.get("created_date"), |
|||
complaint.get("isactive"), |
|||
complaint.get("isdelete") |
|||
); |
|||
|
|||
if (logAffectedRows > 0) { |
|||
|
|||
String updateSql = |
|||
"UPDATE complaints_details " + |
|||
"SET status = ? " + |
|||
"WHERE complaint_id = ? " + |
|||
"AND isactive = 1 " + |
|||
"AND isdelete = 0"; |
|||
|
|||
|
|||
int updateAffectedRows = jdbcTemplate.update( |
|||
updateSql, |
|||
status, |
|||
complaint_id |
|||
); |
|||
|
|||
if (updateAffectedRows > 0) { |
|||
|
|||
response.put("status", "Success"); |
|||
response.put("message", |
|||
"Complaint history saved and complaint status updated successfully"); |
|||
|
|||
} else { |
|||
|
|||
/* |
|||
* Throw exception so @Transactional rolls everything back. |
|||
*/ |
|||
throw new RuntimeException( |
|||
"Unable to update complaint status" |
|||
); |
|||
} |
|||
|
|||
} else { |
|||
|
|||
/* |
|||
* Throw exception so history insert is also rolled back. |
|||
*/ |
|||
throw new RuntimeException( |
|||
"Unable to save complaint details log" |
|||
); |
|||
} |
|||
|
|||
|
|||
|
|||
} catch (Exception e) { |
|||
|
|||
e.printStackTrace(); |
|||
response.put("status", "Error"); |
|||
response.put("message", "Error while saving complaints history"); |
|||
|
|||
} |
|||
|
|||
return Collections.singletonList(response); |
|||
} |
|||
|
|||
public List<?> savemanpower(String zone, String ward, String category, String pct, String status,String vendorid) { |
|||
|
|||
Map<String, Object> response = new HashMap<>(); |
|||
|
|||
try { |
|||
|
|||
String checkSql = |
|||
"SELECT * " + |
|||
"FROM man_power_details " + |
|||
"WHERE pct = ? " + |
|||
"AND isactive = 1 " + |
|||
"AND isdelete = 0 "; |
|||
|
|||
List<Map<String, Object>> complaintList = |
|||
jdbcTemplate.queryForList(checkSql, pct); |
|||
|
|||
if (complaintList.isEmpty()) { |
|||
|
|||
|
|||
String saveSql = "INSERT INTO man_power_details (zone,ward,category,pct,status,vendorid) " + |
|||
"VALUES (?,?,?,?,?,?)"; |
|||
|
|||
KeyHolder keyHolder = new GeneratedKeyHolder(); |
|||
int affectedRows = jdbcTemplate.update(connection -> { |
|||
PreparedStatement ps = connection.prepareStatement(saveSql, Statement.RETURN_GENERATED_KEYS); |
|||
ps.setString(1, zone); |
|||
ps.setString(2, ward); |
|||
ps.setString(3, category); |
|||
ps.setString(4, pct); |
|||
ps.setString(5, status); |
|||
ps.setString(6, vendorid); |
|||
return ps; |
|||
}, keyHolder); |
|||
|
|||
if (affectedRows > 0) { |
|||
response.put("status", "Success"); |
|||
response.put("message", "Manpower details Saved Successfully"); |
|||
} else { |
|||
response.put("status", "Failed"); |
|||
response.put("message", "Error while saving man power details"); |
|||
} |
|||
|
|||
} |
|||
else { |
|||
response.put("status", "Failed"); |
|||
response.put("message","Duplicate PCT " + pct +" already found in man_power_details"); |
|||
|
|||
return Collections.singletonList(response); |
|||
} |
|||
|
|||
} catch (Exception e) { |
|||
e.printStackTrace(); |
|||
response.put("status", "Error"); |
|||
response.put("message", "Error while saving manpower details"); |
|||
} |
|||
|
|||
return Collections.singletonList(response); |
|||
} |
|||
|
|||
@Transactional |
|||
public List<?> savemanpoweremps(String pct, String employee_details,Map<String, MultipartFile> files) { |
|||
|
|||
Map<String, Object> response = new HashMap<>(); |
|||
|
|||
try { |
|||
|
|||
|
|||
String checkSql = |
|||
"SELECT * " + |
|||
"FROM man_power_details " + |
|||
"WHERE pct = ? " + |
|||
"AND isactive = 1 " + |
|||
"AND isdelete = 0 "; |
|||
|
|||
List<Map<String, Object>> complaintList = |
|||
jdbcTemplate.queryForList(checkSql, pct); |
|||
|
|||
if (complaintList.isEmpty()) { |
|||
|
|||
response.put("status", "Failed"); |
|||
response.put("message","PCT " + pct +" not found in man_power_details"); |
|||
|
|||
return Collections.singletonList(response); |
|||
} |
|||
else { |
|||
ObjectMapper objectMapper = new ObjectMapper(); |
|||
|
|||
List<Map<String, Object>> employeeList = |
|||
objectMapper.readValue( |
|||
employee_details, |
|||
new TypeReference<List<Map<String, Object>>>() {} |
|||
); |
|||
|
|||
if (employeeList == null || employeeList.isEmpty()) { |
|||
|
|||
response.put("status", "Failed"); |
|||
response.put("message", "Manpower Employee details cannot be empty"); |
|||
|
|||
return Collections.singletonList(response); |
|||
} |
|||
|
|||
String saveSql = |
|||
"INSERT INTO man_power_employees " + |
|||
"(pct, emp_name, att_date, location, mobile_number, imgfile) " + |
|||
"VALUES (?, ?, ?, ?, ?, ?)"; |
|||
|
|||
|
|||
int totalSaved = 0; |
|||
|
|||
for (Map<String, Object> employee : employeeList) { |
|||
|
|||
String empName =employee.get("emp_name") != null ? employee.get("emp_name").toString(): null; |
|||
|
|||
String attDate =employee.get("att_date") != null ? employee.get("att_date").toString() : null; |
|||
|
|||
String location =employee.get("location") != null ? employee.get("location").toString(): null; |
|||
|
|||
String mobileNumber =employee.get("mobile_number") != null ? employee.get("mobile_number").toString() : null; |
|||
|
|||
String fileKey =employee.get("fileKey") != null ? employee.get("fileKey").toString() : null; |
|||
|
|||
MultipartFile file = null; |
|||
String filePath = null; |
|||
|
|||
if (fileKey != null && !fileKey.trim().isEmpty()) { |
|||
|
|||
file = files.get(fileKey); |
|||
|
|||
if (file != null && !file.isEmpty()) { |
|||
|
|||
filePath = fileUpload(file, "dbfot_manpower_img"); |
|||
} |
|||
} |
|||
|
|||
int affectedRows = jdbcTemplate.update(saveSql,pct,empName,attDate,location,mobileNumber, filePath |
|||
); |
|||
|
|||
if (affectedRows > 0) { |
|||
|
|||
totalSaved++; |
|||
|
|||
} else { |
|||
|
|||
throw new RuntimeException("Failed to save employee: " + empName); |
|||
} |
|||
} |
|||
|
|||
response.put("status", "Success"); |
|||
response.put("message","Man power employees saved successfully"); |
|||
response.put("count", totalSaved); |
|||
} |
|||
|
|||
|
|||
} catch (Exception e) { |
|||
e.printStackTrace(); |
|||
throw new RuntimeException("Error while saving man power employees", e); |
|||
} |
|||
|
|||
return Collections.singletonList(response); |
|||
} |
|||
|
|||
public List<?> savepctmaster(String zone, String ward, String address, String category, String pct, |
|||
String status, String vendorid) { |
|||
|
|||
Map<String, Object> response = new HashMap<>(); |
|||
|
|||
try { |
|||
|
|||
String checkSql = |
|||
"SELECT * " + |
|||
"FROM pct_master " + |
|||
"WHERE pct = ? " + |
|||
"AND isactive = 1 " + |
|||
"AND isdelete = 0 "; |
|||
|
|||
List<Map<String, Object>> complaintList = |
|||
jdbcTemplate.queryForList(checkSql, pct); |
|||
|
|||
if (complaintList.isEmpty()) { |
|||
|
|||
|
|||
String saveSql = "INSERT INTO pct_master (zone,ward,address,category,pct,status,vendorid) " + |
|||
"VALUES (?,?,?,?,?,?,?)"; |
|||
|
|||
KeyHolder keyHolder = new GeneratedKeyHolder(); |
|||
int affectedRows = jdbcTemplate.update(connection -> { |
|||
PreparedStatement ps = connection.prepareStatement(saveSql, Statement.RETURN_GENERATED_KEYS); |
|||
ps.setString(1, zone); |
|||
ps.setString(2, ward); |
|||
ps.setString(3, address); |
|||
ps.setString(4, category); |
|||
ps.setString(5, pct); |
|||
ps.setString(6, status); |
|||
ps.setString(7, vendorid); |
|||
return ps; |
|||
}, keyHolder); |
|||
|
|||
if (affectedRows > 0) { |
|||
response.put("status", "Success"); |
|||
response.put("message","PCT Master Saved Successfully"); |
|||
} else { |
|||
response.put("status", "Failed"); |
|||
response.put("message", "Error while saving pct master"); |
|||
} |
|||
|
|||
} |
|||
else { |
|||
response.put("status", "Failed"); |
|||
response.put("message","Duplicate PCT " + pct +" already found in pct master"); |
|||
|
|||
return Collections.singletonList(response); |
|||
} |
|||
|
|||
} catch (Exception e) { |
|||
e.printStackTrace(); |
|||
response.put("status", "Error"); |
|||
response.put("message", "Error while saving pct master"); |
|||
} |
|||
|
|||
|
|||
return Collections.singletonList(response); |
|||
} |
|||
|
|||
public List<?> updatepctstatus(String pct, String status, String vendorid) { |
|||
|
|||
Map<String, Object> response = new HashMap<>(); |
|||
|
|||
try { |
|||
|
|||
String checkSql = |
|||
"SELECT id, zone, ward,address,category,pct,status, " + |
|||
"vendorid,created_date, isactive, isdelete " + |
|||
"FROM pct_master " + |
|||
"WHERE pct = ? " + |
|||
"AND isactive = 1 " + |
|||
"AND isdelete = 0 " + |
|||
"FOR UPDATE"; |
|||
|
|||
List<Map<String, Object>> complaintList = |
|||
jdbcTemplate.queryForList(checkSql, pct); |
|||
|
|||
|
|||
if (complaintList.isEmpty()) { |
|||
|
|||
response.put("status", "Failed"); |
|||
response.put("message","PCT " + pct + " not found in pct_master"); |
|||
|
|||
return Collections.singletonList(response); |
|||
} |
|||
|
|||
Map<String, Object> complaint = complaintList.get(0); |
|||
|
|||
String logSql = |
|||
"INSERT INTO pct_master_log " + |
|||
"(pct_master_id, zone, ward, address,category,pct, status, " + |
|||
"vendorid,created_date, isactive, isdelete) " + |
|||
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; |
|||
|
|||
|
|||
int logAffectedRows = jdbcTemplate.update( |
|||
logSql, |
|||
|
|||
complaint.get("id"), |
|||
complaint.get("zone"), |
|||
complaint.get("ward"), |
|||
complaint.get("address"), |
|||
complaint.get("category"), |
|||
complaint.get("pct"), |
|||
complaint.get("status"), |
|||
complaint.get("vendorid"), |
|||
complaint.get("created_date"), |
|||
complaint.get("isactive"), |
|||
complaint.get("isdelete") |
|||
|
|||
); |
|||
|
|||
if (logAffectedRows > 0) { |
|||
|
|||
String updateSql = |
|||
"UPDATE pct_master " + |
|||
"SET status = ? " + |
|||
"WHERE pct = ? " + |
|||
"AND isactive = 1 " + |
|||
"AND isdelete = 0"; |
|||
|
|||
|
|||
int updateAffectedRows = jdbcTemplate.update( |
|||
updateSql, |
|||
status, |
|||
pct |
|||
); |
|||
|
|||
if (updateAffectedRows > 0) { |
|||
|
|||
response.put("status", "Success"); |
|||
response.put("message","PCT status updated successfully"); |
|||
|
|||
} else { |
|||
|
|||
/* |
|||
* Throw exception so @Transactional rolls everything back. |
|||
*/ |
|||
throw new RuntimeException( |
|||
"Unable to update pct status" |
|||
); |
|||
} |
|||
|
|||
} else { |
|||
|
|||
/* |
|||
* Throw exception so history insert is also rolled back. |
|||
*/ |
|||
throw new RuntimeException( |
|||
"Unable to save in pct master log" |
|||
); |
|||
} |
|||
|
|||
|
|||
|
|||
} catch (Exception e) { |
|||
e.printStackTrace(); |
|||
response.put("status", "Error"); |
|||
response.put("message", "Error while updating pct master"); |
|||
} |
|||
|
|||
return Collections.singletonList(response); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
package in.gov.secureapi.service; |
|||
|
|||
import org.springframework.stereotype.Service; |
|||
import java.time.LocalDateTime; |
|||
import java.time.format.DateTimeFormatter; |
|||
|
|||
@Service |
|||
public class DateTimeUtil { |
|||
public static String getCurrentDateTime() { |
|||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("ddMMyyyyHHmmss"); |
|||
LocalDateTime now = LocalDateTime.now(); |
|||
return now.format(formatter); |
|||
} |
|||
public static String getCurrentYear() { |
|||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy"); |
|||
LocalDateTime now = LocalDateTime.now(); |
|||
return now.format(formatter); |
|||
} |
|||
public static String getCurrentMonth() { |
|||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM"); |
|||
LocalDateTime now = LocalDateTime.now(); |
|||
return now.format(formatter); |
|||
} |
|||
public static String getCurrentDay() { |
|||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd"); |
|||
LocalDateTime now = LocalDateTime.now(); |
|||
return now.format(formatter); |
|||
} |
|||
public static String getCurrentDateTimeMysql() { |
|||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); |
|||
LocalDateTime now = LocalDateTime.now(); |
|||
return now.format(formatter); |
|||
} |
|||
} |
|||
@ -0,0 +1,97 @@ |
|||
package in.gov.secureapi.service; |
|||
|
|||
import org.springframework.beans.factory.annotation.Value; |
|||
import org.springframework.security.authentication.BadCredentialsException; |
|||
import org.springframework.security.authentication.DisabledException; |
|||
import org.springframework.security.crypto.password.PasswordEncoder; |
|||
import org.springframework.stereotype.Service; |
|||
|
|||
import in.gov.secureapi.repository.TokenRepository; |
|||
import in.gov.secureapi.repository.UserRepository; |
|||
import in.gov.secureapi.util.TokenUtil; |
|||
|
|||
import java.time.LocalDateTime; |
|||
|
|||
@Service |
|||
public class TokenService { |
|||
private final TokenRepository repo; |
|||
private final UserRepository userRepository; |
|||
private final PasswordEncoder passwordEncoder; |
|||
private final long ttlHours; |
|||
|
|||
public TokenService( |
|||
TokenRepository repo, |
|||
UserRepository userRepository, |
|||
PasswordEncoder passwordEncoder, |
|||
@Value("${api.token.ttl-hours:24}") long ttlHours) { |
|||
this.repo = repo; |
|||
this.userRepository = userRepository; |
|||
this.passwordEncoder = passwordEncoder; |
|||
this.ttlHours = ttlHours; |
|||
} |
|||
|
|||
public TokenResult authenticateAndCreate(String username, String rawPassword) { |
|||
var userOpt = userRepository.findByUsername(username); |
|||
if (userOpt.isEmpty()) { |
|||
throw new BadCredentialsException("Invalid username or password"); |
|||
} |
|||
|
|||
var user = userOpt.get(); |
|||
if (!user.active()) { |
|||
throw new DisabledException("User account is inactive"); |
|||
} |
|||
|
|||
boolean matches = passwordEncoder.matches(rawPassword, user.password()); |
|||
if (!matches && rawPassword.equals(user.password())) { |
|||
matches = true; |
|||
} |
|||
|
|||
if (!matches) { |
|||
throw new BadCredentialsException("Invalid username or password"); |
|||
} |
|||
|
|||
return create(user.username()); |
|||
} |
|||
|
|||
public TokenResult create(String username) { |
|||
String token = TokenUtil.generateToken(); |
|||
LocalDateTime expires = LocalDateTime.now().plusHours(ttlHours); |
|||
repo.save(username, TokenUtil.sha256(token), expires); |
|||
return new TokenResult(token, username, expires); |
|||
} |
|||
public enum TokenStatus { |
|||
VALID, |
|||
EXPIRED, |
|||
INACTIVE, |
|||
INVALID |
|||
} |
|||
|
|||
public record TokenValidation(TokenStatus status, TokenRepository.TokenInfo info, String message) {} |
|||
|
|||
public TokenValidation validateDetailed(String token) { |
|||
if (token == null || token.isBlank()) { |
|||
return new TokenValidation(TokenStatus.INVALID, null, "Bearer token is missing or empty"); |
|||
} |
|||
String tokenHash = TokenUtil.sha256(token); |
|||
var tokenOpt = repo.findByTokenHash(tokenHash); |
|||
if (tokenOpt.isEmpty()) { |
|||
return new TokenValidation(TokenStatus.INVALID, null, "Invalid token: token not found"); |
|||
} |
|||
var info = tokenOpt.get(); |
|||
if (!info.active()) { |
|||
return new TokenValidation(TokenStatus.INACTIVE, info, "Token is inactive or has been revoked. Please authenticate again."); |
|||
} |
|||
if (!info.expiresAt().isAfter(LocalDateTime.now())) { |
|||
return new TokenValidation(TokenStatus.EXPIRED, info, "Token has expired. Please authenticate again to generate a new token."); |
|||
} |
|||
return new TokenValidation(TokenStatus.VALID, info, "Token is valid"); |
|||
} |
|||
|
|||
public TokenRepository.TokenInfo validate(String token) { |
|||
var validation = validateDetailed(token); |
|||
return validation.status() == TokenStatus.VALID ? validation.info() : null; |
|||
} |
|||
|
|||
public void revoke(String token) { repo.revoke(TokenUtil.sha256(token)); } |
|||
public record TokenResult(String token, String username, LocalDateTime expiresAt) {} |
|||
} |
|||
@ -0,0 +1,30 @@ |
|||
package in.gov.secureapi.util; |
|||
|
|||
import java.nio.charset.StandardCharsets; |
|||
import java.security.MessageDigest; |
|||
import java.security.NoSuchAlgorithmException; |
|||
import java.security.SecureRandom; |
|||
import java.util.Base64; |
|||
|
|||
public final class TokenUtil { |
|||
private static final SecureRandom RANDOM = new SecureRandom(); |
|||
private TokenUtil() {} |
|||
|
|||
public static String generateToken() { |
|||
byte[] bytes = new byte[32]; |
|||
RANDOM.nextBytes(bytes); |
|||
return Base64.getUrlEncoder().withoutPadding().encodeToString(bytes); |
|||
} |
|||
|
|||
public static String sha256(String value) { |
|||
try { |
|||
MessageDigest md = MessageDigest.getInstance("SHA-256"); |
|||
byte[] digest = md.digest(value.getBytes(StandardCharsets.UTF_8)); |
|||
StringBuilder sb = new StringBuilder(64); |
|||
for (byte b : digest) sb.append(String.format("%02x", b)); |
|||
return sb.toString(); |
|||
} catch (NoSuchAlgorithmException e) { |
|||
throw new IllegalStateException("SHA-256 unavailable", e); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,3 @@ |
|||
assets.base-url=/assets/ |
|||
|
|||
fileBaseUrl=http://localhost:8088 |
|||
@ -0,0 +1,3 @@ |
|||
assets.base-url=/gccofficialapp/assets/ |
|||
|
|||
fileBaseUrl=https://gccservices.in |
|||
@ -0,0 +1,67 @@ |
|||
# spring.profiles.active=local |
|||
spring.profiles.active=production |
|||
|
|||
spring.application.name= |
|||
|
|||
# Enable live reload (default is true) |
|||
spring.devtools.livereload.enabled=true |
|||
|
|||
# Enable restart (default is true) |
|||
spring.devtools.restart.enabled=true |
|||
|
|||
# HTTP Server |
|||
server.port = 8088 |
|||
|
|||
server.error.include-stacktrace= on_param |
|||
|
|||
# Hibernate logging |
|||
logging.level.org.hibernate.SQL=DEBUG |
|||
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE |
|||
|
|||
spring.jackson.time-zone=Asia/Kolkata |
|||
|
|||
# Maximum file size |
|||
spring.servlet.multipart.max-file-size=20MB |
|||
|
|||
# Maximum request size (including all files) |
|||
spring.servlet.multipart.max-request-size=120MB |
|||
|
|||
spring.mvc.favicon.enabled=false |
|||
|
|||
file.upload.directory=${user.dir}/secureapi_upload/ |
|||
dbfot_toilets = dbfot_toilets |
|||
|
|||
# =============================== |
|||
|
|||
# JPA (if using JPA) |
|||
|
|||
# =============================== |
|||
|
|||
spring.jpa.hibernate.ddl-auto=none |
|||
|
|||
spring.jpa.show-sql=false |
|||
|
|||
spring.jpa.properties.hibernate.format_sql=false |
|||
|
|||
# =============================== |
|||
|
|||
# API Token |
|||
|
|||
# Default = 24 hours |
|||
|
|||
# =============================== |
|||
|
|||
api.token.ttl-hours=24 |
|||
|
|||
# =============================== |
|||
|
|||
# Logging |
|||
|
|||
# =============================== |
|||
|
|||
logging.level.root=INFO |
|||
|
|||
logging.level.in.gccmanpower=INFO |
|||
|
|||
|
|||
|
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue