Skip to content

Commit 31a0dbd

Browse files
committed
Moves all of the HAT claim logic into Authentication controller
1 parent 1345be0 commit 31a0dbd

File tree

3 files changed

+79
-122
lines changed

3 files changed

+79
-122
lines changed

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

+77-19
Original file line numberDiff line numberDiff line change
@@ -24,34 +24,36 @@
2424

2525
package org.hatdex.hat.api.controllers
2626

27-
import java.net.{ URLDecoder, URLEncoder }
27+
import java.net.{URLDecoder, URLEncoder}
2828

2929
import akka.Done
3030
import javax.inject.Inject
3131
import com.mohiva.play.silhouette.api.repositories.AuthInfoRepository
32-
import com.mohiva.play.silhouette.api.util.{ Credentials, PasswordHasherRegistry }
33-
import com.mohiva.play.silhouette.api.{ LoginEvent, Silhouette }
34-
import com.mohiva.play.silhouette.impl.exceptions.{ IdentityNotFoundException, InvalidPasswordException }
32+
import com.mohiva.play.silhouette.api.util.{Credentials, PasswordHasherRegistry}
33+
import com.mohiva.play.silhouette.api.{LoginEvent, Silhouette}
34+
import com.mohiva.play.silhouette.impl.exceptions.{IdentityNotFoundException, InvalidPasswordException}
3535
import com.mohiva.play.silhouette.impl.providers.CredentialsProvider
3636
import org.hatdex.hat.api.json.HatJsonFormats
3737
import org.hatdex.hat.api.models._
3838
import org.hatdex.hat.api.service.applications.ApplicationsService
39-
import org.hatdex.hat.api.service.{ HatServicesService, LogService, MailTokenService, UsersService }
39+
import org.hatdex.hat.api.service.{HatServicesService, LogService, MailTokenService, UsersService}
4040
import org.hatdex.hat.authentication._
4141
import org.hatdex.hat.phata.models._
42-
import org.hatdex.hat.resourceManagement.{ HatServerProvider, _ }
43-
import org.hatdex.hat.utils.{ HatBodyParsers, HatMailer }
44-
import play.api.Logger
45-
import play.api.cache.{ Cached, CachedBuilder }
42+
import org.hatdex.hat.resourceManagement.{HatServerProvider, _}
43+
import org.hatdex.hat.utils.{HatBodyParsers, HatMailer}
44+
import play.api.{Configuration, Logger}
45+
import play.api.cache.{Cached, CachedBuilder}
4646
import play.api.libs.json.Json
47-
import play.api.mvc.{ Action, _ }
47+
import play.api.libs.ws.WSClient
48+
import play.api.mvc.{Action, _}
4849

4950
import scala.concurrent.ExecutionContext.Implicits.global
5051
import scala.concurrent.Future
5152

5253
class Authentication @Inject() (
5354
components: ControllerComponents,
5455
cached: Cached,
56+
configuration: Configuration,
5557
parsers: HatBodyParsers,
5658
hatServerProvider: HatServerProvider,
5759
silhouette: Silhouette[HatApiAuthEnvironment],
@@ -64,6 +66,7 @@ class Authentication @Inject() (
6466
logService: LogService,
6567
mailer: HatMailer,
6668
tokenService: MailTokenService[MailTokenUser],
69+
wsClient: WSClient,
6770
limiter: UserLimiter) extends HatApiController(components, silhouette) with HatJsonFormats {
6871

6972
private val logger = Logger(this.getClass)
@@ -228,15 +231,10 @@ class Authentication @Inject() (
228231
}
229232
}
230233

231-
/*
232-
BEGIN: hat-claim
233-
Entire Section below is on HAT Claim.
234-
TODO: Refactor to New File?
235-
*/
236234
/**
237235
* Sends an email to the owner with a link to claim the hat
238236
*/
239-
def claim(): Action[ApiClaimHatRequest] = UserAwareAction.async(parsers.json[ApiClaimHatRequest]) { implicit request =>
237+
def handleClaimStart(): Action[ApiClaimHatRequest] = UserAwareAction.async(parsers.json[ApiClaimHatRequest]) { implicit request =>
240238

241239
val claimHatRequest = request.body
242240
val email = request.dynamicEnvironment.ownerEmail
@@ -301,7 +299,67 @@ class Authentication @Inject() (
301299
Future.successful(response)
302300
}
303301
}
304-
/*
305-
END: hat-claim
306-
*/
302+
303+
def handleClaimComplete(claimToken: String): Action[HatClaimCompleteRequest] = UserAwareAction.async(parsers.json[HatClaimCompleteRequest]) { implicit request =>
304+
implicit val hatClaimComplete: HatClaimCompleteRequest = request.body
305+
306+
tokenService.retrieve(claimToken).flatMap {
307+
case Some(token) if token.isSignUp && !token.isExpired && token.email == request.dynamicEnvironment.ownerEmail =>
308+
usersService.listUsers.map(_.find(_.roles.contains(Owner()))).flatMap {
309+
case Some(user) =>
310+
val eventualResult = for {
311+
_ <- updateHatMembership(hatClaimComplete)
312+
_ <- authInfoRepository.update(user.loginInfo, passwordHasherRegistry.current.hash(request.body.password))
313+
_ <- tokenService.expire(token.id)
314+
authenticator <- env.authenticatorService.create(user.loginInfo)
315+
result <- env.authenticatorService.renew(authenticator, Ok(Json.toJson(SuccessResponse("HAT claimed"))))
316+
_ <- logService.logAction(request.dynamicEnvironment.domain, LogRequest("claimed", None, None), None).recover {
317+
case e =>
318+
logger.error(s"LogActionError::unclaimed. Reason: ${e.getMessage}")
319+
Done
320+
}
321+
} yield {
322+
//env.eventBus.publish(LoginEvent(user, request))
323+
//mailer.passwordChanged(token.email, user)
324+
325+
result
326+
}
327+
328+
eventualResult.recover {
329+
case e =>
330+
logger.error(s"HAT claim process failed with error ${e.getMessage}")
331+
BadRequest(Json.toJson(ErrorMessage("Bad Request", "HAT claim process failed")))
332+
}
333+
334+
case None => Future.successful(Unauthorized(Json.toJson(ErrorMessage("HAT claim unauthorized", "No user matching token"))))
335+
}
336+
337+
case Some(_) =>
338+
Future.successful(Unauthorized(Json.toJson(ErrorMessage("Invalid Token", "Token expired or invalid"))))
339+
340+
case None =>
341+
Future.successful(Unauthorized(Json.toJson(ErrorMessage("Invalid Token", "Token does not exist"))))
342+
}
343+
}
344+
345+
private def updateHatMembership(claim: HatClaimCompleteRequest): Future[Done] = {
346+
val path = "api/products/hat/claim"
347+
val hattersUrl = s"${configuration.underlying.getString("hatters.scheme")}${configuration.underlying.getString("hatters.address")}"
348+
349+
logger.info(s"Proxy POST request to $hattersUrl/$path with parameters: $claim")
350+
351+
val futureResponse = wsClient.url(s"$hattersUrl/$path")
352+
//.withHttpHeaders("x-auth-token" → token.accessToken)
353+
.post(Json.toJson(claim.copy(password = "")))
354+
355+
futureResponse.flatMap { response =>
356+
response.status match {
357+
case OK =>
358+
Future.successful(Done)
359+
case _ =>
360+
logger.error(s"Failed to claim HAT with Hatters. Claim details:\n$claim\nHatters response: ${response.body}")
361+
Future.failed(new UnknownError("HAT claim failed"))
362+
}
363+
}
364+
}
307365
}

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

-101
This file was deleted.

hat/conf/routes

+2-2
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ GET /control/v2/auth/hatlogin org.hatdex.hat.
2525
POST /control/v2/auth/password org.hatdex.hat.api.controllers.Authentication.passwordChangeProcess
2626
POST /control/v2/auth/passwordReset org.hatdex.hat.api.controllers.Authentication.handleForgotPassword
2727
POST /control/v2/auth/passwordreset/confirm/:token org.hatdex.hat.api.controllers.Authentication.handleResetPassword(token: String)
28-
POST /control/v2/auth/claim org.hatdex.hat.api.controllers.Authentication.claim
29-
POST /control/v2/auth/claim/complete/:claimToken org.hatdex.hat.api.controllers.HattersRequestProxy.proxyRequestHatClaim(claimToken: String)
28+
POST /control/v2/auth/claim org.hatdex.hat.api.controllers.Authentication.handleClaimStart
29+
POST /control/v2/auth/claim/complete/:claimToken org.hatdex.hat.api.controllers.Authentication.handleClaimComplete(claimToken: String)
3030

3131
-> /api/v2 v20.Routes
3232
-> /api/v2.0 v20.Routes

0 commit comments

Comments
 (0)