24
24
25
25
package org .hatdex .hat .api .controllers
26
26
27
+ import java .net .URLDecoder
27
28
import javax .inject .Inject
28
29
29
30
import com .mohiva .play .silhouette .api .repositories .AuthInfoRepository
30
31
import com .mohiva .play .silhouette .api .util .{ Clock , Credentials , PasswordHasherRegistry }
31
32
import com .mohiva .play .silhouette .api .{ LoginEvent , Silhouette }
32
- import com .mohiva .play .silhouette .impl .exceptions .InvalidPasswordException
33
+ import com .mohiva .play .silhouette .impl .exceptions .{ IdentityNotFoundException , InvalidPasswordException }
33
34
import com .mohiva .play .silhouette .impl .providers .CredentialsProvider
34
35
import org .hatdex .hat .api .json .HatJsonFormats
35
- import org .hatdex .hat .api .models .{ ErrorMessage , SuccessResponse }
36
- import org .hatdex .hat .api .service .UsersService
36
+ import org .hatdex .hat .api .models ._
37
+ import org .hatdex .hat .api .service .{ HatServicesService , MailTokenService , UsersService }
37
38
import org .hatdex .hat .authentication ._
38
39
import org .hatdex .hat .phata .models .{ ApiPasswordChange , ApiPasswordResetRequest , MailTokenUser }
39
- import org .hatdex .hat .phata .service .{ HatServicesService , MailTokenService }
40
40
import org .hatdex .hat .resourceManagement .{ HatServerProvider , _ }
41
41
import org .hatdex .hat .utils .{ HatBodyParsers , HatMailer }
42
42
import play .api .i18n .MessagesApi
@@ -64,7 +64,16 @@ class Authentication @Inject() (
64
64
65
65
private val logger = Logger (this .getClass)
66
66
67
- def hatLogin (name : String , redirectUrl : String ): Action [AnyContent ] = SecuredAction (WithRole (" owner" )).async { implicit request =>
67
+ def publicKey (): Action [AnyContent ] = UserAwareAction .async { implicit request =>
68
+ val publicKey = hatServerProvider.toString(request.dynamicEnvironment.publicKey)
69
+ Future .successful(Ok (publicKey))
70
+ }
71
+
72
+ def validateToken (): Action [AnyContent ] = SecuredAction .async { implicit request =>
73
+ Future .successful(Ok (Json .toJson(SuccessResponse (" Authenticated" ))))
74
+ }
75
+
76
+ def hatLogin (name : String , redirectUrl : String ): Action [AnyContent ] = SecuredAction (WithRole (Owner ())).async { implicit request =>
68
77
for {
69
78
service <- hatServicesService.findOrCreateHatService(name, redirectUrl)
70
79
linkedService <- hatServicesService.hatServiceLink(request.identity, service, Some (redirectUrl))
@@ -74,7 +83,55 @@ class Authentication @Inject() (
74
83
}
75
84
}
76
85
77
- def passwordChangeProcess : Action [ApiPasswordChange ] = SecuredAction (WithRole (" owner" )).async(parsers.json[ApiPasswordChange ]) { implicit request =>
86
+ def applicationToken (name : String , resource : String ): Action [AnyContent ] = SecuredAction (WithRole (Owner ())).async { implicit request =>
87
+ for {
88
+ service <- hatServicesService.findOrCreateHatService(name, resource)
89
+ token <- hatServicesService.hatServiceToken(request.identity, service)
90
+ result <- env.authenticatorService.embed(token.accessToken, Ok (Json .toJson(token)))
91
+ } yield {
92
+ result
93
+ }
94
+ }
95
+
96
+ private val hatService = HatService (
97
+ " hat" , " hat" , " HAT API" ,
98
+ " " , " " , " " ,
99
+ browser = true ,
100
+ category = " api" ,
101
+ setup = true ,
102
+ loginAvailable = true )
103
+ def accessToken (): Action [AnyContent ] = UserAwareAction .async { implicit request =>
104
+ val eventuallyAuthenticatedUser = for {
105
+ usernameParam <- request.getQueryString(" username" ).orElse(request.headers.get(" username" ))
106
+ passwordParam <- request.getQueryString(" password" ).orElse(request.headers.get(" password" ))
107
+ } yield {
108
+ val username = usernameParam
109
+ val password = URLDecoder .decode(passwordParam, " UTF-8" )
110
+ logger.info(s " Authenticating $username: $password" )
111
+ credentialsProvider.authenticate(Credentials (username, password))
112
+ .flatMap { loginInfo =>
113
+ usersService.getUser(loginInfo.providerKey).flatMap {
114
+ case Some (user) =>
115
+ val customClaims = hatServicesService.generateUserTokenClaims(user, hatService)
116
+ for {
117
+ authenticator <- env.authenticatorService.create(loginInfo)
118
+ token <- env.authenticatorService.init(authenticator.copy(customClaims = Some (customClaims)))
119
+ _ <- usersService.logLogin(user, " api" , user.roles.filter(_.extra.isEmpty).map(_.title).mkString(" :" ), None , None )
120
+ result <- env.authenticatorService.embed(token, Ok (Json .toJson(AccessToken (token, user.userId))))
121
+ } yield {
122
+ env.eventBus.publish(LoginEvent (user, request))
123
+ result
124
+ }
125
+ case None => Future .failed(new IdentityNotFoundException (" Couldn't find user" ))
126
+ }
127
+ }
128
+ }
129
+ eventuallyAuthenticatedUser getOrElse {
130
+ Future .successful(Unauthorized (Json .toJson(ErrorMessage (" Credentials required" , " No username or password provided to retrieve token" ))))
131
+ }
132
+ }
133
+
134
+ def passwordChangeProcess : Action [ApiPasswordChange ] = SecuredAction (WithRole (Owner ())).async(parsers.json[ApiPasswordChange ]) { implicit request =>
78
135
request.body.password map { oldPassword =>
79
136
val eventualResult = for {
80
137
_ <- credentialsProvider.authenticate(Credentials (request.identity.email, oldPassword))
@@ -102,11 +159,10 @@ class Authentication @Inject() (
102
159
val email = request.body.email
103
160
val response = Ok (Json .toJson(SuccessResponse (" If the email you have entered is correct, you will shortly receive an email with password reset instructions" )))
104
161
if (email == request.dynamicEnvironment.ownerEmail) {
105
- usersService.listUsers.map(_.find(_.role == " owner " )).flatMap {
162
+ usersService.listUsers.map(_.find(_.roles.contains( Owner ()) )).flatMap {
106
163
case Some (user) =>
107
164
val token = MailTokenUser (email, isSignUp = false )
108
165
tokenService.create(token).map { _ =>
109
- // TODO generate password reset link for frontend to handle
110
166
val scheme = if (request.secure) {
111
167
" https://"
112
168
}
@@ -134,7 +190,7 @@ class Authentication @Inject() (
134
190
tokenService.retrieve(tokenId).flatMap {
135
191
case Some (token) if ! token.isSignUp && ! token.isExpired =>
136
192
if (token.email == request.dynamicEnvironment.ownerEmail) {
137
- usersService.listUsers.map(_.find(_.role == " owner " )).flatMap {
193
+ usersService.listUsers.map(_.find(_.roles.contains( Owner ()) )).flatMap {
138
194
case Some (user) =>
139
195
for {
140
196
_ <- authInfoRepository.update(user.loginInfo, passwordHasherRegistry.current.hash(request.body.newPassword))
@@ -150,6 +206,7 @@ class Authentication @Inject() (
150
206
}
151
207
}
152
208
else {
209
+ logger.info(s " Token email: ${token.email}, while owner email is ${request.dynamicEnvironment.ownerEmail}" )
153
210
Future .successful(Unauthorized (Json .toJson(ErrorMessage (" Password reset unauthorized" , " Only HAT owner can reset their password" ))))
154
211
}
155
212
case Some (_) =>
0 commit comments