diff --git a/pom.xml b/pom.xml index 33b56a1..0536c6e 100644 --- a/pom.xml +++ b/pom.xml @@ -17,6 +17,59 @@ 11 + + + org.springframework.boot + spring-boot-starter-security + 2.5.4 + + + org.springframework.security + spring-security-web + + + org.springframework.security + spring-security-config + + + + + org.springframework.security.oauth.boot + spring-security-oauth2-autoconfigure + 2.5.2 + + + org.springframework.security + spring-security-web + + + org.springframework.security + spring-security-config + + + + + org.springframework.security + spring-security-web + 5.3.9.RELEASE + + + org.springframework.security + spring-security-config + 5.3.9.RELEASE + + + + io.jsonwebtoken + jjwt + 0.9.1 + + + com.nimbusds + nimbus-jose-jwt + 8.19 + + org.springframework.boot spring-boot-starter-data-jpa @@ -34,20 +87,16 @@ brave 5.16.0 - + + org.apache.velocity + velocity-engine-core + 2.3 + org.apache.commons commons-lang3 3.12.0 - - - org.apache.velocity - velocity - 1.7 - - - org.springframework.boot spring-boot-devtools diff --git a/src/main/java/com/iconplus/smartproc/configuration/CustomUserDetailsService.java b/src/main/java/com/iconplus/smartproc/configuration/CustomUserDetailsService.java new file mode 100644 index 0000000..3d6bc23 --- /dev/null +++ b/src/main/java/com/iconplus/smartproc/configuration/CustomUserDetailsService.java @@ -0,0 +1,33 @@ +package com.iconplus.smartproc.configuration; + +import com.iconplus.smartproc.model.projection.UserRoleView; +import com.iconplus.smartproc.repository.RolesRepository; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Optional; + +@Service +public class CustomUserDetailsService implements UserDetailsService { + + private final RolesRepository rolesRepository; + + public CustomUserDetailsService(RolesRepository rolesRepository) { + this.rolesRepository = rolesRepository; + } + + + @Override + public UserDetails loadUserByUsername(String username) { + List authorities = null; + Optional userRole = rolesRepository.getUserRoleByUserId(username); + if (userRole.isPresent()) { + authorities=List.of(new SimpleGrantedAuthority(userRole.get().getRole())); + } + + return new org.springframework.security.core.userdetails.User(username, username, authorities); + } +} diff --git a/src/main/java/com/iconplus/smartproc/configuration/JwtAuthenticationAccessDenied.java b/src/main/java/com/iconplus/smartproc/configuration/JwtAuthenticationAccessDenied.java new file mode 100644 index 0000000..a7d80d5 --- /dev/null +++ b/src/main/java/com/iconplus/smartproc/configuration/JwtAuthenticationAccessDenied.java @@ -0,0 +1,39 @@ +package com.iconplus.smartproc.configuration; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.iconplus.smartproc.exception.ErrorResponse; +import com.iconplus.smartproc.util.Constants; +import lombok.extern.log4j.Log4j2; +import org.springframework.http.MediaType; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.web.access.AccessDeniedHandler; +import org.springframework.stereotype.Component; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +@Component +@Log4j2 +public class JwtAuthenticationAccessDenied implements AccessDeniedHandler { + + private static final String DEFAULT_CODE = "30000"; + private static final String DEFAULT_MESSAGE = "Access denied"; + + @Override + public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException { + + log.error("Access denied, {}", e.getMessage()); + + ErrorResponse errorResponse = new ErrorResponse(); + errorResponse.setCode(DEFAULT_CODE); + errorResponse.setTitle(Constants.TITLE_INVALID_NEXT_STEP); + errorResponse.setMessage(DEFAULT_MESSAGE); + + httpServletResponse.setStatus(HttpServletResponse.SC_FORBIDDEN); + httpServletResponse.setContentType(MediaType.APPLICATION_JSON_VALUE); + + httpServletResponse.getOutputStream() + .println(new ObjectMapper().writeValueAsString(errorResponse)); + } +} diff --git a/src/main/java/com/iconplus/smartproc/configuration/JwtAuthenticationEntryPoint.java b/src/main/java/com/iconplus/smartproc/configuration/JwtAuthenticationEntryPoint.java new file mode 100644 index 0000000..e6fa58d --- /dev/null +++ b/src/main/java/com/iconplus/smartproc/configuration/JwtAuthenticationEntryPoint.java @@ -0,0 +1,54 @@ +package com.iconplus.smartproc.configuration; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.iconplus.smartproc.exception.ErrorResponse; +import com.iconplus.smartproc.util.Constants; +import org.springframework.http.MediaType; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.stereotype.Component; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + + +@Component +public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint { + + @Override + public void commence(HttpServletRequest request, HttpServletResponse response, + AuthenticationException authException) throws IOException { + + ErrorResponse errorResponse = new ErrorResponse(); + + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + response.setContentType(MediaType.APPLICATION_JSON_VALUE); + + var exception = (Exception) request.getAttribute("exception"); + + String message; + + if (exception != null) { + + if (exception.getCause() != null) { + message = exception.getCause().toString() + " " + exception.getMessage(); + } else { + message = exception.getMessage(); + } + + errorResponse.setCode(Constants.ERR_CODE_40051); + errorResponse.setTitle(Constants.TITLE_INVALID_NEXT_STEP); + errorResponse.setMessage(message); + response.getOutputStream() + .println(new ObjectMapper().writeValueAsString(errorResponse)); + } else { + errorResponse.setCode(Constants.ERR_CODE_80007); + errorResponse.setTitle(Constants.TITLE_INVALID_NEXT_STEP); + errorResponse.setMessage("Invalid Access Token"); + response.getOutputStream() + .println(new ObjectMapper().writeValueAsString(errorResponse)); + } + } + +} diff --git a/src/main/java/com/iconplus/smartproc/configuration/JwtRequestFilter.java b/src/main/java/com/iconplus/smartproc/configuration/JwtRequestFilter.java new file mode 100644 index 0000000..b04e38b --- /dev/null +++ b/src/main/java/com/iconplus/smartproc/configuration/JwtRequestFilter.java @@ -0,0 +1,79 @@ +package com.iconplus.smartproc.configuration; + +import com.iconplus.smartproc.exception.BusinessException; +import com.iconplus.smartproc.model.request.PostAccessTokenRequest; +import com.iconplus.smartproc.service.authentication.PostCheckAccessTokenService; +import io.jsonwebtoken.ExpiredJwtException; +import org.springframework.http.HttpStatus; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; +import org.springframework.web.filter.OncePerRequestFilter; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +@Component +public class JwtRequestFilter extends OncePerRequestFilter { + + private final JwtTokenUtil jwtTokenUtil; + private final PostCheckAccessTokenService postCheckAccessTokenService; + + public JwtRequestFilter(JwtTokenUtil jwtTokenUtil, + PostCheckAccessTokenService postCheckAccessTokenService) { + this.jwtTokenUtil = jwtTokenUtil; + this.postCheckAccessTokenService = postCheckAccessTokenService; + } + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) + throws ServletException, IOException { + try { + String jwtToken = extractJwtFromRequest(request); + if (StringUtils.hasText(jwtToken) && jwtTokenUtil.validateTokenOnly(jwtToken)) { + isValidToken(request, jwtToken); + UserDetails userDetails = new org.springframework.security.core.userdetails.User(jwtTokenUtil.getUsernameFromToken(jwtToken), "", + jwtTokenUtil.getRolesFromToken(jwtToken)); + + UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken( + userDetails, null, userDetails.getAuthorities()); + SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken); + } + } catch(ExpiredJwtException | BadCredentialsException ex) + { + request.setAttribute("exception", ex); + } + chain.doFilter(request, response); + } + + private void isValidToken(HttpServletRequest request, String jwtToken) { + String requestUrl = request.getRequestURI(); + String refreshTokenUrl = "/authentication-service/authentication/v1/refresh-token"; + if (!refreshTokenUrl.equals(requestUrl)) { + var isValid = isValidAuthenticateToken(jwtToken); + if (!isValid) { + throw new BusinessException(HttpStatus.UNAUTHORIZED, "Invalid Access Token"); + } + } + } + + private boolean isValidAuthenticateToken(String jwtToken) { + return postCheckAccessTokenService.execute(PostAccessTokenRequest.builder() + .accessToken(jwtToken) + .build()).getIsValid(); + } + + private String extractJwtFromRequest(HttpServletRequest request) { + String bearerToken = request.getHeader("Authorization"); + if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) { + return bearerToken.substring(7); + } + return null; + } +} \ No newline at end of file diff --git a/src/main/java/com/iconplus/smartproc/configuration/JwtTokenUtil.java b/src/main/java/com/iconplus/smartproc/configuration/JwtTokenUtil.java new file mode 100644 index 0000000..ebab745 --- /dev/null +++ b/src/main/java/com/iconplus/smartproc/configuration/JwtTokenUtil.java @@ -0,0 +1,91 @@ +package com.iconplus.smartproc.configuration; + +import com.iconplus.smartproc.model.token.TokenContent; +import io.jsonwebtoken.*; +import org.apache.commons.lang3.ObjectUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.stereotype.Component; + +import java.io.Serializable; +import java.security.KeyPair; +import java.util.*; +import java.util.function.Function; + +@Component +public class JwtTokenUtil implements Serializable { + private static final long serialVersionUID = -2550185165626007488L; + + @Autowired + private KeyPair keyPair; + + //retrieve expiration date from jwt token + public Date getExpirationDateFromToken(String token) { + return getClaimFromToken(token, Claims::getExpiration); + } + public T getClaimFromToken(String token, Function claimsResolver) { + final Claims claims = getAllClaimsFromToken(token); + return claimsResolver.apply(claims); + } + //for retrieveing any information from token we will need the secret key + public Claims getAllClaimsFromToken(String token) { + return Jwts.parser().setSigningKey(keyPair.getPublic()).parseClaimsJws(token).getBody(); + } + + private Boolean isTokenExpired(String token) { + final Date expiration = getExpirationDateFromToken(token); + return expiration.before(new Date()); + } + + public String generateToken(String username, TokenContent tokenContent, Integer expirationInMs, String channel, String scopeType) { + Map claims = new HashMap<>(); + + claims.put("authorities", tokenContent.getAccessMenu()); + claims.put("fullname", tokenContent.getFullname()); + claims.put("username", tokenContent.getUsername()); + claims.put("user_id", tokenContent.getUserId()); + claims.put("role", tokenContent.getRole()); + + return doGenerateToken(claims, username, expirationInMs); + } + + public String doGenerateToken(Map claims, String subject, Integer expirationInMs) { + return Jwts.builder().setClaims(claims).setSubject(subject).setIssuedAt(new Date(System.currentTimeMillis())) + .setExpiration(new Date(System.currentTimeMillis() + expirationInMs)) + .signWith(SignatureAlgorithm.RS256, keyPair.getPrivate()).compact(); + } + + public Boolean validateToken(String token, UserDetails userDetails) { + final String username = getUsernameFromToken(token); + return (username.equals(userDetails.getUsername()) && !isTokenExpired(token)); + } + + public boolean validateTokenOnly(String authToken) { + try { + Jwts.parser().setSigningKey(keyPair.getPublic()).parseClaimsJws(authToken); + return true; + } catch (SignatureException | MalformedJwtException | UnsupportedJwtException | IllegalArgumentException ex) { + throw new BadCredentialsException("INVALID_CREDENTIALS", ex); + } catch (ExpiredJwtException ex) { + throw ex; + } + } + + public String getUsernameFromToken(String token) { + Claims claims = Jwts.parser().setSigningKey(keyPair.getPublic()).parseClaimsJws(token).getBody(); + return claims.getSubject(); + + } + + @SuppressWarnings("squid:S4834") + public List getRolesFromToken(String token) { + Claims claims = Jwts.parser().setSigningKey(keyPair.getPublic()).parseClaimsJws(token).getBody(); + List authorityList = new ArrayList<>(); + ArrayList authorities = claims.get("authorities", ArrayList.class); + authorities.forEach(c-> authorityList.add(new SimpleGrantedAuthority(c))); + return authorityList; + + } +} \ No newline at end of file diff --git a/src/main/java/com/iconplus/smartproc/configuration/KeyConfiguration.java b/src/main/java/com/iconplus/smartproc/configuration/KeyConfiguration.java new file mode 100644 index 0000000..6077f9c --- /dev/null +++ b/src/main/java/com/iconplus/smartproc/configuration/KeyConfiguration.java @@ -0,0 +1,30 @@ +package com.iconplus.smartproc.configuration; + +import com.iconplus.smartproc.util.RSAUtil; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.security.KeyPair; +import java.security.PrivateKey; +import java.security.PublicKey; + +@Configuration +public class KeyConfiguration { + + @Value("${jwt.private-key}") + private String privateKey; + + @Value("${jwt.public-key}") + private String publicKey; + + @Bean + public KeyPair keypairBean() { + + PublicKey pubKey = RSAUtil.getPublicKey(publicKey); + PrivateKey privKey = RSAUtil.getPrivateKey(privateKey); + + return new KeyPair(pubKey, privKey); + } + +} diff --git a/src/main/java/com/iconplus/smartproc/configuration/WebSecurityConfig.java b/src/main/java/com/iconplus/smartproc/configuration/WebSecurityConfig.java new file mode 100644 index 0000000..9c6274a --- /dev/null +++ b/src/main/java/com/iconplus/smartproc/configuration/WebSecurityConfig.java @@ -0,0 +1,57 @@ +package com.iconplus.smartproc.configuration; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.builders.WebSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; + + +@Configuration +@EnableWebSecurity +@EnableGlobalMethodSecurity(prePostEnabled = true) +public class WebSecurityConfig extends WebSecurityConfigurerAdapter { + + @Autowired + private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint; + + @Autowired + private JwtAuthenticationAccessDenied jwtAuthenticationAccessDenied; + + @Autowired + private JwtRequestFilter jwtRequestFilter; + + @Override + protected void configure(HttpSecurity httpSecurity) throws Exception { + httpSecurity + .sessionManagement() + .sessionCreationPolicy(SessionCreationPolicy.STATELESS) + .sessionFixation().none().and() + .csrf().disable(); + httpSecurity.authorizeRequests() + .antMatchers("/api*/**").permitAll() + .antMatchers("/actuator/health").permitAll() + .antMatchers("/swagger*/**").permitAll() + .antMatchers("/v2*/**").permitAll() + .antMatchers("/token/jwks.json").permitAll() + .anyRequest().authenticated().and() + .exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint) + .accessDeniedHandler(jwtAuthenticationAccessDenied); + httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class); + + } + + @Override + public void configure(WebSecurity web) throws Exception { + web.ignoring().antMatchers("/v2/api-docs", + "/configuration/ui", + "/swagger-resources/**", + "/configuration/security", + "/swagger-ui.html", + "/webjars/**"); + } +} \ No newline at end of file diff --git a/src/main/java/com/iconplus/smartproc/controller/UsersController.java b/src/main/java/com/iconplus/smartproc/controller/UsersController.java index 9a87b64..73758c8 100644 --- a/src/main/java/com/iconplus/smartproc/controller/UsersController.java +++ b/src/main/java/com/iconplus/smartproc/controller/UsersController.java @@ -45,7 +45,7 @@ public class UsersController { .orElseThrow(() -> new ResourceNotFoundException("Users not exist with id :" + id)); users.setUsername(usersDetails.getUsername()); - users.setNama(usersDetails.getNama()); + users.setFullname(usersDetails.getFullname()); users.setEmail(usersDetails.getEmail()); users.setPassword(usersDetails.getPassword()); // users.setInstansi(usersDetails.getInstansi()); diff --git a/src/main/java/com/iconplus/smartproc/exception/ErrorDetails.java b/src/main/java/com/iconplus/smartproc/exception/ErrorDetails.java deleted file mode 100644 index a4ac27c..0000000 --- a/src/main/java/com/iconplus/smartproc/exception/ErrorDetails.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.iconplus.smartproc.exception; - -import java.util.Date; - -public class ErrorDetails { - private Date timestamp; - private String message; - private String details; - - public ErrorDetails(Date timestamp, String message, String details) { - super(); - this.timestamp = timestamp; - this.message = message; - this.details = details; - } - - public Date getTimestamp() { - return timestamp; - } - - public String getMessage() { - return message; - } - - public String getDetails() { - return details; - } -} diff --git a/src/main/java/com/iconplus/smartproc/exception/ErrorHelper.java b/src/main/java/com/iconplus/smartproc/exception/ErrorHelper.java index afdc490..012e13e 100644 --- a/src/main/java/com/iconplus/smartproc/exception/ErrorHelper.java +++ b/src/main/java/com/iconplus/smartproc/exception/ErrorHelper.java @@ -5,6 +5,7 @@ import org.apache.velocity.Template; import org.apache.velocity.VelocityContext; import org.apache.velocity.app.Velocity; import org.apache.velocity.app.VelocityEngine; +import org.apache.velocity.context.Context; import org.apache.velocity.runtime.resource.loader.StringResourceLoader; import org.apache.velocity.runtime.resource.util.StringResourceRepository; import org.springframework.context.annotation.Configuration; @@ -88,7 +89,7 @@ public class ErrorHelper { .getApplicationAttribute(StringResourceLoader.REPOSITORY_NAME_DEFAULT); resourceRepository.putStringResource(RAW_TEMPLATE, rawNotificationTemplate); - return new VelocityContext(parameters); + return new VelocityContext((Context) parameters); } private void addVelocityProperties() { diff --git a/src/main/java/com/iconplus/smartproc/helper/service/TokenUtils.java b/src/main/java/com/iconplus/smartproc/helper/service/TokenUtils.java new file mode 100644 index 0000000..4256cb4 --- /dev/null +++ b/src/main/java/com/iconplus/smartproc/helper/service/TokenUtils.java @@ -0,0 +1,53 @@ +package com.iconplus.smartproc.helper.service; + +import com.nimbusds.jose.JWSObject; +import com.nimbusds.jose.Payload; +import lombok.extern.log4j.Log4j2; +import net.minidev.json.JSONObject; +import org.apache.commons.lang3.StringUtils; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +@Log4j2 +public class TokenUtils { + + private TokenUtils() { + + } + + public static Map decodeToken(String jwtToken) { + Map body = new HashMap<>(); + + try { + if (jwtToken != null) { + jwtToken = jwtToken.replace("Bearer", ""); + jwtToken = jwtToken.trim(); + } + if (StringUtils.isBlank(jwtToken)) { + return body; + } + JWSObject token = JWSObject.parse(jwtToken); + + Payload tokenPayload = token.getPayload(); + JSONObject tokenBody = tokenPayload.toJSONObject(); + + tokenBody.forEach((key, value) -> { + if (Objects.isNull(value)) { + value = ""; + } + + body.put(key, value.toString()); + }); + } catch (Exception e) { + log.error("Failed to parse JWT Token. Error: {}", e.getMessage()); + } + + return body; + } + + public static Object getValueByParam(String param, String token) { + return decodeToken(token).get(param); + } +} \ No newline at end of file diff --git a/src/main/java/com/iconplus/smartproc/model/entity/Menu.java b/src/main/java/com/iconplus/smartproc/model/entity/Menu.java new file mode 100644 index 0000000..d37879f --- /dev/null +++ b/src/main/java/com/iconplus/smartproc/model/entity/Menu.java @@ -0,0 +1,52 @@ +package com.iconplus.smartproc.model.entity; + +import com.iconplus.smartproc.helper.base.BaseEntity; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.hibernate.annotations.GeneratorType; +import org.hibernate.annotations.Type; + +import javax.persistence.*; + + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Entity +@Table(name = "permission") +public class Menu extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private Long id; + + @Column(name = "subMenu") + private String subMenu; + + @Column(name = "menu") + private String menu; + +// @Column(name = "can_view") +// @Type(type = "org.hibernate.type.NumericBooleanType") +// private Boolean canView; +// +// @Column(name = "can_read") +// @Type(type = "org.hibernate.type.NumericBooleanType") +// private Boolean canRead; +// +// @Column(name = "can_create") +// @Type(type = "org.hibernate.type.NumericBooleanType") +// private Boolean canCreate; +// +// @Column(name = "can_delete") +// @Type(type = "org.hibernate.type.NumericBooleanType") +// private Boolean canDelete; + + @Column(name = "deleted") + @Type(type = "org.hibernate.type.NumericBooleanType") + private Boolean deleted; +} diff --git a/src/main/java/com/iconplus/smartproc/model/entity/Permission.java b/src/main/java/com/iconplus/smartproc/model/entity/Permission.java new file mode 100644 index 0000000..9d54f1a --- /dev/null +++ b/src/main/java/com/iconplus/smartproc/model/entity/Permission.java @@ -0,0 +1,41 @@ +package com.iconplus.smartproc.model.entity; + +import com.iconplus.smartproc.helper.base.BaseEntity; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.hibernate.annotations.GenericGenerator; +import org.hibernate.annotations.Type; + +import javax.persistence.Column; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.Table; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Table(name = "permission") +public class Permission extends BaseEntity { + + @Id + @GeneratedValue(generator = "UUID") + @GenericGenerator( + name = "UUID", + strategy = "org.hibernate.id.UUIDGenerator" + ) + @Column(name = "id") + private String id; + + @Column(name = "role_id") + private Long roleId; + + @Column(name = "role_id") + private Long menuId; + + @Column(name = "deleted") + @Type(type = "org.hibernate.type.NumericBooleanType") + private Boolean deleted; +} diff --git a/src/main/java/com/iconplus/smartproc/model/entity/TokenManagement.java b/src/main/java/com/iconplus/smartproc/model/entity/TokenManagement.java new file mode 100644 index 0000000..10c7b65 --- /dev/null +++ b/src/main/java/com/iconplus/smartproc/model/entity/TokenManagement.java @@ -0,0 +1,51 @@ +package com.iconplus.smartproc.model.entity; + +import com.iconplus.smartproc.helper.base.BaseEntity; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.hibernate.annotations.GenericGenerator; +import org.hibernate.annotations.Type; + +import javax.persistence.*; +import java.sql.Timestamp; + +@Data +@Builder +@Entity +@AllArgsConstructor +@NoArgsConstructor +@Table(name = "token_management") +public class TokenManagement extends BaseEntity { + + @Id + @GeneratedValue(generator = "UUID") + @GenericGenerator( + name = "UUID", + strategy = "org.hibernate.id.UUIDGenerator" + ) + @Column(name = "id") + private String id; + + @Column(name = "user_id") + private String userId; + + @Lob + @Column(name = "access_token") + private String accessToken; + + @Lob + @Column(name = "refresh_token") + private String refreshToken; + + @Column(name = "issued_time") + private Timestamp issuedTime; + + @Column(name = "expired_time") + private Timestamp expiredTime; + + @Column(name = "deleted") + @Type(type = "org.hibernate.type.NumericBooleanType") + private Boolean deleted; +} diff --git a/src/main/java/com/iconplus/smartproc/model/entity/Users.java b/src/main/java/com/iconplus/smartproc/model/entity/Users.java index f9bcffa..1e5ab59 100644 --- a/src/main/java/com/iconplus/smartproc/model/entity/Users.java +++ b/src/main/java/com/iconplus/smartproc/model/entity/Users.java @@ -20,8 +20,8 @@ public class Users extends BaseEntity { @Column(name = "username") private String username; - @Column(name = "nama") - private String nama; + @Column(name = "fullname") + private String fullname; @Column(name = "email") private String email; diff --git a/src/main/java/com/iconplus/smartproc/model/projection/TokenManagementView.java b/src/main/java/com/iconplus/smartproc/model/projection/TokenManagementView.java new file mode 100644 index 0000000..9e651d8 --- /dev/null +++ b/src/main/java/com/iconplus/smartproc/model/projection/TokenManagementView.java @@ -0,0 +1,16 @@ +package com.iconplus.smartproc.model.projection; + +import java.sql.Clob; + +public interface TokenManagementView { + + String getId(); + void setId(String id); + + String getUserId(); + void setUserId(String userId); + + Clob getAccessToken(); + void setAccessToken(Clob accessToken); + +} diff --git a/src/main/java/com/iconplus/smartproc/model/projection/UserRoleView.java b/src/main/java/com/iconplus/smartproc/model/projection/UserRoleView.java new file mode 100644 index 0000000..284aa4d --- /dev/null +++ b/src/main/java/com/iconplus/smartproc/model/projection/UserRoleView.java @@ -0,0 +1,19 @@ +package com.iconplus.smartproc.model.projection; + +public interface UserRoleView { + + Long getId(); + String getUsername(); + String getPassword(); + Long getRoleId(); + String getRole(); + Boolean getDeleted(); + + void setId(Long id); + void setUsername(String username); + void setPassword(String password); + void setRoleId(Long roleId); + void setRole(String role); + void setDeleted(Boolean deleted); + +} diff --git a/src/main/java/com/iconplus/smartproc/model/request/LoginRequest.java b/src/main/java/com/iconplus/smartproc/model/request/LoginRequest.java new file mode 100644 index 0000000..c33198e --- /dev/null +++ b/src/main/java/com/iconplus/smartproc/model/request/LoginRequest.java @@ -0,0 +1,17 @@ +package com.iconplus.smartproc.model.request; + +import com.iconplus.smartproc.helper.base.BaseRequest; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class LoginRequest extends BaseRequest { + + private String email; + private String password; +} diff --git a/src/main/java/com/iconplus/smartproc/model/request/PostAccessTokenRequest.java b/src/main/java/com/iconplus/smartproc/model/request/PostAccessTokenRequest.java new file mode 100644 index 0000000..e426cf0 --- /dev/null +++ b/src/main/java/com/iconplus/smartproc/model/request/PostAccessTokenRequest.java @@ -0,0 +1,16 @@ +package com.iconplus.smartproc.model.request; + +import com.iconplus.smartproc.helper.base.BaseRequest; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class PostAccessTokenRequest extends BaseRequest { + + private String accessToken; +} \ No newline at end of file diff --git a/src/main/java/com/iconplus/smartproc/model/response/LoginResponse.java b/src/main/java/com/iconplus/smartproc/model/response/LoginResponse.java new file mode 100644 index 0000000..675e95b --- /dev/null +++ b/src/main/java/com/iconplus/smartproc/model/response/LoginResponse.java @@ -0,0 +1,26 @@ +package com.iconplus.smartproc.model.response; + +import com.iconplus.smartproc.helper.base.BaseResponse; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Set; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class LoginResponse extends BaseResponse { + + private String accessToken; + private String validity; + private String refreshToken; + private String username; + private String email; + private Long roleId; + private String role; + Set accessMenu; + +} diff --git a/src/main/java/com/iconplus/smartproc/model/response/PostAccessTokenResponse.java b/src/main/java/com/iconplus/smartproc/model/response/PostAccessTokenResponse.java new file mode 100644 index 0000000..a9c4f88 --- /dev/null +++ b/src/main/java/com/iconplus/smartproc/model/response/PostAccessTokenResponse.java @@ -0,0 +1,16 @@ +package com.iconplus.smartproc.model.response; + +import com.iconplus.smartproc.helper.base.BaseResponse; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class PostAccessTokenResponse extends BaseResponse { + + private Boolean isValid; +} \ No newline at end of file diff --git a/src/main/java/com/iconplus/smartproc/model/token/TokenContent.java b/src/main/java/com/iconplus/smartproc/model/token/TokenContent.java new file mode 100644 index 0000000..28907de --- /dev/null +++ b/src/main/java/com/iconplus/smartproc/model/token/TokenContent.java @@ -0,0 +1,16 @@ +package com.iconplus.smartproc.model.token; + +import lombok.Builder; +import lombok.Data; + +import java.util.Set; + +@Builder +@Data +public class TokenContent { + private String userId; + private String role; + private Set accessMenu; + private String fullname; + private String username; +} diff --git a/src/main/java/com/iconplus/smartproc/repository/RolesRepository.java b/src/main/java/com/iconplus/smartproc/repository/RolesRepository.java index 2abc8e5..9b55ea3 100644 --- a/src/main/java/com/iconplus/smartproc/repository/RolesRepository.java +++ b/src/main/java/com/iconplus/smartproc/repository/RolesRepository.java @@ -1,10 +1,26 @@ package com.iconplus.smartproc.repository; import com.iconplus.smartproc.model.entity.Roles; +import com.iconplus.smartproc.model.projection.UserRoleView; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; import org.springframework.stereotype.Repository; +import java.util.Optional; + @Repository public interface RolesRepository extends JpaRepository { + + @Query(value = "SELECT u.id as id, " + + "u.username as username, " + + "u.password as password, " + + "u.roleId as roleId, " + + "r.role as role " + + "FROM Users u " + + "join Roles r on r.id=u.roleId " + + "WHERE u.deleted = false " + + "AND r.deleted = false " + + "AND u.username = :userName ") + Optional getUserRoleByUserId(String userName); } diff --git a/src/main/java/com/iconplus/smartproc/repository/TokenManagementRepository.java b/src/main/java/com/iconplus/smartproc/repository/TokenManagementRepository.java new file mode 100644 index 0000000..9809026 --- /dev/null +++ b/src/main/java/com/iconplus/smartproc/repository/TokenManagementRepository.java @@ -0,0 +1,19 @@ +package com.iconplus.smartproc.repository; + +import com.iconplus.smartproc.model.entity.TokenManagement; +import com.iconplus.smartproc.model.projection.TokenManagementView; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface TokenManagementRepository extends JpaRepository { + + @Query(value = "SELECT access_token as accessToken FROM token_management " + + "WHERE user_id = :userId " + + "AND deleted = false " + + "AND expired_time >= SYSDATE", nativeQuery = true) + List findAccessTokenByUserIdAndDeletedFalse(String userId); +} diff --git a/src/main/java/com/iconplus/smartproc/repository/UsersRepository.java b/src/main/java/com/iconplus/smartproc/repository/UsersRepository.java index aa25de3..e15d932 100644 --- a/src/main/java/com/iconplus/smartproc/repository/UsersRepository.java +++ b/src/main/java/com/iconplus/smartproc/repository/UsersRepository.java @@ -4,7 +4,11 @@ import com.iconplus.smartproc.model.entity.Users; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; +import java.util.Optional; + @Repository public interface UsersRepository extends JpaRepository { + + Optional findByEmailAndDeletedFalse(String email); } diff --git a/src/main/java/com/iconplus/smartproc/service/authentication/LoginService.java b/src/main/java/com/iconplus/smartproc/service/authentication/LoginService.java new file mode 100644 index 0000000..e824182 --- /dev/null +++ b/src/main/java/com/iconplus/smartproc/service/authentication/LoginService.java @@ -0,0 +1,47 @@ +package com.iconplus.smartproc.service.authentication; + +import com.iconplus.smartproc.exception.BusinessException; +import com.iconplus.smartproc.helper.service.BaseService; +import com.iconplus.smartproc.model.request.LoginRequest; +import com.iconplus.smartproc.model.response.LoginResponse; +import com.iconplus.smartproc.repository.UsersRepository; +import com.iconplus.smartproc.util.Constants; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Service; + +@Service +@Slf4j +public class LoginService implements BaseService { + + + private final UsersRepository usersRepository; + + public LoginService(UsersRepository userRepository) { + this.usersRepository = userRepository; + + } + + @Override + public LoginResponse execute(LoginRequest input) { + + var user= usersRepository.findByEmailAndDeletedFalse(input.getEmail()) + .orElseThrow(() -> new BusinessException(HttpStatus.CONFLICT, + Constants.ERR_CODE_10003, + Constants.ERR_TTL_10003, + String.format(Constants.ERR_MSG_10003, input.getEmail()))); + + if (!StringUtils.equalsIgnoreCase(input.getPassword(), user.getPassword())) { + throw new BusinessException(HttpStatus.CONFLICT, + Constants.ERR_CODE_10004, + Constants.ERR_TTL_10004, + Constants.ERR_MSG_10004); + } + + + + return LoginResponse.builder() + .build(); + } +} diff --git a/src/main/java/com/iconplus/smartproc/service/authentication/PostCheckAccessTokenService.java b/src/main/java/com/iconplus/smartproc/service/authentication/PostCheckAccessTokenService.java new file mode 100644 index 0000000..a77aa51 --- /dev/null +++ b/src/main/java/com/iconplus/smartproc/service/authentication/PostCheckAccessTokenService.java @@ -0,0 +1,55 @@ +package com.iconplus.smartproc.service.authentication; + +import com.iconplus.smartproc.helper.service.BaseService; +import com.iconplus.smartproc.helper.service.TokenUtils; +import com.iconplus.smartproc.model.projection.TokenManagementView; +import com.iconplus.smartproc.model.request.PostAccessTokenRequest; +import com.iconplus.smartproc.model.response.PostAccessTokenResponse; +import com.iconplus.smartproc.repository.TokenManagementRepository; +import com.iconplus.smartproc.util.CommonUtil; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.stream.Collectors; + +@Service +@Slf4j +public class PostCheckAccessTokenService implements BaseService { + + private TokenManagementRepository tokenManagementRepository; + + public PostCheckAccessTokenService(TokenManagementRepository tokenManagementRepository) { + this.tokenManagementRepository = tokenManagementRepository; + } + + @Override + public PostAccessTokenResponse execute(PostAccessTokenRequest input) { + + var decodeToken = TokenUtils.decodeToken(input.getAccessToken()); + String userId = decodeToken.get("user_id"); + + List tokenManagementViews = tokenManagementRepository.findAccessTokenByUserIdAndDeletedFalse(userId); + if (tokenManagementViews.isEmpty()) { + log.error("access token not found in db"); + return PostAccessTokenResponse.builder() + .isValid(false) + .build(); + } + + List userTokenList = tokenManagementViews.stream() + .map(c-> CommonUtil.clobToString(c.getAccessToken())) + .collect(Collectors.toList()); + + boolean isMatch = userTokenList.stream().anyMatch(s -> s.equals(input.getAccessToken())); + if (isMatch) { + return PostAccessTokenResponse.builder() + .isValid(true) + .build(); + } + + return PostAccessTokenResponse.builder() + .isValid(false) + .build(); + } +} diff --git a/src/main/java/com/iconplus/smartproc/util/CommonUtil.java b/src/main/java/com/iconplus/smartproc/util/CommonUtil.java new file mode 100644 index 0000000..617d5be --- /dev/null +++ b/src/main/java/com/iconplus/smartproc/util/CommonUtil.java @@ -0,0 +1,25 @@ +package com.iconplus.smartproc.util; + +import com.iconplus.smartproc.exception.TechnicalException; + +import java.sql.Clob; +import java.sql.SQLException; + +public class CommonUtil { + private CommonUtil() { + } + + public static String clobToString(Clob input) { + try { + if (input != null) { + return input.getSubString(1, (int) input.length()); + } else { + return null; + } + } catch (SQLException exception) { + throw new TechnicalException(Constants.ERR_TTL_40041, + Constants.ERR_TTL_40041, + Constants.ERR_MSG_40041); + } + } +} diff --git a/src/main/java/com/iconplus/smartproc/util/Constants.java b/src/main/java/com/iconplus/smartproc/util/Constants.java index 5a72c7a..9273684 100644 --- a/src/main/java/com/iconplus/smartproc/util/Constants.java +++ b/src/main/java/com/iconplus/smartproc/util/Constants.java @@ -12,4 +12,21 @@ public class Constants { public static final String ERR_TTL_10002 = "Data tersedia"; public static final String ERR_MSG_10002 = "Jenis Anggaran dengan id : %s sudah tersedia"; + public static final String ERR_CODE_10003 = "10003"; + public static final String ERR_TTL_10003 = "Data tidak tersedia"; + public static final String ERR_MSG_10003 = "User dengan email : %s tidak ditemukan"; + + public static final String ERR_CODE_10004 = "10004"; + public static final String ERR_TTL_10004 = "Gagal Authentikasi User"; + public static final String ERR_MSG_10004 = "Silahkan Periksa kembali Email dan Password Anda"; + + public static final String ERR_CODE_40041 = "40041"; + public static final String ERR_TTL_40041 = "Terjadi Gangguan"; + public static final String ERR_MSG_40041 = "Masalah Koneksi System"; + + public static final String ERR_CODE_40051 = "40051"; + public static final String ERR_CODE_80007 = "80007"; + public static final String TITLE_INVALID_NEXT_STEP = "Proses tidak dapat dilanjutkan"; + + } diff --git a/src/main/java/com/iconplus/smartproc/util/RSAUtil.java b/src/main/java/com/iconplus/smartproc/util/RSAUtil.java new file mode 100644 index 0000000..7d182e2 --- /dev/null +++ b/src/main/java/com/iconplus/smartproc/util/RSAUtil.java @@ -0,0 +1,109 @@ +package com.iconplus.smartproc.util; + +import com.iconplus.smartproc.exception.TechnicalException; +import lombok.extern.log4j.Log4j2; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import java.security.*; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.Base64; + +@Log4j2 +public class RSAUtil { + + private RSAUtil(){ + + } + + public static PrivateKey getPrivateKey(String base64PrivateKey){ + PrivateKey privateKey = null; + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(base64PrivateKey.getBytes())); + try { + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + privateKey = keyFactory.generatePrivate(keySpec); + } catch (InvalidKeySpecException e) { + log.error("InvalidKeySpecException : "+e.getMessage()); + throw new TechnicalException("80000", "InvalidKeySpecException : "+e.getMessage()); + } catch (NoSuchAlgorithmException e) { + log.error("NoSuchAlgorithmException : "+e.getMessage()); + throw new TechnicalException("80000", "NoSuchAlgorithmException : "+e.getMessage()); + } + return privateKey; + } + + public static PublicKey getPublicKey(String base64PublicKey){ + PublicKey publicKey = null; + X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(base64PublicKey.getBytes())); + try { + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + publicKey = keyFactory.generatePublic(keySpec); + } catch (InvalidKeySpecException e) { + log.error("InvalidKeySpecException : "+e.getMessage()); + throw new TechnicalException("80000", "InvalidKeySpecException : "+e.getMessage()); + } catch (NoSuchAlgorithmException e) { + log.error("NoSuchAlgorithmException : "+e.getMessage()); + throw new TechnicalException("80000", "NoSuchAlgorithmException : "+e.getMessage()); + } + return publicKey; + } + + private static String decryptChipper(byte[] data, PrivateKey privateKey) { + try { + Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPPadding"); + cipher.init(Cipher.DECRYPT_MODE, privateKey); + return new String(cipher.doFinal(data)); + } catch (NoSuchAlgorithmException e) { + log.error("NoSuchAlgorithmException : "+e.getMessage()); + throw new TechnicalException("80000", "NoSuchAlgorithmException : "+e.getMessage()); + } catch (NoSuchPaddingException e) { + log.error("NoSuchPaddingException : "+e.getMessage()); + throw new TechnicalException("80000", "NoSuchPaddingException : "+e.getMessage()); + } catch (InvalidKeyException e) { + log.error("InvalidKeyException : "+e.getMessage()); + throw new TechnicalException("80000", "InvalidKeyException : "+e.getMessage()); + } catch (IllegalBlockSizeException e) { + log.error("IllegalBlockSizeException : "+e.getMessage()); + throw new TechnicalException("80000", "IllegalBlockSizeException : "+e.getMessage()); + } catch (BadPaddingException e) { + log.error("BadPaddingException : "+e.getMessage()); + throw new TechnicalException("80000", "BadPaddingException : "+e.getMessage()); + } + } + + private static byte[] encryptChipper(byte[] data, PublicKey publicKey) { + try { + Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPPadding"); + cipher.init(Cipher.ENCRYPT_MODE, publicKey); + return cipher.doFinal(data); + } catch (NoSuchAlgorithmException e) { + log.error("NoSuchAlgorithmException : "+e.getMessage()); + throw new TechnicalException("80000", "NoSuchAlgorithmException : "+e.getMessage()); + } catch (NoSuchPaddingException e) { + log.error("NoSuchPaddingException : "+e.getMessage()); + throw new TechnicalException("80000", "NoSuchPaddingException : "+e.getMessage()); + } catch (InvalidKeyException e) { + log.error("InvalidKeyException : "+e.getMessage()); + throw new TechnicalException("80000", "InvalidKeyException : "+e.getMessage()); + } catch (IllegalBlockSizeException e) { + log.error("IllegalBlockSizeException : "+e.getMessage()); + throw new TechnicalException("80000", "IllegalBlockSizeException : "+e.getMessage()); + } catch (BadPaddingException e) { + log.error("BadPaddingException : "+e.getMessage()); + throw new TechnicalException("80000", "BadPaddingException : "+e.getMessage()); + } + } + + public static String decrypt(String data, String base64PrivateKey) { + return decryptChipper(Base64.getDecoder().decode(data.getBytes()), getPrivateKey(base64PrivateKey)); + } + + public static String encrypt(String data, String base64PublicKey) { + return Base64.getEncoder().encodeToString(encryptChipper(data.getBytes(), getPublicKey(base64PublicKey))); + } + +} diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yml new file mode 100644 index 0000000..60a202b --- /dev/null +++ b/src/main/resources/application-local.yml @@ -0,0 +1,22 @@ +#Configuration for database +jwt: + public-key: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx3MRA7zxvaWKrtmPl2hRJLFiyryvj0ZUlmWw9OZIgqwJUDBTsg5yFX4hCQrANV1yy5ibTqAn2APdNCdhGgp8R2YLWrUR2vVGbmnKXXzEDsFpT6cgo+/a+lWaUq8aYEhzVg8Xjmy9oG1s521LklEz/jKD2xNE4OgY2Y1SgfUH+bexs84ZEyUhcSpCrcZenjCns7Ubp9zsWPrXa/j6kr7ZuFWH7nXN/i+oYF0HFhc+hDaVr2R9Q7s56wAQNE8XfI2Q+h4iRI1hWa5Vva9ha1DnCN9McJDLBHoGy1coIUEoQKkDhCNQmdHlubIJYDoFIyfDTrSQIXw2gzrrC9sOgYU64wIDAQAB + private-key: MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDHcxEDvPG9pYqu2Y+XaFEksWLKvK+PRlSWZbD05kiCrAlQMFOyDnIVfiEJCsA1XXLLmJtOoCfYA900J2EaCnxHZgtatRHa9UZuacpdfMQOwWlPpyCj79r6VZpSrxpgSHNWDxeObL2gbWznbUuSUTP+MoPbE0Tg6BjZjVKB9Qf5t7GzzhkTJSFxKkKtxl6eMKeztRun3OxY+tdr+PqSvtm4VYfudc3+L6hgXQcWFz6ENpWvZH1DuznrABA0Txd8jZD6HiJEjWFZrlW9r2FrUOcI30xwkMsEegbLVyghQShAqQOEI1CZ0eW5sglgOgUjJ8NOtJAhfDaDOusL2w6BhTrjAgMBAAECggEAL8SPPqXx/8m1/Up96XVCeddCIewu39GoUJzqVL0SgLlTQbFRWkO7bwpWPyQkBKPs6nYSPDJ/WG0UfXkw+FuqiC8YaREUxawYwjj0Do/jJOWNo1YTqy+28l2uKD9nwceYV/QlYCcLA3Exs/upLdHk8eyHR+DFjlgIG5KNQDK/Rj8cLdQGJjAZjHUGzaRq/HhDi+0LVkOoUvzbzle8RSiiZnNrxCt0UV1CambQzvyxXzqLgtGe4s+qItwk/zzkvXDRnRju3+/AyaFaToA2Wnp9gdV2rT4cRGHhj0Dqh0vwrBH4RiPtEfLQjIa9DhTPHfpr6ACGe6GF96fJ889WIEl4gQKBgQDrGgYKGwIhY9j2V7tu6eHmLxYFGzxSPOtfBZPzSLUyQXrcd6wFlb+Fsae2bgBFZJzilIv4lG0wNaGSFjAjteyXMoqVCTIUKJCjzWs+g3qjuA5fu9IAeWEEaXuKSVvkV5tJrnwhmqYdH1jQx5xhpO434W2a4wuEgOnU6SzNjvS/TwKBgQDZLb3Q9KRwlMcyt/rgjeTOf2SD1jvlKD2t+YRuaxlzBs7j99hCEP3GLaTz6/rIptfkutwk10FM/piO5PCyoPXNc2A1Z73ukmsirMRy2G06PfqKBQrV/B9vtIqPW1Agz7KUpXARrHL8Bb6T2ljKl2HU+kC4Pgx2GZQgYnbEvNBGLQKBgQCNf3grFN5PYlzuxxbURofjlmtWX3IKvQechSrqvdPwj2B22L/8DIjc2nPqZIJdQZT6+hTUY+DjpyO7XQdUNuZSieTGlmZBo7iKHdRyJ4fkiZ59F/notyUhFqt4K3bXE7MuwYmMYBuiY399COaiDjYiA9eH5SbFdSFHN5/ziBreGQKBgGZHNG2DyS1745PvMScvq+HT/PJZojt5iBK2v7eAmKujOSwDPMVgWyNJu7VkHOcCLAp8NdDjzs0D2bTx/KkjRJ9NBrIf+UKxkeLymlG7uzUCm0sEtOWxptxkmhyJVGMfbWqzvuOT41LtIaNf4REH2fsDIBekoRm9UhUuSeC9SxjBAoGAP6pdT7HlMsr++XJFqs9+dj7tRsX+a6Coi3T8BFaf2Nl9YxMFFaSBbNOO4QfZWyCUcfbkLrcdEka5VTVt/HwckDKzckxWqH3hMn2+kAKJnHhAvB1M38KAyvdpA54vNgVyID+vL5VbqpHk8wsUbVWZc8F/ERELfuHwzcD0rt6Opnc= + expired-time : + service-account: + access-token: 480 + refresh-token: 720 + +spring: + datasource: + driver-class-name: org.postgresql.Driver + url: jdbc:postgresql://localhost:5432/smartproc + username: postgres + password: postgre + jackson: + default-property-inclusion: NON_NULL + jpa: + hibernate: + ddl-auto: update + database-platform: org.hibernate.dialect.PostgreSQLDialect + show-sql: true diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties deleted file mode 100644 index dd5e35d..0000000 --- a/src/main/resources/application.properties +++ /dev/null @@ -1,12 +0,0 @@ -spring.datasource.url=jdbc:postgresql://localhost:5432/smartproc -spring.datasource.username=postgres -spring.datasource.password=postgre -spring.jpa.show-sql=true - -## Hibernate Properties -# The SQL dialect makes Hibernate generate better SQL for the chosen database -spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect - -# Hibernate ddl auto (create, create-drop, validate, update) -spring.jpa.hibernate.ddl-auto = update -server.port=9090 \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 0000000..b14266e --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,7 @@ +spring: + application: + name: "smartproc-service" + profiles: + active: local +server: + port: 9090 \ No newline at end of file diff --git a/src/main/resources/bootstrap.properties b/src/main/resources/bootstrap.properties new file mode 100644 index 0000000..68d2e92 --- /dev/null +++ b/src/main/resources/bootstrap.properties @@ -0,0 +1,5 @@ +spring.application.name=smartproc +spring.profiles.active=@spring.profiles.active@ +management.endpoints.web.exposure.include=* +management.endpoint.health.show-details=ALWAYS +management.metrics.tags.application=${spring.application.name} \ No newline at end of file