Skip to content

Commit ed490da

Browse files
authored
Merge pull request #682 from dataswift/suppress-emails-for-password-reset
Suppress the email to the user for the final endpoint to allow for AP…
2 parents 7a525e2 + 1b68ec6 commit ed490da

File tree

7 files changed

+237
-152
lines changed

7 files changed

+237
-152
lines changed

Dockerfile.copilot

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
FROM adoptopenjdk/openjdk11:jdk-11.0.6_10-alpine
2+
3+
ARG FILE_PATH
4+
ENV FILE_PATH=$FILE_PATH
5+
ARG STRING
6+
ENV STRING=$STRING
7+
ARG DELIMITER
8+
ENV DELIMITER=$DELIMITER
9+
ARG KEYWORD
10+
ENV KEYWORD=$KEYWORD
11+
12+
ARG SBT_VERSION=1.3.10
13+
14+
RUN set -x \
15+
&& apk --update add --no-cache --virtual .build-deps curl \
16+
&& ESUM="3060065764193651aa3fe860a17ff8ea9afc1e90a3f9570f0584f2d516c34380" \
17+
&& SBT_URL="https://github.com/sbt/sbt/releases/download/v1.3.10/sbt-1.3.10.tgz" \
18+
&& apk add bash \
19+
&& curl -Ls ${SBT_URL} > /tmp/sbt-${SBT_VERSION}.tgz \
20+
&& sha256sum /tmp/sbt-${SBT_VERSION}.tgz \
21+
&& (echo "${ESUM} /tmp/sbt-${SBT_VERSION}.tgz" | sha256sum -c -) \
22+
&& tar -zxf /tmp/sbt-${SBT_VERSION}.tgz -C /opt/ \
23+
&& sed -i -r 's#run \"\$\@\"#unset JAVA_TOOL_OPTIONS\nrun \"\$\@\"#g' /opt/sbt/bin/sbt \
24+
&& apk del --purge .build-deps \
25+
&& rm -rf /tmp/sbt-${SBT_VERSION}.tgz /var/cache/apk/*
26+
27+
28+
ENV PATH="/opt/sbt/bin:$PATH" \
29+
JAVA_OPTS="-XX:+UseContainerSupport -Dfile.encoding=UTF-8" \
30+
SBT_OPTS="-Xmx2048M -Xss2M"
31+
32+
WORKDIR /app
33+
ADD . /app
34+
35+
RUN ["chmod", "-R", "777", "start.sh"]
36+
37+
CMD ./start.sh
38+

hat/app/org/hatdex/hat/api/controllers/Authentication.scala

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -308,18 +308,26 @@ class Authentication @Inject() (
308308
*/
309309

310310
// return token and not email, like the other endpoint
311-
def handleForgotPassword: Action[ApiPasswordResetRequest] =
311+
def handleForgotPassword(
312+
sendEmailToUser: Option[Boolean]
313+
): Action[ApiPasswordResetRequest] =
312314
UserAwareAction.async(parsers.json[ApiPasswordResetRequest]) { implicit request =>
313315
implicit val language: Lang = Lang.defaultLang
316+
implicit val sendEmail: Boolean = sendEmailToUser.getOrElse(true)
317+
314318
logger.debug("Processing forgotten password request")
319+
315320
val email = request.body.email
316-
val response = Ok(
321+
322+
// predefined response
323+
var response = Ok(
317324
Json.toJson(
318325
SuccessResponse(
319326
"If the email you have entered is correct, you will shortly receive an email with password reset instructions"
320327
)
321328
)
322329
)
330+
323331
if (email == request.dynamicEnvironment.ownerEmail)
324332
// Find the specific user who is the owner.
325333
userService
@@ -330,11 +338,23 @@ class Authentication @Inject() (
330338
// Create a token for the reset with a 24 hour expiry
331339
// isSignUp is potentially the issue here.
332340
val token = MailTokenUser(email, isSignup = false)
341+
333342
// Store that token
334-
tokenService.create(token).map { _ =>
335-
mailer.passwordReset(email, passwordResetLink(request.host, token.id))
336-
response
343+
tokenService.create(token).map { _ => {
344+
// This sends the user an email to the reset URL
345+
// And a generic response
346+
if (sendEmail) {
347+
mailer.passwordReset(email, passwordResetLink(request.host, token.id))
348+
response
349+
}
350+
351+
// Do not send an email, but return the token
352+
else {
353+
response = Ok(Json.obj("tokenId" -> token.id))
354+
response
355+
}
337356
}
357+
}
338358
// The user was not found, but return the "If we found an email address, we'll send the link."
339359
case None => Future.successful(response)
340360
}
@@ -345,9 +365,13 @@ class Authentication @Inject() (
345365
/**
346366
* Saves the new password and authenticates the user
347367
*/
348-
def handleResetPassword(tokenId: String): Action[ApiPasswordChange] =
368+
def handleResetPassword(
369+
tokenId: String,
370+
sendEmailToUser: Option[Boolean]): Action[ApiPasswordChange] =
349371
UserAwareAction.async(parsers.json[ApiPasswordChange]) { implicit request =>
350372
implicit val language: Lang = Lang.defaultLang
373+
implicit val sendEmail: Boolean = sendEmailToUser.getOrElse(true)
374+
351375
tokenService.retrieve(tokenId).flatMap {
352376
// Token was found, is not signup nor expired
353377
case Some(token) if !token.isSignUp && !token.isExpired =>
@@ -381,7 +405,9 @@ class Authentication @Inject() (
381405
// Push a loginEvent on the bus
382406
env.eventBus.publish(LoginEvent(user, request))
383407
// Mail the user, telling them the password changed
384-
mailer.passwordChanged(token.email)
408+
if (sendEmail) {
409+
mailer.passwordChanged(token.email)
410+
}
385411
// ???: return an AuthenticatorResult, why
386412
result
387413
}
@@ -411,21 +437,14 @@ class Authentication @Inject() (
411437

412438
val claimHatRequest = request.body
413439
val email = request.dynamicEnvironment.ownerEmail
414-
val response = Ok(Json.toJson(SuccessResponse("You will shortly receive an email with claim instructions AAA")))
415-
416-
println(claimHatRequest.email == email)
417-
println(email)
418-
println(claimHatRequest)
440+
val response = Ok(Json.toJson(SuccessResponse("You will shortly receive an email with claim instructions")))
419441

420442
if (claimHatRequest.email == email)
421443
userService
422444
.listUsers()
423445
.map(_.find(u => (u.roles.contains(Owner()) && !(u.roles.contains(Verified("email"))))))
424446
.flatMap {
425-
case Some(user) => {
426-
427-
println(s"found user ${user.email}")
428-
447+
case Some(user) => {
429448
val eventualClaimContext = for {
430449
maybeApplication <- applicationsService
431450
.applicationStatus()(request.dynamicEnvironment, user, request)
@@ -481,7 +500,6 @@ class Authentication @Inject() (
481500
}
482501
}
483502
case None => {
484-
println(s"found user ${response}")
485503
Future.successful(response)
486504
}
487505
}

hat/app/org/hatdex/hat/phata/models/HatClaims.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ case class ApiVerificationRequest(
3030
applicationId: String,
3131
email: String,
3232
redirectUri: String) {
33-
// override def toString: String =
34-
// s"ApiVerificationRequest(applicationId: $applicationId, email:REDACTED, redirectUri: $redirectUri)"
33+
override def toString: String =
34+
s"ApiVerificationRequest(applicationId: $applicationId, email:REDACTED, redirectUri: $redirectUri)"
3535
}
3636

3737
object ApiVerificationRequest {

hat/conf/routes

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ GET /healthz org.hatdex.hat.
2121
# AUTHENTICATION routes
2222
GET /control/v2/auth/hatlogin org.hatdex.hat.api.controllers.Authentication.hatLogin(name: String, redirect: String)
2323
POST /control/v2/auth/password org.hatdex.hat.api.controllers.Authentication.passwordChangeProcess
24-
POST /control/v2/auth/passwordReset org.hatdex.hat.api.controllers.Authentication.handleForgotPassword
25-
POST /control/v2/auth/passwordreset/confirm/:token org.hatdex.hat.api.controllers.Authentication.handleResetPassword(token: String)
24+
POST /control/v2/auth/passwordReset org.hatdex.hat.api.controllers.Authentication.handleForgotPassword(sendEmailToUser: Option[Boolean])
25+
POST /control/v2/auth/passwordreset/confirm/:token org.hatdex.hat.api.controllers.Authentication.handleResetPassword(token: String, sendEmailToUser: Option[Boolean])
2626
POST /control/v2/auth/claim org.hatdex.hat.api.controllers.Authentication.handleVerificationRequest(lang: Option[String], sendEmailToUser: Option[Boolean])
2727
POST /control/v2/auth/request-verification org.hatdex.hat.api.controllers.Authentication.handleVerificationRequest(lang: Option[String], sendEmailToUser: Option[Boolean])
2828
POST /control/v2/auth/claim/complete/:verificationToken org.hatdex.hat.api.controllers.Authentication.handleVerification(verificationToken: String)

hat/test/org/hatdex/hat/api/controllers/AuthenticationSpec.scala

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ class AuthenticationSpec extends AuthenticationContext {
213213
.withJsonBody(Json.toJson(passwordForgottenIncorrect))
214214

215215
val controller = application.injector.instanceOf[Authentication]
216-
val result: Future[Result] = Helpers.call(controller.handleForgotPassword, request)
216+
val result: Future[Result] = Helpers.call(controller.handleForgotPassword(Some(true)), request)
217217

218218
status(result) must equal(OK)
219219
}
@@ -225,7 +225,7 @@ class AuthenticationSpec extends AuthenticationContext {
225225
.withJsonBody(Json.toJson(passwordForgottenOwner))
226226

227227
val controller = application.injector.instanceOf[Authentication]
228-
val result: Future[Result] = Helpers.call(controller.handleForgotPassword, request)
228+
val result: Future[Result] = Helpers.call(controller.handleForgotPassword(Some(true)), request)
229229

230230
status(result) must equal(OK)
231231
}
@@ -236,7 +236,7 @@ class AuthenticationSpec extends AuthenticationContext {
236236
.withJsonBody(Json.toJson(passwordResetStrong))
237237

238238
val controller = application.injector.instanceOf[Authentication]
239-
val result: Future[Result] = Helpers.call(controller.handleResetPassword("nosuchtoken"), request)
239+
val result: Future[Result] = Helpers.call(controller.handleResetPassword("nosuchtoken", (Some(true))), request)
240240

241241
status(result) must equal(UNAUTHORIZED)
242242
(contentAsJson(result) \ "cause").as[String] must equal("Token does not exist")
@@ -253,7 +253,7 @@ class AuthenticationSpec extends AuthenticationContext {
253253

254254
val result: Future[Result] = for {
255255
_ <- tokenService.create(MailTokenUser(tokenId, "[email protected]", DateTime.now().minusHours(1), isSignUp = false))
256-
result <- Helpers.call(controller.handleResetPassword(tokenId), request)
256+
result <- Helpers.call(controller.handleResetPassword(tokenId, (Some(true))), request)
257257
} yield result
258258

259259
status(result) must equal(UNAUTHORIZED)
@@ -271,7 +271,7 @@ class AuthenticationSpec extends AuthenticationContext {
271271

272272
val result: Future[Result] = for {
273273
_ <- tokenService.create(MailTokenUser(tokenId, "[email protected]", DateTime.now().plusHours(1), isSignUp = false))
274-
result <- Helpers.call(controller.handleResetPassword(tokenId), request)
274+
result <- Helpers.call(controller.handleResetPassword(tokenId, (Some(true))), request)
275275
} yield result
276276

277277
status(result) must equal(UNAUTHORIZED)
@@ -289,7 +289,7 @@ class AuthenticationSpec extends AuthenticationContext {
289289
val tokenId = UUID.randomUUID().toString
290290
val result: Future[Result] = for {
291291
_ <- tokenService.create(MailTokenUser(tokenId, "[email protected]", DateTime.now().plusHours(1), isSignUp = false))
292-
result <- Helpers.call(controller.handleResetPassword(tokenId), request)
292+
result <- Helpers.call(controller.handleResetPassword(tokenId, (Some(true))), request)
293293
} yield result
294294

295295
status(result) must equal(OK)
@@ -310,7 +310,7 @@ class AuthenticationSpec extends AuthenticationContext {
310310
_ <- userService.saveUser(
311311
owner.copy(roles = Seq(DataDebitOwner("")))
312312
) // forcing owner user to a different role for the test
313-
result <- Helpers.call(controller.handleResetPassword(tokenId), request)
313+
result <- Helpers.call(controller.handleResetPassword(tokenId, (Some(true))), request)
314314
} yield result
315315

316316
status(result) must equal(UNAUTHORIZED)
@@ -323,7 +323,7 @@ class AuthenticationSpec extends AuthenticationContext {
323323
.withJsonBody(Json.toJson(apiVerificationRequestMatching))
324324

325325
val controller = application.injector.instanceOf[Authentication]
326-
val result: Future[Result] = Helpers.call(controller.handleVerificationRequest(None), request)
326+
val result: Future[Result] = Helpers.call(controller.handleVerificationRequest(None, (Some(true))), request)
327327

328328
status(result) must equal(OK)
329329
}
@@ -334,7 +334,7 @@ class AuthenticationSpec extends AuthenticationContext {
334334
.withJsonBody(Json.toJson(apiVerificationRequestNotMatching))
335335

336336
val controller = application.injector.instanceOf[Authentication]
337-
val result: Future[Result] = Helpers.call(controller.handleVerificationRequest(None), request)
337+
val result: Future[Result] = Helpers.call(controller.handleVerificationRequest(None, (Some(true))), request)
338338

339339
status(result) must equal(OK)
340340
}

0 commit comments

Comments
 (0)