KEYCLOAK-2133 KEYCLOAK-1782

This commit is contained in:
Stian Thorgersen 2015-11-25 13:59:47 +01:00
parent 889679c715
commit be040eaa18
23 changed files with 240 additions and 170 deletions

View file

@ -2,7 +2,7 @@ package org.keycloak.events.email;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.keycloak.email.EmailException; import org.keycloak.email.EmailException;
import org.keycloak.email.EmailProvider; import org.keycloak.email.EmailTemplateProvider;
import org.keycloak.events.admin.AdminEvent; import org.keycloak.events.admin.AdminEvent;
import org.keycloak.events.Event; import org.keycloak.events.Event;
import org.keycloak.events.EventListenerProvider; import org.keycloak.events.EventListenerProvider;
@ -23,13 +23,13 @@ public class EmailEventListenerProvider implements EventListenerProvider {
private KeycloakSession session; private KeycloakSession session;
private RealmProvider model; private RealmProvider model;
private EmailProvider emailProvider; private EmailTemplateProvider emailTemplateProvider;
private Set<EventType> includedEvents; private Set<EventType> includedEvents;
public EmailEventListenerProvider(KeycloakSession session, EmailProvider emailProvider, Set<EventType> includedEvents) { public EmailEventListenerProvider(KeycloakSession session, EmailTemplateProvider emailTemplateProvider, Set<EventType> includedEvents) {
this.session = session; this.session = session;
this.model = session.realms(); this.model = session.realms();
this.emailProvider = emailProvider; this.emailTemplateProvider = emailTemplateProvider;
this.includedEvents = includedEvents; this.includedEvents = includedEvents;
} }
@ -41,7 +41,7 @@ public class EmailEventListenerProvider implements EventListenerProvider {
UserModel user = session.users().getUserById(event.getUserId(), realm); UserModel user = session.users().getUserById(event.getUserId(), realm);
if (user != null && user.getEmail() != null && user.isEmailVerified()) { if (user != null && user.getEmail() != null && user.isEmailVerified()) {
try { try {
emailProvider.setRealm(realm).setUser(user).sendEvent(event); emailTemplateProvider.setRealm(realm).setUser(user).sendEvent(event);
} catch (EmailException e) { } catch (EmailException e) {
log.error("Failed to send type mail", e); log.error("Failed to send type mail", e);
} }

View file

@ -1,7 +1,7 @@
package org.keycloak.events.email; package org.keycloak.events.email;
import org.keycloak.Config; import org.keycloak.Config;
import org.keycloak.email.EmailProvider; import org.keycloak.email.EmailTemplateProvider;
import org.keycloak.events.EventListenerProvider; import org.keycloak.events.EventListenerProvider;
import org.keycloak.events.EventListenerProviderFactory; import org.keycloak.events.EventListenerProviderFactory;
import org.keycloak.events.EventType; import org.keycloak.events.EventType;
@ -26,8 +26,8 @@ public class EmailEventListenerProviderFactory implements EventListenerProviderF
@Override @Override
public EventListenerProvider create(KeycloakSession session) { public EventListenerProvider create(KeycloakSession session) {
EmailProvider emailProvider = session.getProvider(EmailProvider.class); EmailTemplateProvider emailTemplateProvider = session.getProvider(EmailTemplateProvider.class);
return new EmailEventListenerProvider(session, emailProvider, includedEvents); return new EmailEventListenerProvider(session, emailTemplateProvider, includedEvents);
} }
@Override @Override

View file

@ -1195,7 +1195,7 @@ module.controller('RealmSMTPSettingsCtrl', function($scope, Current, Realm, real
} }
} }
obj['port'] = obj['port'].toString(); obj['port'] = obj['port'] && obj['port'].toString();
return obj; return obj;
} }

View file

@ -0,0 +1,14 @@
package org.keycloak.email;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.provider.Provider;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public interface EmailSenderProvider extends Provider {
void send(RealmModel realm, UserModel user, String subject, String textBody, String htmlBody) throws EmailException;
}

View file

@ -5,5 +5,5 @@ import org.keycloak.provider.ProviderFactory;
/** /**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a> * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/ */
public interface EmailProviderFactory extends ProviderFactory<EmailProvider> { public interface EmailSenderProviderFactory extends ProviderFactory<EmailSenderProvider> {
} }

View file

@ -7,7 +7,7 @@ import org.keycloak.provider.Spi;
/** /**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a> * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/ */
public class EmailSpi implements Spi { public class EmailSenderSpi implements Spi {
@Override @Override
public boolean isInternal() { public boolean isInternal() {
@ -16,16 +16,16 @@ public class EmailSpi implements Spi {
@Override @Override
public String getName() { public String getName() {
return "email"; return "emailSender";
} }
@Override @Override
public Class<? extends Provider> getProviderClass() { public Class<? extends Provider> getProviderClass() {
return EmailProvider.class; return EmailSenderProvider.class;
} }
@Override @Override
public Class<? extends ProviderFactory> getProviderFactoryClass() { public Class<? extends ProviderFactory> getProviderFactoryClass() {
return EmailProviderFactory.class; return EmailSenderProviderFactory.class;
} }
} }

View file

@ -8,15 +8,15 @@ import org.keycloak.provider.Provider;
/** /**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a> * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/ */
public interface EmailProvider extends Provider { public interface EmailTemplateProvider extends Provider {
String IDENTITY_PROVIDER_BROKER_CONTEXT = "identityProviderBrokerCtx"; String IDENTITY_PROVIDER_BROKER_CONTEXT = "identityProviderBrokerCtx";
public EmailProvider setRealm(RealmModel realm); public EmailTemplateProvider setRealm(RealmModel realm);
public EmailProvider setUser(UserModel user); public EmailTemplateProvider setUser(UserModel user);
public EmailProvider setAttribute(String name, Object value); public EmailTemplateProvider setAttribute(String name, Object value);
public void sendEvent(Event event) throws EmailException; public void sendEvent(Event event) throws EmailException;

View file

@ -0,0 +1,9 @@
package org.keycloak.email;
import org.keycloak.provider.ProviderFactory;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public interface EmailTemplateProviderFactory extends ProviderFactory<EmailTemplateProvider> {
}

View file

@ -0,0 +1,31 @@
package org.keycloak.email;
import org.keycloak.provider.Provider;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.provider.Spi;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class EmailTemplateSpi implements Spi {
@Override
public boolean isInternal() {
return true;
}
@Override
public String getName() {
return "emailTemplate";
}
@Override
public Class<? extends Provider> getProviderClass() {
return EmailTemplateProvider.class;
}
@Override
public Class<? extends ProviderFactory> getProviderFactoryClass() {
return org.keycloak.email.EmailTemplateProviderFactory.class;
}
}

View file

@ -1 +1,2 @@
org.keycloak.email.EmailSpi org.keycloak.email.EmailSenderSpi
org.keycloak.email.EmailTemplateSpi

View file

@ -1,29 +1,10 @@
package org.keycloak.email.freemarker; package org.keycloak.email.freemarker;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import javax.mail.Message;
import javax.mail.Multipart;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import org.jboss.logging.Logger;
import org.keycloak.broker.provider.BrokeredIdentityContext; import org.keycloak.broker.provider.BrokeredIdentityContext;
import org.keycloak.common.util.ObjectUtil; import org.keycloak.common.util.ObjectUtil;
import org.keycloak.email.EmailException; import org.keycloak.email.EmailException;
import org.keycloak.email.EmailProvider; import org.keycloak.email.EmailSenderProvider;
import org.keycloak.email.EmailTemplateProvider;
import org.keycloak.email.freemarker.beans.EventBean; import org.keycloak.email.freemarker.beans.EventBean;
import org.keycloak.email.freemarker.beans.ProfileBean; import org.keycloak.email.freemarker.beans.ProfileBean;
import org.keycloak.events.Event; import org.keycloak.events.Event;
@ -33,43 +14,43 @@ import org.keycloak.freemarker.FreeMarkerUtil;
import org.keycloak.freemarker.Theme; import org.keycloak.freemarker.Theme;
import org.keycloak.freemarker.ThemeProvider; import org.keycloak.freemarker.ThemeProvider;
import org.keycloak.freemarker.beans.MessageFormatterMethod; import org.keycloak.freemarker.beans.MessageFormatterMethod;
import org.keycloak.models.IdentityProviderModel;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel; import org.keycloak.models.UserModel;
import java.text.MessageFormat;
import java.util.*;
/** /**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a> * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/ */
public class FreeMarkerEmailProvider implements EmailProvider { public class FreeMarkerEmailTemplateProvider implements EmailTemplateProvider {
private static final Logger log = Logger.getLogger(FreeMarkerEmailProvider.class);
private KeycloakSession session; private KeycloakSession session;
private FreeMarkerUtil freeMarker; private FreeMarkerUtil freeMarker;
private RealmModel realm; private RealmModel realm;
private UserModel user; private UserModel user;
private final Map<String, Object> attributes = new HashMap<String, Object>(); private final Map<String, Object> attributes = new HashMap<>();
public FreeMarkerEmailProvider(KeycloakSession session, FreeMarkerUtil freeMarker) { public FreeMarkerEmailTemplateProvider(KeycloakSession session, FreeMarkerUtil freeMarker) {
this.session = session; this.session = session;
this.freeMarker = freeMarker; this.freeMarker = freeMarker;
} }
@Override @Override
public EmailProvider setRealm(RealmModel realm) { public EmailTemplateProvider setRealm(RealmModel realm) {
this.realm = realm; this.realm = realm;
return this; return this;
} }
@Override @Override
public EmailProvider setUser(UserModel user) { public EmailTemplateProvider setUser(UserModel user) {
this.user = user; this.user = user;
return this; return this;
} }
@Override @Override
public EmailProvider setAttribute(String name, Object value) { public EmailTemplateProvider setAttribute(String name, Object value) {
attributes.put(name, value); attributes.put(name, value);
return this; return this;
} }
@ -178,73 +159,9 @@ public class FreeMarkerEmailProvider implements EmailProvider {
} }
} }
private void send(String subject, String textBody, String htmlBody) throws EmailException { private void send(String subject, String textBody, String htmlBody) throws EmailException {
try { EmailSenderProvider emailSender = session.getProvider(EmailSenderProvider.class);
String address = user.getEmail(); emailSender.send(realm, user, subject, textBody, htmlBody);
Map<String, String> config = realm.getSmtpConfig();
Properties props = new Properties();
props.setProperty("mail.smtp.host", config.get("host"));
boolean auth = "true".equals(config.get("auth"));
boolean ssl = "true".equals(config.get("ssl"));
boolean starttls = "true".equals(config.get("starttls"));
if (config.containsKey("port")) {
props.setProperty("mail.smtp.port", config.get("port"));
}
if (auth) {
props.put("mail.smtp.auth", "true");
}
if (ssl) {
props.put("mail.smtp.socketFactory.port", config.get("port"));
props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
}
if (starttls) {
props.put("mail.smtp.starttls.enable", "true");
}
String from = config.get("from");
Session session = Session.getInstance(props);
Multipart multipart = new MimeMultipart("alternative");
if(textBody != null) {
MimeBodyPart textPart = new MimeBodyPart();
textPart.setText(textBody, "UTF-8");
multipart.addBodyPart(textPart);
}
if(htmlBody != null) {
MimeBodyPart htmlPart = new MimeBodyPart();
htmlPart.setContent(htmlBody, "text/html; charset=UTF-8");
multipart.addBodyPart(htmlPart);
}
Message msg = new MimeMessage(session);
msg.setFrom(new InternetAddress(from));
msg.setHeader("To", address);
msg.setSubject(subject);
msg.setContent(multipart);
msg.saveChanges();
msg.setSentDate(new Date());
Transport transport = session.getTransport("smtp");
if (auth) {
transport.connect(config.get("user"), config.get("password"));
} else {
transport.connect();
}
transport.sendMessage(msg, new InternetAddress[]{new InternetAddress(address)});
} catch (Exception e) {
log.warn("Failed to send email", e);
throw new EmailException(e);
}
} }
@Override @Override

View file

@ -1,8 +1,8 @@
package org.keycloak.email.freemarker; package org.keycloak.email.freemarker;
import org.keycloak.Config; import org.keycloak.Config;
import org.keycloak.email.EmailProvider; import org.keycloak.email.EmailTemplateProvider;
import org.keycloak.email.EmailProviderFactory; import org.keycloak.email.EmailTemplateProviderFactory;
import org.keycloak.freemarker.FreeMarkerUtil; import org.keycloak.freemarker.FreeMarkerUtil;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.KeycloakSessionFactory;
@ -10,13 +10,13 @@ import org.keycloak.models.KeycloakSessionFactory;
/** /**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a> * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/ */
public class FreeMarkerEmailProviderFactory implements EmailProviderFactory { public class FreeMarkerEmailTemplateProviderFactory implements EmailTemplateProviderFactory {
private FreeMarkerUtil freeMarker; private FreeMarkerUtil freeMarker;
@Override @Override
public EmailProvider create(KeycloakSession session) { public EmailTemplateProvider create(KeycloakSession session) {
return new FreeMarkerEmailProvider(session, freeMarker); return new FreeMarkerEmailTemplateProvider(session, freeMarker);
} }
@Override @Override
@ -26,7 +26,6 @@ public class FreeMarkerEmailProviderFactory implements EmailProviderFactory {
@Override @Override
public void postInit(KeycloakSessionFactory factory) { public void postInit(KeycloakSessionFactory factory) {
} }
@Override @Override

View file

@ -1 +0,0 @@
org.keycloak.email.freemarker.FreeMarkerEmailProviderFactory

View file

@ -0,0 +1 @@
org.keycloak.email.freemarker.FreeMarkerEmailTemplateProviderFactory

View file

@ -24,7 +24,7 @@ import org.keycloak.authentication.requiredactions.util.UserUpdateProfileContext
import org.keycloak.broker.provider.BrokeredIdentityContext; import org.keycloak.broker.provider.BrokeredIdentityContext;
import org.keycloak.common.util.ObjectUtil; import org.keycloak.common.util.ObjectUtil;
import org.keycloak.email.EmailException; import org.keycloak.email.EmailException;
import org.keycloak.email.EmailProvider; import org.keycloak.email.EmailTemplateProvider;
import org.keycloak.freemarker.BrowserSecurityHeaderSetup; import org.keycloak.freemarker.BrowserSecurityHeaderSetup;
import org.keycloak.freemarker.FreeMarkerException; import org.keycloak.freemarker.FreeMarkerException;
import org.keycloak.freemarker.FreeMarkerUtil; import org.keycloak.freemarker.FreeMarkerUtil;
@ -153,7 +153,7 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
String link = builder.build(realm.getName()).toString(); String link = builder.build(realm.getName()).toString();
long expiration = TimeUnit.SECONDS.toMinutes(realm.getAccessCodeLifespanUserAction()); long expiration = TimeUnit.SECONDS.toMinutes(realm.getAccessCodeLifespanUserAction());
session.getProvider(EmailProvider.class).setRealm(realm).setUser(user).sendVerifyEmail(link, expiration); session.getProvider(EmailTemplateProvider.class).setRealm(realm).setUser(user).sendVerifyEmail(link, expiration);
} catch (EmailException e) { } catch (EmailException e) {
logger.error("Failed to send verification email", e); logger.error("Failed to send verification email", e);
return setError(Messages.EMAIL_SENT_ERROR).createErrorPage(); return setError(Messages.EMAIL_SENT_ERROR).createErrorPage();

View file

@ -13,7 +13,7 @@ import org.keycloak.authentication.requiredactions.VerifyEmail;
import org.keycloak.authentication.authenticators.broker.util.SerializedBrokeredIdentityContext; import org.keycloak.authentication.authenticators.broker.util.SerializedBrokeredIdentityContext;
import org.keycloak.broker.provider.BrokeredIdentityContext; import org.keycloak.broker.provider.BrokeredIdentityContext;
import org.keycloak.email.EmailException; import org.keycloak.email.EmailException;
import org.keycloak.email.EmailProvider; import org.keycloak.email.EmailTemplateProvider;
import org.keycloak.events.Details; import org.keycloak.events.Details;
import org.keycloak.events.Errors; import org.keycloak.events.Errors;
import org.keycloak.events.EventBuilder; import org.keycloak.events.EventBuilder;
@ -68,10 +68,10 @@ public class IdpEmailVerificationAuthenticator extends AbstractIdpAuthenticator
long expiration = TimeUnit.SECONDS.toMinutes(context.getRealm().getAccessCodeLifespanUserAction()); long expiration = TimeUnit.SECONDS.toMinutes(context.getRealm().getAccessCodeLifespanUserAction());
try { try {
context.getSession().getProvider(EmailProvider.class) context.getSession().getProvider(EmailTemplateProvider.class)
.setRealm(realm) .setRealm(realm)
.setUser(existingUser) .setUser(existingUser)
.setAttribute(EmailProvider.IDENTITY_PROVIDER_BROKER_CONTEXT, brokerContext) .setAttribute(EmailTemplateProvider.IDENTITY_PROVIDER_BROKER_CONTEXT, brokerContext)
.sendConfirmIdentityBrokerLink(link, expiration); .sendConfirmIdentityBrokerLink(link, expiration);
event.success(); event.success();

View file

@ -8,29 +8,20 @@ import org.keycloak.authentication.Authenticator;
import org.keycloak.authentication.AuthenticatorFactory; import org.keycloak.authentication.AuthenticatorFactory;
import org.keycloak.authentication.authenticators.broker.AbstractIdpAuthenticator; import org.keycloak.authentication.authenticators.broker.AbstractIdpAuthenticator;
import org.keycloak.authentication.authenticators.browser.AbstractUsernameFormAuthenticator; import org.keycloak.authentication.authenticators.browser.AbstractUsernameFormAuthenticator;
import org.keycloak.email.EmailException;
import org.keycloak.email.EmailProvider;
import org.keycloak.events.Details; import org.keycloak.events.Details;
import org.keycloak.events.Errors; import org.keycloak.events.Errors;
import org.keycloak.events.EventBuilder; import org.keycloak.events.EventBuilder;
import org.keycloak.login.LoginFormsProvider;
import org.keycloak.models.AuthenticationExecutionModel; import org.keycloak.models.AuthenticationExecutionModel;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel; import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.protocol.oidc.TokenManager;
import org.keycloak.provider.ProviderConfigProperty; import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.services.Urls;
import org.keycloak.services.messages.Messages; import org.keycloak.services.messages.Messages;
import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import java.util.List; import java.util.List;
import java.util.concurrent.TimeUnit;
/** /**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>

View file

@ -8,7 +8,7 @@ import org.keycloak.authentication.Authenticator;
import org.keycloak.authentication.AuthenticatorFactory; import org.keycloak.authentication.AuthenticatorFactory;
import org.keycloak.authentication.authenticators.browser.AbstractUsernameFormAuthenticator; import org.keycloak.authentication.authenticators.browser.AbstractUsernameFormAuthenticator;
import org.keycloak.email.EmailException; import org.keycloak.email.EmailException;
import org.keycloak.email.EmailProvider; import org.keycloak.email.EmailTemplateProvider;
import org.keycloak.events.Details; import org.keycloak.events.Details;
import org.keycloak.events.Errors; import org.keycloak.events.Errors;
import org.keycloak.events.EventBuilder; import org.keycloak.events.EventBuilder;
@ -72,7 +72,7 @@ public class ResetCredentialEmail implements Authenticator, AuthenticatorFactory
long expiration = TimeUnit.SECONDS.toMinutes(context.getRealm().getAccessCodeLifespanUserAction()); long expiration = TimeUnit.SECONDS.toMinutes(context.getRealm().getAccessCodeLifespanUserAction());
try { try {
context.getSession().getProvider(EmailProvider.class).setRealm(context.getRealm()).setUser(user).sendPasswordReset(link, expiration); context.getSession().getProvider(EmailTemplateProvider.class).setRealm(context.getRealm()).setUser(user).sendPasswordReset(link, expiration);
event.clone().event(EventType.SEND_RESET_PASSWORD) event.clone().event(EventType.SEND_RESET_PASSWORD)
.user(user) .user(user)
.detail(Details.USERNAME, username) .detail(Details.USERNAME, username)

View file

@ -0,0 +1,102 @@
package org.keycloak.email;
import org.jboss.logging.Logger;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import javax.mail.Message;
import javax.mail.Multipart;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import java.util.Date;
import java.util.Map;
import java.util.Properties;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class DefaultEmailSenderProvider implements EmailSenderProvider {
private static final Logger log = Logger.getLogger(DefaultEmailSenderProvider.class);
@Override
public void send(RealmModel realm, UserModel user, String subject, String textBody, String htmlBody) throws EmailException {
try {
String address = user.getEmail();
Map<String, String> config = realm.getSmtpConfig();
Properties props = new Properties();
props.setProperty("mail.smtp.host", config.get("host"));
boolean auth = "true".equals(config.get("auth"));
boolean ssl = "true".equals(config.get("ssl"));
boolean starttls = "true".equals(config.get("starttls"));
if (config.containsKey("port")) {
props.setProperty("mail.smtp.port", config.get("port"));
}
if (auth) {
props.setProperty("mail.smtp.auth", "true");
}
if (ssl) {
props.setProperty("mail.smtp.ssl.enable", "true");
}
if (starttls) {
props.setProperty("mail.smtp.starttls.enable", "true");
}
props.setProperty("mail.smtp.timeout", "10000");
props.setProperty("mail.smtp.connectiontimeout", "10000");
String from = config.get("from");
Session session = Session.getInstance(props);
Multipart multipart = new MimeMultipart("alternative");
if(textBody != null) {
MimeBodyPart textPart = new MimeBodyPart();
textPart.setText(textBody, "UTF-8");
multipart.addBodyPart(textPart);
}
if(htmlBody != null) {
MimeBodyPart htmlPart = new MimeBodyPart();
htmlPart.setContent(htmlBody, "text/html; charset=UTF-8");
multipart.addBodyPart(htmlPart);
}
Message msg = new MimeMessage(session);
msg.setFrom(new InternetAddress(from));
msg.setHeader("To", address);
msg.setSubject(subject);
msg.setContent(multipart);
msg.saveChanges();
msg.setSentDate(new Date());
Transport transport = session.getTransport("smtp");
if (auth) {
transport.connect(config.get("user"), config.get("password"));
} else {
transport.connect();
}
transport.sendMessage(msg, new InternetAddress[]{new InternetAddress(address)});
} catch (Exception e) {
log.error("Failed to send email", e);
throw new EmailException(e);
}
}
@Override
public void close() {
}
}

View file

@ -0,0 +1,34 @@
package org.keycloak.email;
import org.keycloak.Config;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class DefaultEmailSenderProviderFactory implements EmailSenderProviderFactory {
@Override
public EmailSenderProvider create(KeycloakSession session) {
return new DefaultEmailSenderProvider();
}
@Override
public void init(Config.Scope config) {
}
@Override
public void postInit(KeycloakSessionFactory factory) {
}
@Override
public void close() {
}
@Override
public String getId() {
return "default";
}
}

View file

@ -2,61 +2,37 @@ package org.keycloak.services.resources.admin;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.jboss.resteasy.annotations.cache.NoCache; import org.jboss.resteasy.annotations.cache.NoCache;
import org.jboss.resteasy.spi.BadRequestException;
import org.jboss.resteasy.spi.NotFoundException; import org.jboss.resteasy.spi.NotFoundException;
import org.keycloak.common.ClientConnection; import org.keycloak.common.ClientConnection;
import org.keycloak.email.EmailException;
import org.keycloak.email.EmailProvider;
import org.keycloak.events.admin.OperationType; import org.keycloak.events.admin.OperationType;
import org.keycloak.models.ClientModel; import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ModelReadOnlyException;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleMapperModel; import org.keycloak.models.RoleMapperModel;
import org.keycloak.models.RoleModel; import org.keycloak.models.RoleModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.models.utils.ModelToRepresentation; import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.models.utils.RepresentationToModel;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.protocol.oidc.TokenManager;
import org.keycloak.protocol.oidc.utils.RedirectUtils;
import org.keycloak.representations.idm.ClientMappingsRepresentation; import org.keycloak.representations.idm.ClientMappingsRepresentation;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.representations.idm.MappingsRepresentation; import org.keycloak.representations.idm.MappingsRepresentation;
import org.keycloak.representations.idm.RoleRepresentation; import org.keycloak.representations.idm.RoleRepresentation;
import org.keycloak.services.ErrorResponse;
import org.keycloak.services.Urls;
import org.keycloak.services.managers.BruteForceProtector; import org.keycloak.services.managers.BruteForceProtector;
import org.keycloak.services.managers.ClientSessionCode;
import org.keycloak.services.managers.RealmManager; import org.keycloak.services.managers.RealmManager;
import javax.ws.rs.Consumes; import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE; import javax.ws.rs.DELETE;
import javax.ws.rs.GET; import javax.ws.rs.GET;
import javax.ws.rs.POST; import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path; import javax.ws.rs.Path;
import javax.ws.rs.PathParam; import javax.ws.rs.PathParam;
import javax.ws.rs.Produces; import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context; import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo; import javax.ws.rs.core.UriInfo;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.TimeUnit;
/** /**
* Base resource for managing users * Base resource for managing users

View file

@ -8,7 +8,7 @@ import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.keycloak.common.ClientConnection; import org.keycloak.common.ClientConnection;
import org.keycloak.authentication.RequiredActionProvider; import org.keycloak.authentication.RequiredActionProvider;
import org.keycloak.email.EmailException; import org.keycloak.email.EmailException;
import org.keycloak.email.EmailProvider; import org.keycloak.email.EmailTemplateProvider;
import org.keycloak.events.Details; import org.keycloak.events.Details;
import org.keycloak.events.EventBuilder; import org.keycloak.events.EventBuilder;
import org.keycloak.events.EventType; import org.keycloak.events.EventType;
@ -23,7 +23,6 @@ import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ModelDuplicateException; import org.keycloak.models.ModelDuplicateException;
import org.keycloak.models.ModelReadOnlyException; import org.keycloak.models.ModelReadOnlyException;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserConsentModel; import org.keycloak.models.UserConsentModel;
import org.keycloak.models.UserCredentialModel; import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel; import org.keycloak.models.UserModel;
@ -34,18 +33,14 @@ import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.protocol.oidc.TokenManager; import org.keycloak.protocol.oidc.TokenManager;
import org.keycloak.protocol.oidc.utils.RedirectUtils; import org.keycloak.protocol.oidc.utils.RedirectUtils;
import org.keycloak.provider.ProviderFactory; import org.keycloak.provider.ProviderFactory;
import org.keycloak.representations.idm.ClientMappingsRepresentation;
import org.keycloak.representations.idm.CredentialRepresentation; import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.representations.idm.FederatedIdentityRepresentation; import org.keycloak.representations.idm.FederatedIdentityRepresentation;
import org.keycloak.representations.idm.GroupRepresentation; import org.keycloak.representations.idm.GroupRepresentation;
import org.keycloak.representations.idm.MappingsRepresentation;
import org.keycloak.representations.idm.RoleRepresentation;
import org.keycloak.representations.idm.UserConsentRepresentation; import org.keycloak.representations.idm.UserConsentRepresentation;
import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.representations.idm.UserSessionRepresentation; import org.keycloak.representations.idm.UserSessionRepresentation;
import org.keycloak.services.managers.AuthenticationManager; import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.ClientSessionCode; import org.keycloak.services.managers.ClientSessionCode;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.services.managers.UserManager; import org.keycloak.services.managers.UserManager;
import org.keycloak.services.ErrorResponse; import org.keycloak.services.ErrorResponse;
import org.keycloak.services.Urls; import org.keycloak.services.Urls;
@ -804,7 +799,7 @@ public class UsersResource {
String link = builder.build(realm.getName()).toString(); String link = builder.build(realm.getName()).toString();
long expiration = TimeUnit.SECONDS.toMinutes(realm.getAccessCodeLifespanUserAction()); long expiration = TimeUnit.SECONDS.toMinutes(realm.getAccessCodeLifespanUserAction());
this.session.getProvider(EmailProvider.class).setRealm(realm).setUser(user).sendExecuteActions(link, expiration); this.session.getProvider(EmailTemplateProvider.class).setRealm(realm).setUser(user).sendExecuteActions(link, expiration);
//audit.user(user).detail(Details.EMAIL, user.getEmail()).detail(Details.CODE_ID, accessCode.getCodeId()).success(); //audit.user(user).detail(Details.EMAIL, user.getEmail()).detail(Details.CODE_ID, accessCode.getCodeId()).success();
@ -856,7 +851,7 @@ public class UsersResource {
String link = builder.build(realm.getName()).toString(); String link = builder.build(realm.getName()).toString();
long expiration = TimeUnit.SECONDS.toMinutes(realm.getAccessCodeLifespanUserAction()); long expiration = TimeUnit.SECONDS.toMinutes(realm.getAccessCodeLifespanUserAction());
this.session.getProvider(EmailProvider.class).setRealm(realm).setUser(user).sendVerifyEmail(link, expiration); this.session.getProvider(EmailTemplateProvider.class).setRealm(realm).setUser(user).sendVerifyEmail(link, expiration);
//audit.user(user).detail(Details.EMAIL, user.getEmail()).detail(Details.CODE_ID, accessCode.getCodeId()).success(); //audit.user(user).detail(Details.EMAIL, user.getEmail()).detail(Details.CODE_ID, accessCode.getCodeId()).success();

View file

@ -0,0 +1 @@
org.keycloak.email.DefaultEmailSenderProviderFactory