Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,8 @@ services:
- VAULT_URL=http://vault:8200
- VAULT_HOST=vault
- PREFERENCES=$PREFERENCES
- APP_NAME=$APP_NAME
- APP_BASE_URL=$APP_BASE_URL
depends_on:
- captcha
- kafka-1
Expand Down
5 changes: 5 additions & 0 deletions user-management/keycloak-gateway/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,11 @@
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-vault-config</artifactId>
</dependency>
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.15.3</version>
</dependency>
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka-test</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,25 @@ class WhitelistConfig(private val preferences: Preferences) {

@Bean("whitelist")
fun whitelist(): Whitelist {
val whitelist = with(preferences.auth.whitelist) {
with(preferences.auth.whitelist) {
val file = File(file)
if (!enabled) {
logger.info("whitelist disabled by preferences")
Whitelist()
return Whitelist()
}

if (!file.exists()) {
logger.info("whitelist file doesn't exists")
Whitelist()
return Whitelist()
}

val list = file.readLines().onEach { it.trim().toLowerCase() }
Whitelist(list.isNotEmpty(), list)
}

logger.info("whitelist enabled: ${whitelist.isEnabled}")
logger.info("whitelist emails: ${whitelist.emails}")
return whitelist
logger.info("whitelist enabled: ${list.isNotEmpty()}")
logger.info("whitelist emails: $list")

return Whitelist(list.isNotEmpty(), list)
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import co.nilin.opex.auth.gateway.data.*
import co.nilin.opex.auth.gateway.model.ActionTokenResult
import co.nilin.opex.auth.gateway.model.AuthEvent
import co.nilin.opex.auth.gateway.model.UserCreatedEvent
import co.nilin.opex.auth.gateway.providers.CustomEmailTemplateProvider
import co.nilin.opex.auth.gateway.utils.*
import co.nilin.opex.utility.error.data.OpexError
import co.nilin.opex.utility.error.data.OpexException
Expand Down Expand Up @@ -377,13 +378,14 @@ class UserManagementResource(private val session: KeycloakSession) : RealmResour

private fun sendEmail(user: UserModel, sendAction: (EmailTemplateProvider) -> Unit) {
if (!user.isEnabled) throw OpexException(OpexError.BadRequest, "User is disabled")

val clientId = Constants.ACCOUNT_MANAGEMENT_CLIENT_ID
val client = session.clients().getClientByClientId(opexRealm, clientId)
if (client == null || !client.isEnabled) throw OpexException(OpexError.BadRequest, "Client error")

try {
val provider = session.getProvider(EmailTemplateProvider::class.java)
val provider = session.getAllProviders(EmailTemplateProvider::class.java)
.find { it is CustomEmailTemplateProvider }!!
//val provider = session.getProvider(CustomEmailTemplateProvider::class.java)
sendAction(provider.setRealm(opexRealm).setUser(user))
} catch (e: Exception) {
logger.error("Unable to send verification email")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package co.nilin.opex.auth.gateway.providers

import co.nilin.opex.auth.gateway.ApplicationContextHolder
import org.jsoup.Jsoup
import org.jsoup.nodes.Document
import org.keycloak.email.EmailSenderProvider
import org.keycloak.email.EmailTemplateProvider
import org.keycloak.events.Event
import org.keycloak.models.KeycloakSession
import org.keycloak.models.RealmModel
import org.keycloak.models.UserModel
import org.keycloak.sessions.AuthenticationSessionModel
import org.springframework.core.io.ClassPathResource

class CustomEmailTemplateProvider(private val session: KeycloakSession) : EmailTemplateProvider {

private var authenticationSession: AuthenticationSessionModel? = null
private var realm: RealmModel? = null
private var user: UserModel? = null
private var attributes = HashMap<String, Any>()
private val appName by lazy {
ApplicationContextHolder.getCurrentContext()!!.environment.resolvePlaceholders("\${APP_NAME}")
}
private val baseUrl by lazy {
ApplicationContextHolder.getCurrentContext()!!.environment.resolvePlaceholders("\${APP_BASE_URL}")
}

override fun setAuthenticationSession(authenticationSession: AuthenticationSessionModel?): EmailTemplateProvider {
this.authenticationSession = authenticationSession
return this
}

override fun setRealm(realm: RealmModel?): EmailTemplateProvider {
this.realm = realm
return this
}

override fun setUser(user: UserModel?): EmailTemplateProvider {
this.user = user
return this
}

override fun setAttribute(name: String?, value: Any?): EmailTemplateProvider {
if (name != null && value != null)
attributes[name] = value
return this
}

override fun sendEvent(event: Event?) {
TODO("Not yet implemented")
}

override fun sendPasswordReset(link: String?, expirationInMinutes: Long) {
val template = processTemplate("password-reset.html") {
getElementById("action-button")?.attr("href", link ?: "")
}
send("$appName reset password", template.html(), "Please click on the link below to reset password\n $link")
}

override fun sendSmtpTestEmail(config: MutableMap<String, String>?, user: UserModel?) {
val template = processTemplate("execute-action.html") {
getElementById("action-button")?.attr("href", "/where-link-goes")
}
send("$appName SMTP test email", template.html(), "This is a test email")
}

override fun sendConfirmIdentityBrokerLink(link: String?, expirationInMinutes: Long) {
TODO("Not yet implemented")
}

override fun sendExecuteActions(link: String?, expirationInMinutes: Long) {
val template = processTemplate("execute-action.html") {
getElementById("action-button")?.attr("href", link ?: "")
}
send("$appName execute actions", template.html(), "Please click on the link below to execute actions\n $link")
}

override fun sendVerifyEmail(link: String?, expirationInMinutes: Long) {
val template = processTemplate("verify-email.html") {
getElementById("action-button")?.attr("href", link ?: "")
}
send("$appName verify email", template.html(), "Please click on the link below to verify email\n $link")
}

override fun send(subjectFormatKey: String?, bodyTemplate: String?, bodyAttributes: MutableMap<String, Any>?) {
send(subjectFormatKey, mutableListOf(), bodyTemplate, bodyAttributes)
}

override fun send(
subjectFormatKey: String?,
subjectAttributes: MutableList<Any>?,
bodyTemplate: String?,
bodyAttributes: MutableMap<String, Any>?
) {

}

private fun send(subject: String, body: String, textBody: String) {
val emailSender = session.getProvider(EmailSenderProvider::class.java)
emailSender.send(realm?.smtpConfig, user, subject, textBody, body)
}

private fun processTemplate(fileName: String, docBuilder: Document.() -> Unit): Document {
return getTemplateAsDocument(fileName).apply {
getElementById("site")?.apply {
attr("href", baseUrl)
text(appName)
}
getElementById("link")?.apply {
attr("href", baseUrl)
text(appName)
}
docBuilder(this)
}
}

override fun close() {}

private fun getTemplate(fileName: String): String {
return ClassPathResource("email-templates/$fileName").inputStream
.bufferedReader()
.use { it.readText() }
}

private fun getTemplateAsDocument(fileName: String): Document {
return Jsoup.parse(getTemplate(fileName))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package co.nilin.opex.auth.gateway.providers

import org.keycloak.Config
import org.keycloak.email.EmailTemplateProvider
import org.keycloak.email.EmailTemplateProviderFactory
import org.keycloak.models.KeycloakSession
import org.keycloak.models.KeycloakSessionFactory

class CustomEmailTemplateProviderFactory : EmailTemplateProviderFactory {

override fun create(session: KeycloakSession): EmailTemplateProvider {
return CustomEmailTemplateProvider(session)
}

override fun init(config: Config.Scope?) {

}

override fun postInit(factory: KeycloakSessionFactory?) {

}

override fun close() {

}

override fun getId(): String {
return "custom-email-template"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
co.nilin.opex.auth.gateway.providers.CustomEmailTemplateProviderFactory
Loading