Commit 050d737c authored by Ghitha Dinan's avatar Ghitha Dinan

add send email

parent c76b181c
Pipeline #405 failed with stages
package id.go.kemenag.madrasah.pmrms.notif.config
import org.springframework.context.annotation.Configuration
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
@Configuration
class SecurityConfig : WebSecurityConfigurerAdapter() {
override fun configure(http: HttpSecurity) {
http.httpBasic()?.disable()
http.cors()?.and()?.csrf()?.disable()
}
}
package id.go.kemenag.madrasah.pmrms.notif.config
import id.go.kemenag.madrasah.pmrms.notif.constant.AUDIENCE_FILTER_PATH
import id.go.kemenag.madrasah.pmrms.notif.interceptor.TokenInterceptor
import org.springframework.boot.web.servlet.FilterRegistrationBean
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.web.servlet.config.annotation.CorsRegistry
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer
@Configuration
class SpringWebConfig : WebMvcConfigurer {
override fun addCorsMappings(registry: CorsRegistry) {
registry.addMapping("/**")
.allowedMethods("HEAD", "GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "PUT", "TRACE")
.allowedOrigins("*")
.allowedHeaders("*")
.maxAge(60000)
}
@Bean
fun tokenFilter(): FilterRegistrationBean<TokenInterceptor>? {
val registrationBean: FilterRegistrationBean<TokenInterceptor> =
FilterRegistrationBean<TokenInterceptor>()
AUDIENCE_FILTER_PATH.forEach {
it.value.forEach {
registrationBean.addUrlPatterns(it)
}
}
registrationBean.filter = TokenInterceptor()
registrationBean.order = 1
return registrationBean
}
override fun addViewControllers(registry: ViewControllerRegistry) {
registry.addRedirectViewController("/v2/api-docs", "/v2/api-docs")
registry.addRedirectViewController("/swagger-resources/configuration/ui", "/swagger-resources/configuration/ui")
registry.addRedirectViewController(
"/swagger-resources/configuration/security",
"/swagger-resources/configuration/security"
)
registry.addRedirectViewController("/swagger-resources", "/swagger-resources")
}
override fun addResourceHandlers(registry: ResourceHandlerRegistry) {
registry.addResourceHandler("/swagger-ui.html**")
.addResourceLocations("classpath:/META-INF/resources/swagger-ui.html")
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/")
}
}
package id.go.kemenag.madrasah.pmrms.notif.config
import org.springframework.beans.factory.annotation.Value
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import springfox.documentation.builders.PathSelectors
import springfox.documentation.builders.RequestHandlerSelectors
import springfox.documentation.service.*
import springfox.documentation.spi.DocumentationType
import springfox.documentation.spi.service.contexts.SecurityContext
import springfox.documentation.spring.web.plugins.Docket
import springfox.documentation.swagger2.annotations.EnableSwagger2
import java.util.*
@Suppress("UNCHECKED_CAST", "DEPRECATION")
@Configuration
@EnableSwagger2
class SwaggerConfig {
@Value("\${swagger.host}")
private val host: String? = null
@Value("\${swagger.protocol}")
private val protocol: String? = null
@Bean
fun api(): Docket? {
return Docket(DocumentationType.SWAGGER_2)
.host(host)
.select()
.apis(RequestHandlerSelectors.basePackage("id.go.kemenag.madrasah.pmrms.notif.controller"))
.paths(PathSelectors.any())
.build()
.useDefaultResponseMessages(false)
.apiInfo(apiInfo())
.protocols(setOf(protocol))
.securitySchemes(listOf(apiKey()) as List<SecurityScheme>?)
.securityContexts(Collections.singletonList(securityContext()))
}
private fun apiInfo(): ApiInfo {
return ApiInfo(
"PMRMS API - Notif",
"Notification Service for Application PMRMS",
"1.0",
"Terms of service",
Contact("Application PMRMS", "-", "-"),
"",
"",
Collections.emptyList()
)
}
private fun apiKey(): ApiKey {
return ApiKey("Bearer", "Authorization", "header")
}
private fun securityContext(): SecurityContext? {
return SecurityContext.builder().securityReferences(defaultAuth()).forPaths(PathSelectors.regex("/.*")).build()
}
private fun defaultAuth(): List<SecurityReference?> {
return listOf(SecurityReference("Bearer", arrayOfNulls(0)))
}
}
package id.go.kemenag.madrasah.pmrms.notif.constant
import java.io.File
const val ROOT_DIR = "D:\\app-files\\pmrms\\files\\"
val UPLOAD_DIR = "${ROOT_DIR}uploads${File.separator}"
val UPLOAD_USER_DIR = "${UPLOAD_DIR}users"
package id.go.kemenag.madrasah.pmrms.notif.constant
const val EXPIRATION_TIME: Long = 864000000 // 10 days (1 hour = 3600000)
const val SECRET = "INOVASI-DAERAH"
const val TOKEN_PREFIX = "Bearer "
const val HEADER_STRING = "Authorization"
val USER_ADMIN_ALLOWED_PATH =
listOf(
"/users/*",
"/auth/logout",
"/auth/detail",
"/profile/*",
)
val AUDIENCE_FILTER_PATH = mapOf(
"user-admin" to USER_ADMIN_ALLOWED_PATH
)
package id.go.kemenag.madrasah.pmrms.notif.constant
const val VALIDATOR_MSG_REQUIRED = "harus di isi"
const val VALIDATOR_MSG_NOT_VALID = "tidak valid"
const val VALIDATOR_MSG_HAS_USED = "sudah di pakai"
const val VALIDATOR_MSG_NOT_FOUND = "tidak di temukan"
const val VALIDATOR_MSG_NOT_HAVE_ACCESS = "tidak mempunyai hak akses"
const val VALIDATOR_MSG_DELETE_RELATION = "Data tidak dapat di hapus."
const val VALIDATOR_MSG_REQUEST_FAILED = "Gagal melakukan request data"
package id.go.kemenag.madrasah.pmrms.notif.controller
import id.go.kemenag.madrasah.pmrms.notif.model.request.EmailRequest
import id.go.kemenag.madrasah.pmrms.notif.model.response.ReturnData
import id.go.kemenag.madrasah.pmrms.notif.service.EmailService
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
import javax.validation.Valid
@RestController
@RequestMapping(path = ["email"])
class EmailController {
@Autowired
private lateinit var service: EmailService
@PostMapping(value = ["send-email"], produces = ["application/json"])
fun sendEmail(@Valid @RequestBody request: EmailRequest): ResponseEntity<ReturnData> {
return service.sendEamil(request)
}
}
package id.go.kemenag.madrasah.pmrms.notif.helpers
import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.databind.ObjectMapper
import id.go.kemenag.madrasah.pmrms.notif.pojo.Users
import org.springframework.security.core.context.SecurityContextHolder
import java.net.MalformedURLException
import java.net.URL
import javax.servlet.http.HttpServletRequest
fun getUserLogin(): Users? {
return try {
val principal = SecurityContextHolder.getContext().authentication.principal as Any
val objectMapper = ObjectMapper()
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
objectMapper.readValue(principal.toString(), Users::class.java)
} catch (e: Exception) {
null
}
}
@Throws(MalformedURLException::class)
fun getBaseUrl(request: HttpServletRequest): String {
try {
val requestURL = URL(request.requestURL.toString())
val port = if (requestURL.port == -1) "" else ":" + requestURL.port
return requestURL.protocol + "://" + requestURL.host + port + request.contextPath + "/"
} catch (e: Exception) {
throw e
}
}
fun getFullUrl(request: HttpServletRequest): String {
val requestURL = StringBuilder(request.requestURL.toString())
val queryString = request.queryString
return if (queryString == null) {
requestURL.toString()
} else {
requestURL.append('?').append(queryString).toString()
}
}
package id.go.kemenag.madrasah.pmrms.notif.helpers
import id.go.kemenag.madrasah.pmrms.notif.model.response.ReturnData
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
fun responseSuccess(message: String = "Success", data: Any? = null): ResponseEntity<ReturnData> {
return ResponseEntity<ReturnData>(
ReturnData(
success = true,
message = message,
data = data
),
HttpStatus.OK
)
}
fun responseCreated(message: String = "Created", data: Any? = null): ResponseEntity<ReturnData> {
return ResponseEntity<ReturnData>(
ReturnData(
success = true,
message = message,
data = data
),
HttpStatus.CREATED
)
}
fun responseInternalServerError(
message: String = "Internal Server Error",
data: Any? = null
): ResponseEntity<ReturnData> {
return ResponseEntity<ReturnData>(
ReturnData(
success = false,
message = message,
data = data
),
HttpStatus.INTERNAL_SERVER_ERROR
)
}
fun responseNotFound(message: String = "Not Found", data: Any? = null): ResponseEntity<ReturnData> {
return ResponseEntity<ReturnData>(
ReturnData(
success = false,
message = message,
data = data
),
HttpStatus.NOT_FOUND
)
}
fun responseUnprocessableEntity(
message: String = "Unprocessable Entity",
data: Any? = null
): ResponseEntity<ReturnData> {
return ResponseEntity<ReturnData>(
ReturnData(
success = false,
message = message,
data = data
),
HttpStatus.UNPROCESSABLE_ENTITY
)
}
fun responseBadRequest(message: String = "Bad Request", data: Any? = null): ResponseEntity<ReturnData> {
return ResponseEntity<ReturnData>(
ReturnData(
success = false,
message = message,
data = data
),
HttpStatus.BAD_REQUEST
)
}
package id.go.kemenag.madrasah.pmrms.notif.interceptor
import com.auth0.jwt.JWT
import com.auth0.jwt.algorithms.Algorithm
import com.auth0.jwt.exceptions.JWTCreationException
import com.auth0.jwt.exceptions.JWTDecodeException
import com.auth0.jwt.exceptions.JWTVerificationException
import com.fasterxml.jackson.databind.ObjectMapper
import id.go.kemenag.madrasah.pmrms.notif.model.response.ReturnData
import id.go.kemenag.madrasah.pmrms.notif.constant.*
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.web.util.ContentCachingRequestWrapper
import javax.servlet.Filter
import javax.servlet.FilterChain
import javax.servlet.ServletRequest
import javax.servlet.ServletResponse
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
class TokenInterceptor : Filter {
override fun doFilter(request: ServletRequest?, response: ServletResponse?, chain: FilterChain?) {
val req = ContentCachingRequestWrapper(request as HttpServletRequest)
val res = response as HttpServletResponse
// Read the Authorization header, where the JWT token should be
val header = req.getHeader(HEADER_STRING)
// If header doesn't contain Bearer or is null will return nothing and exit
if (header == null || !header.startsWith(TOKEN_PREFIX)) {
responseException(res, "Token $VALIDATOR_MSG_REQUIRED")
return
}
var authentication: UsernamePasswordAuthenticationToken? = null
var successAuth = true
try {
authentication = getAuthentication(req)
} catch (e: JWTDecodeException) {
successAuth = false
responseException(res, e.message.toString())
} catch (e: JWTCreationException) {
successAuth = false
responseException(res, e.message.toString())
} catch (e: JWTVerificationException) {
successAuth = false
responseException(res, e.message.toString())
} catch (e: IllegalArgumentException) {
successAuth = false
responseException(res, e.message.toString())
} catch (e: Exception) {
successAuth = false
responseException(res, e.message.toString())
}
SecurityContextHolder.getContext().authentication = authentication
if (successAuth) {
chain?.doFilter(request, response)
}
}
private fun responseException(response: HttpServletResponse, message: String) {
val json = ObjectMapper().writeValueAsString(
ReturnData(
message = message
)
)
response.contentType = "application/json"
response.status = HttpServletResponse.SC_UNAUTHORIZED
response.writer.write(json)
response.writer.flush()
response.writer.close()
}
@Throws(
JWTDecodeException::class,
JWTCreationException::class,
JWTVerificationException::class,
IllegalArgumentException::class
)
private fun getAuthentication(request: HttpServletRequest): UsernamePasswordAuthenticationToken {
val token = request.getHeader(HEADER_STRING)
// parse jwt token from request and validate it internal authentication
val jwt =
JWT.require(Algorithm.HMAC512(SECRET.toByteArray())).build().verify(token.replace(TOKEN_PREFIX, ""))
return if (jwt.subject != null) { // return principal username
val audience = jwt.audience?.get(0)
var isAllowed = false
AUDIENCE_FILTER_PATH[audience]?.forEach lambda@{
if (it.contains("/*")) {
if (request.requestURI.contains(it.replace("/*", ""))) {
isAllowed = true
return@lambda
}
} else {
if (request.requestURI == it) {
isAllowed = true
return@lambda
}
}
}
if (isAllowed) {
/*val objectMapper = ObjectMapper()
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
val users: Users = objectMapper.readValue(jwt.subject, Users::class.java)
val servletContext = request.session.servletContext
val webApplicationContext = WebApplicationContextUtils.getWebApplicationContext(servletContext)
val repoUser = webApplicationContext!!.getBean(UserRepository::class.java)
val checkUser = repoUser.findByIdAndActive(users.id)
if (checkUser.isPresent) {
UsernamePasswordAuthenticationToken(
objectMapper.writeValueAsString(users),
audience,
ArrayList()
)
} else {
throw Exception("User $VALIDATOR_MSG_NOT_FOUND")
}*/
throw Exception("User $VALIDATOR_MSG_NOT_FOUND")
} else {
throw Exception("User $VALIDATOR_MSG_NOT_HAVE_ACCESS")
}
} else {
throw Exception("Token $VALIDATOR_MSG_NOT_VALID")
}
}
}
package id.go.kemenag.madrasah.pmrms.notif.model.request
import id.go.kemenag.madrasah.pmrms.notif.constant.*
import javax.validation.constraints.NotEmpty
data class EmailRequest(
@field:NotEmpty(message = "Penerima $VALIDATOR_MSG_REQUIRED")
var to: String? = null,
@field:NotEmpty(message = "Subjek $VALIDATOR_MSG_REQUIRED")
var subject: String? = null,
@field:NotEmpty(message = "Pesan $VALIDATOR_MSG_REQUIRED")
var message: String? = null
)
package id.go.kemenag.madrasah.pmrms.notif.model.response
data class DatatableResponse(
var content: Any? = null,
var first: Boolean? = null,
var last: Boolean? = null,
var totalElements: Long? = null,
var totalPages: Int? = null
)
package id.go.kemenag.madrasah.pmrms.notif.model.response
data class ErrorMessage(
var key: String? = null,
var message: String? = null
)
package id.go.kemenag.madrasah.pmrms.notif.model.response
data class ReturnData(
var success: Boolean? = false,
var data: Any? = null,
var message: String? = null
)
package id.go.kemenag.madrasah.pmrms.notif.model.response.jwt
data class JwtAuthenticationResponse(
val accessToken: String? = null,
val tokenType: String? = null,
val expired: Long = 0,
val user: Any? = null
)
package id.go.kemenag.madrasah.pmrms.notif.pojo
import com.fasterxml.jackson.annotation.JsonFormat
import com.fasterxml.jackson.annotation.JsonIgnore
import id.go.kemenag.madrasah.pmrms.notif.constant.*
import java.util.*
import javax.persistence.Column
import javax.persistence.Entity
import javax.persistence.Id
import javax.persistence.Table
import javax.validation.constraints.NotEmpty
@Entity
@Table(name = "role", schema = "auth")
data class Role(
@Id
@Column(name = "id")
var id: String? = UUID.randomUUID().toString(),
@Column(name = "name")
@field:NotEmpty(message = "Nama $VALIDATOR_MSG_REQUIRED")
var name: String? = null,
@Column(name = "created_at")
@get:JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy HH:mm:ss", timezone = "GMT+7")
var createdAt: Date? = Date(),
@Column(name = "updated_at")
@get:JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy HH:mm:ss", timezone = "GMT+7")
var updatedAt: Date? = Date(),
@Column(name = "active")
@JsonIgnore
var active: Boolean? = true
)
package id.go.kemenag.madrasah.pmrms.notif.pojo
import com.fasterxml.jackson.annotation.JsonFormat
import com.fasterxml.jackson.annotation.JsonIgnore
import org.hibernate.annotations.Where
import java.util.*
import javax.persistence.*
@Entity
@Table(name = "users", schema = "auth")
data class Users(
@Id
@Column(name = "id")
var id: String? = UUID.randomUUID().toString(),
@Column(name = "email")
var email: String? = null,
@Column(name = "password")
@JsonIgnore
var password: String? = null,
@Column(name = "first_name")
var fullName: String? = null,
@Column(name = "last_name")
var lastName: String? = null,
@OneToMany(mappedBy = "userId")
@Where(clause = "active = true")
var roles: MutableSet<UsersRole>? = null,
@Column(name = "created_at")
@get:JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy HH:mm:ss", timezone = "GMT+7")
var createdAt: Date? = Date(),
@Column(name = "updated_at")
@get:JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy HH:mm:ss", timezone = "GMT+7")
var updatedAt: Date? = Date(),
@Column(name = "active")
@JsonIgnore
var active: Boolean? = true
)
package id.go.kemenag.madrasah.pmrms.notif.pojo
import com.fasterxml.jackson.annotation.JsonFormat
import com.fasterxml.jackson.annotation.JsonIgnore
import id.go.kemenag.madrasah.pmrms.notif.pojo.Role
import java.util.*
import javax.persistence.*
@Entity
@Table(name = "users_role", schema = "auth")
data class UsersRole(
@Id
@Column(name = "id")
var id: String? = UUID.randomUUID().toString(),
@Column(name = "user_id")
var userId: String? = null,
@Column(name = "role_id")
var roleId: String? = null,
@ManyToOne
@JoinColumn(name = "role_id", insertable = false, updatable = false, nullable = true)
var role: Role? = null,
@Column(name = "created_at")
@get:JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy HH:mm:ss", timezone = "GMT+7")
var createdAt: Date? = Date(),
@Column(name = "updated_at")
@get:JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy HH:mm:ss", timezone = "GMT+7")
var updatedAt: Date? = Date(),
@Column(name = "active")
@JsonIgnore
var active: Boolean? = true
)
package id.go.kemenag.madrasah.pmrms.notif.service
import id.go.kemenag.madrasah.pmrms.notif.helpers.responseSuccess
import id.go.kemenag.madrasah.pmrms.notif.model.request.EmailRequest
import id.go.kemenag.madrasah.pmrms.notif.model.response.ReturnData
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Value
import org.springframework.http.ResponseEntity
import org.springframework.mail.SimpleMailMessage
import org.springframework.mail.javamail.JavaMailSender
import org.springframework.stereotype.Service
@Service
class EmailService {
@Value("\${app.name}")
private lateinit var appName: String
@Autowired
private lateinit var javaMailSender: JavaMailSender
@Value("\${spring.mail.from}")
private lateinit var emailFrom: String
fun sendEamil(request: EmailRequest): ResponseEntity<ReturnData> {
return try {
Thread {
sendEmail(request.to, request.subject, request.message)
}.start()
responseSuccess()
} catch (e: Exception) {
throw e
}
}
private fun sendEmail(to: String?, subject: String?, message: String?) {
try {
val msg = SimpleMailMessage()
msg.setTo(to)
msg.setFrom(emailFrom)
msg.setSubject("$subject $appName")
msg.setText(message ?: "")
javaMailSender.send(msg)
} catch (e: Exception) {
throw e
}
}
}
package id.go.kemenag.madrasah.pmrms.notif.validator
import id.go.kemenag.madrasah.pmrms.notif.constant.VALIDATOR_MSG_NOT_VALID
import org.apache.commons.validator.routines.EmailValidator
import javax.validation.Constraint
import javax.validation.ConstraintValidator
import javax.validation.ConstraintValidatorContext
import javax.validation.Payload
import kotlin.reflect.KClass
@Target(AnnotationTarget.FIELD)
@MustBeDocumented
@Constraint(validatedBy = [CustomEmailValidatorConstrain::class])
annotation class EmailValidator(
val message: String = "Email $VALIDATOR_MSG_NOT_VALID",
val groups: Array<KClass<*>> = [],
val payload: Array<KClass<out Payload>> = []
)
class CustomEmailValidatorConstrain :
ConstraintValidator<id.go.kemenag.madrasah.pmrms.notif.validator.EmailValidator, String> {
override fun isValid(p0: String?, p1: ConstraintValidatorContext?): Boolean {
if (p0.isNullOrEmpty()) {
return true
} else {
if (!p0.contains("@madrasah.kemenag.go.id")) {
return false
}
}
return EmailValidator.getInstance().isValid(p0)
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment