Skip to content
This repository has been archived by the owner on Dec 12, 2018. It is now read-only.

Commit

Permalink
Load email template files from a ResourceFactory
Browse files Browse the repository at this point in the history
Fixes: #1330
  • Loading branch information
bdemers committed May 16, 2017
1 parent a3a585f commit 5f0d07a
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -385,8 +385,8 @@ stormpath.email.tlsEnabled = false
stormpath.email.username =
stormpath.email.password =

stormpath.email.verifyEmailTemplate = /com/stormpath/sdk/mail/templates/verifyEmail.json
stormpath.email.forgotPasswordTemplate = /com/stormpath/sdk/mail/templates/forgotPassword.json
stormpath.email.verifyEmailTemplate = classpath:com/stormpath/sdk/mail/templates/verifyEmail.json
stormpath.email.forgotPasswordTemplate = classpath:com/stormpath/sdk/mail/templates/forgotPassword.json

stormpath.application.allowApiClientCredentials = false
stormpath.application.apiSecretQueryTemplate = ?search=profile.stormpathApiKey_1 sw "{0}" or profile.stormpathApiKey_2 sw "{0}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,10 +154,10 @@ public abstract class AbstractStormpathConfiguration {
@Value("#{ @environment['stormpath.email.tokenExpirationHours'] ?: 2 }")
protected int emailExpirationHours;

@Value("#{ @environment['stormpath.email.verifyEmailTemplate'] ?: '/com/stormpath/sdk/mail/templates/verifyEmail.json' }")
@Value("#{ @environment['stormpath.email.verifyEmailTemplate'] ?: 'classpath:com/stormpath/sdk/mail/templates/verifyEmail.json' }")
protected String verifyEmailTemplate;

@Value("#{ @environment['stormpath.email.forgotPasswordTemplate'] ?: '/com/stormpath/sdk/mail/templates/forgotPassword.json' }")
@Value("#{ @environment['stormpath.email.forgotPasswordTemplate'] ?: 'classpath:com/stormpath/sdk/mail/templates/forgotPassword.json' }")
protected String forgotPasswordEmailTemplate;

@Value("#{ @environment['stormpath.application.allowApiClientCredentials'] ?: false }")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.stormpath.sdk.impl.error.DefaultError;
import com.stormpath.sdk.impl.io.ResourceFactory;
import com.stormpath.sdk.impl.mail.template.EmailTemplate;
import com.stormpath.sdk.mail.EmailRequest;
import com.stormpath.sdk.mail.EmailService;
Expand Down Expand Up @@ -42,13 +43,14 @@ public class CommonsEmailService implements EmailService {
private final Logger log = LoggerFactory.getLogger(CommonsEmailService.class);

private final TemplateRenderer templateRenderer;

private final EmailServiceConfig config;
private final ResourceFactory resourceFactory;

public CommonsEmailService(EmailServiceConfig config, TemplateRenderer templateRenderer) {
public CommonsEmailService(EmailServiceConfig config, TemplateRenderer templateRenderer, ResourceFactory resourceFactory) {

this.config = config;
this.templateRenderer = templateRenderer;
this.resourceFactory = resourceFactory;
}

@Override
Expand All @@ -63,7 +65,7 @@ public void sendResetEmail(EmailRequest request) {

private void sendEmail(String template, EmailRequest request) {

try (InputStream inputStream = getClass().getResourceAsStream(template)) {
try (InputStream inputStream = resourceFactory.createResource(template).getInputStream()) {

Map<String, Object> context = new LinkedHashMap<>();

Expand Down Expand Up @@ -123,7 +125,15 @@ private void sendEmail(String template, EmailRequest request) {
throw new ResourceException(error);
}
} catch (IOException e) {
throw new UnsupportedOperationException("wtf", e);
String message = "Failed to parse email template.";
log.warn(message, e);

DefaultError error = new DefaultError(null);
error.setStatus(500);
error.setMessage(message);
error.setDeveloperMessage(e.getMessage());

throw new ResourceException(error);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.stormpath.sdk.impl.mail;

import com.stormpath.sdk.impl.io.DefaultResourceFactory;
import com.stormpath.sdk.impl.mail.template.VelocityTemplateRenderer;
import com.stormpath.sdk.mail.EmailService;
import com.stormpath.sdk.mail.EmailServiceBuilder;
Expand Down Expand Up @@ -28,6 +29,6 @@ public EmailServiceBuilder setConfig(EmailServiceConfig config) {

@Override
public EmailService build() {
return new CommonsEmailService(config, templateRenderer);
return new CommonsEmailService(config, templateRenderer, new DefaultResourceFactory());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.stormpath.sdk.impl.application
import com.stormpath.sdk.account.Account
import com.stormpath.sdk.account.AccountStatus
import com.stormpath.sdk.account.EmailVerificationStatus
import com.stormpath.sdk.account.EmailVerificationToken
import com.stormpath.sdk.api.ApiKey
import com.stormpath.sdk.directory.CustomData
import com.stormpath.sdk.impl.account.DefaultVerificationEmailRequest
Expand Down Expand Up @@ -119,6 +120,7 @@ class OktaApplicationTest {
def account = createMock(Account)
def apiKey = createMock(ApiKey)
def customData = createMock(CustomData)
def emailVerificationToken = createMock(EmailVerificationToken)

def jwt = Jwts.builder()
.setSubject(email)
Expand All @@ -140,13 +142,15 @@ class OktaApplicationTest {
expect(internalDataStore.getApiKey()).andReturn(apiKey)
expect(apiKey.getSecret()).andReturn(apiKeyScret)

expect(account.getEmailVerificationToken()).andReturn(emailVerificationToken).anyTimes()
expect(emailVerificationToken.getValue()).andReturn(jwt)
expect(account.setEmailVerificationStatus(EmailVerificationStatus.VERIFIED)).andReturn(account)
expect(account.setStatus(AccountStatus.ENABLED)).andReturn(account)
expect(account.getCustomData()).andReturn(customData)
expect(customData.put(OktaUserAccountConverter.STORMPATH_EMAIL_VERIFICATION_TOKEN, null)).andReturn(null)
account.save()

replay internalDataStore, client, wellKnown, emailService, account, apiKey, customData
replay internalDataStore, client, wellKnown, emailService, account, apiKey, customData, emailVerificationToken

def app = new OktaApplication(clientId, internalDataStore)

Expand All @@ -159,6 +163,68 @@ class OktaApplicationTest {
app.configureWithProperties(appProps)
app.verifyAccountEmail(jwt)

verify internalDataStore, client, wellKnown, emailService, account, apiKey, customData
verify internalDataStore, client, wellKnown, emailService, account, apiKey, customData, emailVerificationToken
}

@Test
void verifyAccountEmailInvalidTokenTest() {

def email = "[email protected]"
def clientId = "test_client_id"
def apiKeyScret = "api_key_secret"
def internalDataStore = createMock(InternalDataStore)
def client = createMock(DefaultClient)
def emailService = createMock(EmailService)
def wellKnown = createNiceMock(OIDCWellKnownResource)
def account = createMock(Account)
def apiKey = createMock(ApiKey)
def customData = createMock(CustomData)
def emailVerificationToken = createMock(EmailVerificationToken)

def jwt = Jwts.builder()
.setSubject(email)
.claim("tokenType", "verify")
.claim("verifyToken", "this token could be anything")
.claim("userHref", "/api/v1/users/uid")
.compressWith(CompressionCodecs.DEFLATE)
.signWith(SignatureAlgorithm.HS512, apiKeyScret)
.compact()

expect(internalDataStore.getBaseUrl()).andReturn("http://base.example.com").anyTimes()
expect(internalDataStore.getResource("/oauth2/test_authorization_server_id/.well-known/openid-configuration", OIDCWellKnownResource)).andReturn(wellKnown)
client.setTenantResolver(anyObject())

// test specifics
expect(internalDataStore.getResource("/api/v1/users/uid", Account)).andReturn(account)

expect(internalDataStore.getApiKey()).andReturn(apiKey)
expect(apiKey.getSecret()).andReturn(apiKeyScret)

expect(account.getEmailVerificationToken()).andReturn(emailVerificationToken).anyTimes()
expect(emailVerificationToken.getValue()).andReturn(jwt + "_testDifferentToken") // jwt will validate, but token will not match previously saved

replay internalDataStore, client, wellKnown, emailService, account, apiKey, customData, emailVerificationToken

def app = new OktaApplication(clientId, internalDataStore)

def appProps = [
authorizationServerId: "test_authorization_server_id",
emailService: emailService,
registrationWorkflowEnabled: false,
client: client
]
app.configureWithProperties(appProps)

try {
app.verifyAccountEmail(jwt)
fail("expected ResourceException")
}
catch (com.stormpath.sdk.resource.ResourceException e) {
// expected
assertThat(e.getStormpathError().getMessage(), equalTo("Invalid Token"))
assertThat(e.getStormpathError().getDeveloperMessage(), equalTo("Email verification token did NOT match"))
}

verify internalDataStore, client, wellKnown, emailService, account, apiKey, customData, emailVerificationToken
}
}

0 comments on commit 5f0d07a

Please sign in to comment.