Skip to content

Commit f32b27a

Browse files
author
Leonardo Menezes
committed
Merge branch 'develop'
2 parents 635f0ff + a664f7f commit f32b27a

61 files changed

Lines changed: 1491 additions & 967 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,3 +71,38 @@ hosts: [
7171
}
7272
]
7373
```
74+
75+
### User authentication
76+
It is possible to control access to cerebro through authentication.
77+
78+
At the moment, there are no roles/permission level, and either an user is able to access or not.
79+
80+
There are two methods of authentication:
81+
82+
- Basic: username and password on the configuration file
83+
- LDAP: connect to an external provider for authentication
84+
85+
Example for basic:
86+
```yaml
87+
auth {
88+
type: basic
89+
settings: {
90+
username = "admin"
91+
password = "1234"
92+
}
93+
}
94+
```
95+
96+
Example for LDAP:
97+
98+
```yaml
99+
auth {
100+
type: ldap
101+
settings: {
102+
url = "ldap://host:port"
103+
base-dn = "ou=active,ou=Employee"
104+
method = "simple"
105+
user-domain = "domain.com"
106+
}
107+
}
108+
```

app/controllers/AliasesController.scala

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,27 @@
11
package controllers
22

3-
import models.{Aliases, ElasticServer}
3+
import javax.inject.Inject
4+
5+
import controllers.auth.AuthenticationModule
6+
import elastic.ElasticClient
7+
import models.{Aliases, CerebroResponse}
48
import play.api.libs.json.JsArray
9+
510
import scala.concurrent.ExecutionContext.Implicits.global
611

7-
class AliasesController extends BaseController {
12+
class AliasesController @Inject()(val authentication: AuthenticationModule,
13+
client: ElasticClient) extends BaseController {
814

9-
def getAliases = process { (request, client) =>
10-
client.getAliases(ElasticServer(request.host, request.authentication)).map { aliases =>
11-
Status(aliases.status)(Aliases(aliases.body))
15+
def getAliases = process { request =>
16+
client.getAliases(request.target).map { aliases =>
17+
CerebroResponse(aliases.status, Aliases(aliases.body))
1218
}
1319
}
1420

15-
def updateAliases = process { (request, client) =>
21+
def updateAliases = process { request =>
1622
val changes = request.getOptArray("changes").getOrElse(JsArray()).value
17-
client.updateAliases(changes, ElasticServer(request.host, request.authentication)).map { aliases =>
18-
Status(aliases.status)(aliases.body)
23+
client.updateAliases(changes, request.target).map { aliases =>
24+
CerebroResponse(aliases.status, aliases.body)
1925
}
2026
}
2127

app/controllers/AnalysisController.scala

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,52 @@
11
package controllers
22

3+
import javax.inject.Inject
4+
5+
import controllers.auth.AuthenticationModule
6+
import elastic.ElasticClient
7+
import models.{CerebroResponse, ElasticServer}
38
import models.analysis.{IndexAnalyzers, IndexFields, OpenIndices, Tokens}
4-
import models.ElasticServer
59

610
import scala.concurrent.ExecutionContext.Implicits.global
711

8-
class AnalysisController extends BaseController {
12+
class AnalysisController @Inject()(val authentication: AuthenticationModule,
13+
client: ElasticClient) extends BaseController {
914

10-
def getIndices = process { (request, client) =>
11-
client.getIndices(ElasticServer(request.host, request.authentication)).map { response =>
12-
Status(response.status)(OpenIndices(response.body))
15+
def getIndices = process { request =>
16+
client.getIndices(request.target).map { response =>
17+
CerebroResponse(response.status, OpenIndices(response.body))
1318
}
1419
}
1520

16-
def getIndexAnalyzers = process { (request, client) =>
21+
def getIndexAnalyzers = process { request =>
1722
val index = request.get("index")
18-
client.getIndexSettings(index, ElasticServer(request.host, request.authentication)).map { response =>
19-
Status(response.status)(IndexAnalyzers(index, response.body))
23+
client.getIndexSettings(index, request.target).map { response =>
24+
CerebroResponse(response.status, IndexAnalyzers(index, response.body))
2025
}
2126
}
2227

23-
def getIndexFields = process { (request, client) =>
28+
def getIndexFields = process { request =>
2429
val index = request.get("index")
25-
client.getIndexMapping(index, ElasticServer(request.host, request.authentication)).map { response =>
26-
Status(response.status)(IndexFields(index, response.body))
30+
client.getIndexMapping(index, request.target).map { response =>
31+
CerebroResponse(response.status, IndexFields(index, response.body))
2732
}
2833
}
2934

30-
def analyzeByField = process { (request, client) =>
35+
def analyzeByField = process { request =>
3136
val index = request.get("index")
3237
val field = request.get("field")
3338
val text = request.get("text")
34-
client.analyzeTextByField(index, field, text, ElasticServer(request.host, request.authentication)).map { response =>
35-
Status(response.status)(Tokens(response.body))
39+
client.analyzeTextByField(index, field, text, request.target).map { response =>
40+
CerebroResponse(response.status, Tokens(response.body))
3641
}
3742
}
3843

39-
def analyzeByAnalyzer = process { (request, client) =>
44+
def analyzeByAnalyzer = process { request =>
4045
val index = request.get("index")
4146
val analyzer = request.get("analyzer")
4247
val text = request.get("text")
43-
client.analyzeTextByAnalyzer(index, analyzer, text, ElasticServer(request.host, request.authentication)).map { response =>
44-
Status(response.status)(Tokens(response.body))
48+
client.analyzeTextByAnalyzer(index, analyzer, text, request.target).map { response =>
49+
CerebroResponse(response.status, Tokens(response.body))
4550
}
4651
}
4752

app/controllers/Application.scala

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
package controllers
22

3-
import play.api.mvc.{Action, Controller}
4-
import play.api.http.MimeTypes
3+
import com.google.inject.Inject
4+
import controllers.auth.AuthenticationModule
5+
import play.api.mvc.Controller
56

6-
object Application extends Controller {
7+
class Application @Inject()(val authentication: AuthenticationModule) extends Controller with AuthSupport {
78

8-
def index = Action {
9+
def index = AuthAction(authentication, true) { request =>
910
Ok(views.html.Index())
1011
}
1112

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package controllers
2+
3+
import javax.inject.{Inject, Singleton}
4+
5+
import akka.actor.ActorSystem
6+
import controllers.auth.{AuthAction, AuthenticationModule}
7+
import forms.LoginForm
8+
import play.api.mvc.{Action, Controller}
9+
10+
@Singleton
11+
class AuthController @Inject()(system: ActorSystem, authentication: AuthenticationModule) extends Controller {
12+
13+
import AuthController._
14+
15+
private val badFormMsg = "invalid login form data"
16+
17+
def index = Action { implicit request =>
18+
if (authentication.isEnabled) {
19+
request.session.get(AuthAction.SESSION_USER).map { user =>
20+
request.session.get(AuthAction.REDIRECT_URL) match {
21+
case Some(url) =>
22+
Redirect(url, play.api.http.Status.SEE_OTHER)
23+
case None =>
24+
Redirect(routes.Application.index())
25+
}
26+
}.getOrElse {
27+
Ok(views.html.auth.login())
28+
}
29+
} else {
30+
Redirect(routes.Application.index())
31+
}
32+
}
33+
34+
def login = Action { implicit request =>
35+
LoginForm.form.bindFromRequest.fold(
36+
formWithErrors => {
37+
log.error(badFormMsg)
38+
BadRequest(badFormMsg)
39+
},
40+
creds => {
41+
authentication.authentication(creds.user, creds.password) match {
42+
case Some(username) =>
43+
val resp =
44+
request.session.get(AuthAction.REDIRECT_URL) match {
45+
case Some(url) => Redirect(url, play.api.http.Status.SEE_OTHER)
46+
case None => Redirect(routes.Application.index())
47+
}
48+
resp.withSession(AuthAction.SESSION_USER -> username)
49+
case None =>
50+
Redirect(routes.AuthController.index).flashing(LOGIN_MSG -> "wrong user and/or password")
51+
}
52+
}
53+
)
54+
}
55+
56+
def logout = Action {
57+
request =>
58+
Redirect("/login").withNewSession
59+
}
60+
61+
}
62+
63+
object AuthController {
64+
private val log = org.slf4j.LoggerFactory.getLogger(classOf[AuthController])
65+
66+
final val LOGIN_MSG = "login-msg"
67+
}

app/controllers/AuthSupport.scala

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package controllers
2+
3+
import controllers.auth.{AuthAction, AuthenticationModule}
4+
import play.api.mvc.Controller
5+
6+
trait AuthSupport { self: Controller =>
7+
8+
def AuthAction(authentication: AuthenticationModule, redirect: Boolean = false): AuthAction =
9+
new AuthAction(authentication, redirect)
10+
11+
}

app/controllers/BaseController.scala

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,31 @@
11
package controllers
22

3-
import elastic.ElasticClient
3+
import controllers.auth.AuthenticationModule
44
import exceptions.MissingRequiredParamException
5-
import models.CerebroRequest
5+
import models.{CerebroRequest, CerebroResponse}
66
import play.api.Logger
77
import play.api.libs.json.Json
8-
import play.api.mvc.{Action, Controller, Result}
8+
import play.api.mvc.{Controller, Result}
99

1010
import scala.concurrent.Future
1111
import scala.util.control.NonFatal
1212

13-
trait BaseController extends Controller {
13+
trait BaseController extends Controller with AuthSupport {
1414

15-
val client: ElasticClient = ElasticClient
15+
val authentication: AuthenticationModule
1616

1717
protected val logger = Logger("elastic")
1818

19-
type RequestProcessor = (CerebroRequest, ElasticClient) => Future[Result]
19+
type RequestProcessor = (CerebroRequest) => Future[Result]
2020

21-
final def process(processor: RequestProcessor) = Action.async(parse.json) { request =>
21+
final def process(processor: RequestProcessor) = AuthAction(authentication).async(parse.json) { request =>
2222
try {
23-
processor(CerebroRequest(request.body), client)
23+
processor(CerebroRequest(request))
2424
} catch {
2525
case e: MissingRequiredParamException =>
26-
Future.successful(Status(400)(Json.obj("error" -> e.getMessage))) // FIXME: proper error handling
26+
Future.successful(CerebroResponse(400, Json.obj("error" -> e.getMessage))) // FIXME: proper error handling
2727
case NonFatal(e) =>
28-
Future.successful(Status(500)(Json.obj("error" -> "Error"))) // FIXME: proper error handling
28+
Future.successful(CerebroResponse(500, Json.obj("error" -> "Error"))) // FIXME: proper error handling
2929
}
3030
}
3131

app/controllers/CatController.scala

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
11
package controllers
22

3-
import models.analysis.{IndexAnalyzers, IndexFields, OpenIndices, Tokens}
4-
import models.ElasticServer
3+
import javax.inject.Inject
4+
5+
import controllers.auth.AuthenticationModule
6+
import elastic.ElasticClient
7+
import models.{CerebroResponse, ElasticServer}
58

69
import scala.concurrent.ExecutionContext.Implicits.global
710

8-
class CatController extends BaseController {
11+
class CatController @Inject()(val authentication: AuthenticationModule,
12+
client: ElasticClient) extends BaseController {
913

10-
def get = process { (request, client) =>
14+
def get = process { request =>
1115
val api = request.get("api")
12-
client.catRequest(api, ElasticServer(request.host, request.authentication)).map { response =>
13-
Status(response.status)(response.body)
16+
client.catRequest(api, request.target).map { response =>
17+
CerebroResponse(response.status, response.body)
1418
}
1519
}
1620

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,30 @@
11
package controllers
22

3-
import models.ElasticServer
3+
import javax.inject.Inject
4+
5+
import controllers.auth.AuthenticationModule
6+
import elastic.ElasticClient
7+
import models.{CerebroResponse, ElasticServer}
48
import models.commons.{Indices, Nodes}
59
import play.api.libs.json.Json
610

7-
import scala.concurrent.Future
811
import scala.concurrent.ExecutionContext.Implicits.global
12+
import scala.concurrent.Future
913

10-
class ClusterChangesController extends BaseController {
14+
class ClusterChangesController @Inject()(val authentication: AuthenticationModule,
15+
client: ElasticClient) extends BaseController {
1116

12-
def get = process { (request, client) =>
17+
def get = process { request =>
1318
Future.sequence(Seq(
14-
client.getIndices(ElasticServer(request.host, request.authentication)),
15-
client.getNodes(ElasticServer(request.host, request.authentication)),
16-
client.main(ElasticServer(request.host, request.authentication))
19+
client.getIndices(request.target),
20+
client.getNodes(request.target),
21+
client.main(request.target)
1722
)).map { responses =>
1823
Json.obj(
1924
"indices" -> Indices(responses(0).body),
2025
"nodes" -> Nodes(responses(1).body),
2126
"cluster_name" -> (responses(2).body \ "cluster_name").as[String]
2227
)
23-
}.map(Ok(_))
28+
}.map(CerebroResponse(200, _))
2429
}
2530
}

0 commit comments

Comments
 (0)