Skip to content

Commit 339b039

Browse files
authored
Merge pull request #573 from dataswift/dev
Dev
2 parents 75461c2 + 684d227 commit 339b039

File tree

3 files changed

+192
-22
lines changed

3 files changed

+192
-22
lines changed

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

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,8 @@ class Authentication @Inject() (
314314
)
315315
if (email == request.dynamicEnvironment.ownerEmail)
316316
// Find the specific user who is the owner.
317-
userService.listUsers
317+
userService
318+
.listUsers()
318319
.map(_.find(_.roles.contains(Owner())))
319320
.flatMap {
320321
case Some(_) =>
@@ -343,10 +344,11 @@ class Authentication @Inject() (
343344
// Token was found, is not signup nor expired
344345
case Some(token) if !token.isSignUp && !token.isExpired =>
345346
// Token.email matches the dynamicEnv (what is this)
346-
if (token.email == request.dynamicEnvironment.ownerEmail)
347+
if (token.email == request.dynamicEnvironment.ownerEmail) {
347348
// Find the users with the owner role
348349
// ???: Why not using the email
349-
userService.listUsers
350+
userService
351+
.listUsers()
350352
.map(_.find(_.roles.contains(Owner())))
351353
.flatMap {
352354
case Some(user) =>
@@ -378,7 +380,7 @@ class Authentication @Inject() (
378380
case None =>
379381
Future.successful(noUserMatchingToken)
380382
}
381-
else
383+
} else
382384
Future.successful(onlyHatOwnerCanReset)
383385
case Some(_) =>
384386
tokenService.consume(tokenId)
@@ -400,15 +402,12 @@ class Authentication @Inject() (
400402
val email = request.dynamicEnvironment.ownerEmail
401403
val response = Ok(Json.toJson(SuccessResponse("You will shortly receive an email with claim instructions")))
402404

403-
logger.info("Handling verification request")
404-
// (email, applicationId) in the body
405-
// Look up the application (Is this in the HAT itself? Not DEX)
406405
if (claimHatRequest.email == email)
407-
userService.listUsers
406+
userService
407+
.listUsers()
408408
.map(_.find(u => (u.roles.contains(Owner()) && !(u.roles.contains(Verified("email"))))))
409409
.flatMap {
410410
case Some(user) =>
411-
logger.info("User found")
412411
val eventualClaimContext = for {
413412
maybeApplication <- applicationsService
414413
.applicationStatus()(request.dynamicEnvironment, user, request)
@@ -422,7 +421,7 @@ class Authentication @Inject() (
422421
case (app, token) =>
423422
val maybeSetupUrl: Option[String] = app.application.setup match {
424423
case setupInfo: ApplicationSetup.External =>
425-
setupInfo.validRedirectUris.find(_ == claimHatRequest.redirectUri)
424+
setupInfo.validRedirectUris.find(x => claimHatRequest.redirectUri.startsWith(x))
426425
case _ =>
427426
None
428427
}
@@ -469,7 +468,8 @@ class Authentication @Inject() (
469468
tokenService.retrieve(verificationToken).flatMap {
470469
case Some(token)
471470
if token.isSignUp && !token.isExpired && token.email == request.dynamicEnvironment.ownerEmail =>
472-
userService.listUsers
471+
userService
472+
.listUsers()
473473
.map(_.find(u => (u.roles.contains(Owner()) && !(u.roles.contains(Verified("email"))))))
474474
.flatMap {
475475
case Some(user) =>
@@ -582,17 +582,17 @@ class Authentication @Inject() (
582582
* Generate email verification string
583583
*/
584584
private def emailVerificationLink(
585-
host: String,
586-
token: String,
587-
verificationOptions: EmailVerificationOptions): String = {
585+
host: String,
586+
token: String,
587+
verificationOptions: EmailVerificationOptions): String = {
588588
logger.info("Creating email verification link")
589589
s"$emailScheme$host/auth/verify-email/$token?${verificationOptions.asQueryParameters}"
590590
}
591591

592592
// TODO: add reset options support
593593
private def passwordResetLink(
594-
host: String,
595-
token: String): String =
594+
host: String,
595+
token: String): String =
596596
s"$emailScheme$host/auth/change-password/$token"
597597

598598
// private def roleMatcher(rolesToMatch: Seq[UserRole], rolesRequired: Seq[UserRole]): Boolean = {

hat/test/org/hatdex/hat/api/HATTestContext.scala

Lines changed: 146 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import io.dataswift.models.hat.{ DataCredit, DataDebitOwner, Owner }
3636
import net.codingwell.scalaguice.ScalaModule
3737
import org.hatdex.hat.api.service._
3838
import org.hatdex.hat.api.service.applications.{ TestApplicationProvider, TrustedApplicationProvider }
39+
//import io.dataswift.models.hat.applications._
3940
import org.hatdex.hat.authentication.HatApiAuthEnvironment
4041
import org.hatdex.hat.authentication.models.HatUser
4142
import org.hatdex.hat.dal.HatDbSchemaMigration
@@ -50,13 +51,27 @@ import org.scalatestplus.mockito.MockitoSugar
5051
import play.api.http.HttpErrorHandler
5152
import play.api.i18n.{ Lang, MessagesApi }
5253
import play.api.inject.guice.GuiceApplicationBuilder
53-
import play.api.{ Application, Configuration, Logger }
54+
import play.api.{ Application => PlayApplication, Configuration, Logger }
5455

5556
import java.io.StringReader
5657
import java.util.UUID
5758
import scala.concurrent.ExecutionContext.Implicits.global
5859
import scala.concurrent.duration._
5960
import scala.concurrent.{ Await, Future }
61+
import io.dataswift.models.hat.FormattedText
62+
import org.joda.time.{ DateTime, LocalDateTime }
63+
import io.dataswift.models.hat.Drawable
64+
import io.dataswift.models.hat.UserRole
65+
import io.dataswift.models.hat.applications.ApplicationStatus
66+
import io.dataswift.models.hat.applications.ApplicationSetup
67+
import io.dataswift.models.hat.applications.ApplicationPermissions
68+
import io.dataswift.models.hat.applications.ApplicationDeveloper
69+
import io.dataswift.models.hat.applications.ApplicationInfo
70+
import io.dataswift.models.hat.applications.Version
71+
import io.dataswift.models.hat.applications.ApplicationKind
72+
import io.dataswift.models.hat.applications.Application
73+
import io.dataswift.models.hat.applications.DataFeedItem
74+
import io.dataswift.models.hat.applications.ApplicationGraphics
6075

6176
abstract class HATTestContext extends PostgresqlSpec with MockitoSugar with BeforeAndAfter {
6277

@@ -191,8 +206,13 @@ mO9kGhALaD5okBcI/VuAQiFvBXdK0ii/nVcBApXEu47PG4oYUgPI
191206
Seq(DataCredit(""), DataCredit("namespace")),
192207
enabled = true
193208
)
209+
194210
implicit lazy val environment: Environment[HatApiAuthEnvironment] = FakeEnvironment[HatApiAuthEnvironment](
195-
Seq(owner.loginInfo -> owner, dataDebitUser.loginInfo -> dataDebitUser, dataCreditUser.loginInfo -> dataCreditUser),
211+
Seq(
212+
owner.loginInfo -> owner,
213+
dataDebitUser.loginInfo -> dataDebitUser,
214+
dataCreditUser.loginInfo -> dataCreditUser
215+
),
196216
hatServer
197217
)
198218

@@ -204,6 +224,128 @@ mO9kGhALaD5okBcI/VuAQiFvBXdK0ii/nVcBApXEu47PG4oYUgPI
204224
"evolutions/hat-database-schema/14_newHat.sql"
205225
)
206226

227+
// Application
228+
val kindAuth: ApplicationKind.Kind = ApplicationKind.App(
229+
url = "https://itunes.apple.com/gb/app/notables/id1338778866?mt=8",
230+
iosUrl = Some("https://itunes.apple.com/gb/app/notables/id1338778866?mt=8"),
231+
androidUrl = None
232+
)
233+
234+
val descriptionAuth: FormattedText = FormattedText(
235+
text = "",
236+
markdown = None,
237+
html = None
238+
)
239+
240+
val dataPreviewAuth: Seq[DataFeedItem] = List.empty
241+
242+
val graphicsAuth: ApplicationGraphics = ApplicationGraphics(
243+
banner = Drawable(normal = "", small = None, large = None, xlarge = None),
244+
logo = Drawable(
245+
normal = "",
246+
small = None,
247+
large = None,
248+
xlarge = None
249+
),
250+
screenshots = List(
251+
Drawable(
252+
normal = "",
253+
large = None,
254+
small = None,
255+
xlarge = None
256+
),
257+
Drawable(
258+
normal = "",
259+
large = None,
260+
small = None,
261+
xlarge = None
262+
),
263+
Drawable(
264+
normal = "",
265+
large = None,
266+
small = None,
267+
xlarge = None
268+
)
269+
)
270+
)
271+
272+
val appInfoAuth: ApplicationInfo = ApplicationInfo(
273+
version = Version(1, 0, 0),
274+
updateNotes = None,
275+
published = true,
276+
name = "Notables",
277+
headline = "All your words",
278+
description = descriptionAuth,
279+
hmiDescription = None,
280+
termsUrl = "https://example.com/terms",
281+
privacyPolicyUrl = None,
282+
dataUsePurpose = "Data Will be processed by Notables for the following purpose...",
283+
supportContact = "[email protected]",
284+
rating = None,
285+
dataPreview = dataPreviewAuth,
286+
graphics = graphicsAuth,
287+
primaryColor = None,
288+
callbackUrl = None
289+
)
290+
291+
val developerAuth: ApplicationDeveloper = ApplicationDeveloper(
292+
id = "dex",
293+
name = "HATDeX",
294+
url = "https://hatdex.org",
295+
country = Some("United Kingdom"),
296+
logo = Some(
297+
Drawable(
298+
normal =
299+
"https://s3-eu-west-1.amazonaws.com/hubofallthings-com-dexservi-dexpublicassetsbucket-kex8hb7fsdge/notablesapp/0x0ss.png",
300+
small = None,
301+
large = None,
302+
xlarge = None
303+
)
304+
)
305+
)
306+
307+
val permissionsAuth: ApplicationPermissions = ApplicationPermissions(
308+
rolesGranted = List(
309+
UserRole.userRoleDeserialize("namespacewrite", Some("rumpel")),
310+
UserRole.userRoleDeserialize("namespaceread", Some("rumpel")),
311+
UserRole.userRoleDeserialize("datadebit", Some("app-notables"))
312+
),
313+
dataRetrieved = None,
314+
dataRequired = None
315+
)
316+
317+
val setupAuth: ApplicationSetup.External = ApplicationSetup.External(
318+
url = None,
319+
iosUrl = None,
320+
androidUrl = None,
321+
testingUrl = None,
322+
validRedirectUris = List("https://api.onezero-me.com/"),
323+
deauthorizeCallbackUrl = None,
324+
onboarding = None,
325+
preferences = None,
326+
dependencies = None
327+
)
328+
329+
val appStatusAuth: ApplicationStatus.Internal = ApplicationStatus.Internal(
330+
compatibility = Version(1, 0, 0),
331+
dataPreviewEndpoint = None,
332+
staticDataPreviewEndpoint = None,
333+
recentDataCheckEndpoint = Some("/rumpel/notablesv1"),
334+
versionReleaseDate = DateTime.parse("2018-07-24T12:00:00")
335+
)
336+
337+
val notablesAppAuth: Application =
338+
Application(
339+
id = "notablesAuth",
340+
kind = kindAuth,
341+
info = appInfoAuth,
342+
developer = developerAuth,
343+
permissions = permissionsAuth,
344+
dependencies = None,
345+
setup = setupAuth,
346+
status = appStatusAuth
347+
)
348+
207349
def databaseReady(): Future[Unit] = {
208350
val schemaMigration = new HatDbSchemaMigration(application.configuration, db, global)
209351
schemaMigration
@@ -253,10 +395,10 @@ mO9kGhALaD5okBcI/VuAQiFvBXdK0ii/nVcBApXEu47PG4oYUgPI
253395

254396
class EmptyAppProviderModule extends ScalaModule {
255397
override def configure(): Unit =
256-
bind[TrustedApplicationProvider].toInstance(new TestApplicationProvider(Seq()))
398+
bind[TrustedApplicationProvider].toInstance(new TestApplicationProvider(Seq(notablesAppAuth)))
257399
}
258400

259-
lazy val application: Application =
401+
lazy val application: PlayApplication =
260402
new GuiceApplicationBuilder()
261403
.configure(conf)
262404
.overrides(new IntegrationSpecModule)

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

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ import java.util.UUID
4444
import scala.concurrent.ExecutionContext.Implicits.global
4545
import scala.concurrent.duration._
4646
import scala.concurrent.{ Await, Future }
47+
import org.hatdex.hat.phata.models.ApiVerificationRequest
4748

4849
class AuthenticationSpec extends AuthenticationContext {
4950

@@ -190,7 +191,6 @@ class AuthenticationSpec extends AuthenticationContext {
190191
val result = maybeResult.get
191192

192193
status(result) must equal(BAD_REQUEST)
193-
// contentType(result) must beSome("application/json")
194194
(contentAsJson(result) \ "error").as[String] must equal("Bad Request")
195195
}
196196

@@ -228,7 +228,6 @@ class AuthenticationSpec extends AuthenticationContext {
228228
val result: Future[Result] = Helpers.call(controller.handleForgotPassword, request)
229229

230230
status(result) must equal(OK)
231-
//there was one(mockMailer).passwordReset(any[String], any[String])(any[MessagesApi], any[Lang], any[HatServer])
232231
}
233232

234233
"The `handleResetPassword` method" should "Return status 401 if no such token exists" in {
@@ -317,6 +316,29 @@ class AuthenticationSpec extends AuthenticationContext {
317316
status(result) must equal(UNAUTHORIZED)
318317
(contentAsJson(result) \ "cause").as[String] must equal("No user matching token")
319318
}
319+
320+
"The `handleVerificationRequest` method" should "flexibly handle matching redirectURL" in {
321+
val request = FakeRequest("POST", "http://hat.hubofallthings.net")
322+
.withAuthenticator(owner.loginInfo)
323+
.withJsonBody(Json.toJson(apiVerificationRequestMatching))
324+
325+
val controller = application.injector.instanceOf[Authentication]
326+
val result: Future[Result] = Helpers.call(controller.handleVerificationRequest(None), request)
327+
328+
status(result) must equal(OK)
329+
}
330+
331+
it should "not match a different redirect URL base" in {
332+
val request = FakeRequest("POST", "http://hat.hubofallthings.net")
333+
.withAuthenticator(owner.loginInfo)
334+
.withJsonBody(Json.toJson(apiVerificationRequestNotMatching))
335+
336+
val controller = application.injector.instanceOf[Authentication]
337+
val result: Future[Result] = Helpers.call(controller.handleVerificationRequest(None), request)
338+
339+
status(result) must equal(OK)
340+
}
341+
320342
}
321343

322344
class AuthenticationContext extends HATTestContext {
@@ -332,4 +354,10 @@ class AuthenticationContext extends HATTestContext {
332354

333355
val passwordValidationIncorrect: ApiValidationRequest = ApiValidationRequest("[email protected]", "appId")
334356
val passwordValidationOwner: ApiValidationRequest = ApiValidationRequest("[email protected]", "appId")
357+
358+
val apiVerificationRequestMatching: ApiVerificationRequest =
359+
ApiVerificationRequest("notablesAuth", "[email protected]", "https://api.onezero-me.com/auth/redirectfromhat/scoring/facebook/avante/aruz3sc/8e4df03ffdffe0034da0c9008813f2a7:fd43cdc55b52fa31b47fa797ceef5424d52d269f5c3ed8b82858112c1a098a56a6689e101e5c8d499bc79eb28fe61b4d32ed2a9686727bd6f8f93e987b7a4e79")
360+
361+
val apiVerificationRequestNotMatching: ApiVerificationRequest =
362+
ApiVerificationRequest("notablesAuth", "[email protected]", "https://api2.onezero-me.com/auth/redirectfromhat/scoring/facebook/avante/aruz3sc/8e4df03ffdffe0034da0c9008813f2a7:fd43cdc55b52fa31b47fa797ceef5424d52d269f5c3ed8b82858112c1a098a56a6689e101e5c8d499bc79eb28fe61b4d32ed2a9686727bd6f8f93e987b7a4e79")
335363
}

0 commit comments

Comments
 (0)