Skip to content

Commit 8ce9788

Browse files
authored
Merge pull request #41 from delphi-hub/feature/authentication
Introduced JWT based authentication for communication with registry
2 parents 2af18c1 + 90cc6e3 commit 8ce9788

File tree

5 files changed

+52
-11
lines changed

5 files changed

+52
-11
lines changed

build.sbt

+2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ libraryDependencies ++= Seq(
2424
"com.sksamuel.elastic4s" %% "elastic4s-http-streams" % elastic4sVersion,
2525
)
2626

27+
libraryDependencies += "com.pauldijou" %% "jwt-core" % "1.0.0"
28+
2729
libraryDependencies += "org.parboiled" %% "parboiled" % "2.1.4"
2830
libraryDependencies += "io.spray" %% "spray-json" % "1.3.3"
2931
libraryDependencies += "org.scalactic" %% "scalactic" % "3.0.4"

src/main/scala/de/upb/cs/swt/delphi/instancemanagement/InstanceRegistry.scala

+11-5
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,11 @@ import java.net.InetAddress
2020

2121
import akka.http.scaladsl.Http
2222
import akka.http.scaladsl.model._
23+
import akka.http.scaladsl.model.headers.RawHeader
2324
import akka.http.scaladsl.unmarshalling.Unmarshal
2425
import akka.util.ByteString
2526
import de.upb.cs.swt.delphi.instancemanagement.InstanceEnums.{ComponentType, InstanceState}
27+
import de.upb.cs.swt.delphi.webapi.authorization.AuthProvider
2628
import de.upb.cs.swt.delphi.webapi.{AppLogging, Configuration, _}
2729
import spray.json._
2830

@@ -92,7 +94,10 @@ object InstanceRegistry extends InstanceJsonSupport with AppLogging {
9294
method = HttpMethods.POST,
9395
configuration.instanceRegistryUri + ReportOperationType.toOperationUriString(operationType, id))
9496

95-
Await.result(Http(system).singleRequest(request) map { response =>
97+
val useGenericNameForToken = operationType == ReportOperationType.Start //Must use generic name for startup, no id known at that point
98+
99+
Await.result(Http(system).singleRequest(request.withHeaders(RawHeader("Authorization",
100+
s"Bearer ${AuthProvider.generateJwt(useGenericName = useGenericNameForToken)}"))) map { response =>
96101
if (response.status == StatusCodes.OK) {
97102
log.info(s"Successfully reported ${operationType.toString} to Instance Registry.")
98103
Success()
@@ -146,7 +151,7 @@ object InstanceRegistry extends InstanceJsonSupport with AppLogging {
146151
configuration.instanceRegistryUri +
147152
s"/matchingInstance?Id=${configuration.assignedID.getOrElse(-1)}&ComponentType=ElasticSearch")
148153

149-
Await.result(Http(system).singleRequest(request) map { response =>
154+
Await.result(Http(system).singleRequest(request.withHeaders(RawHeader("Authorization",s"Bearer ${AuthProvider.generateJwt()}"))) map { response =>
150155
response.status match {
151156
case StatusCodes.OK =>
152157
try {
@@ -189,7 +194,7 @@ object InstanceRegistry extends InstanceJsonSupport with AppLogging {
189194
configuration.instanceRegistryUri +
190195
s"/matchingResult?CallerId=${configuration.assignedID.getOrElse(-1)}&MatchedInstanceId=$idToPost&MatchingSuccessful=$isElasticSearchReachable")
191196

192-
Await.result(Http(system).singleRequest(request) map { response =>
197+
Await.result(Http(system).singleRequest(request.withHeaders(RawHeader("Authorization",s"Bearer ${AuthProvider.generateJwt()}"))) map { response =>
193198
if (response.status == StatusCodes.OK) {
194199
log.info(s"Successfully posted matching result to Instance Registry.")
195200
Success()
@@ -216,7 +221,7 @@ object InstanceRegistry extends InstanceJsonSupport with AppLogging {
216221

217222
val request = HttpRequest(method = HttpMethods.POST, configuration.instanceRegistryUri + s"/deregister?Id=$id")
218223

219-
Await.result(Http(system).singleRequest(request) map { response =>
224+
Await.result(Http(system).singleRequest(request.withHeaders(RawHeader("Authorization",s"Bearer ${AuthProvider.generateJwt()}"))) map { response =>
220225
if (response.status == StatusCodes.OK) {
221226
log.info("Successfully deregistered from Instance Registry.")
222227
Success()
@@ -237,7 +242,8 @@ object InstanceRegistry extends InstanceJsonSupport with AppLogging {
237242
def postInstance(instance: Instance, uri: String)(): Future[HttpResponse] = {
238243
try {
239244
val request = HttpRequest(method = HttpMethods.POST, uri = uri, entity = instance.toJson(instanceFormat).toString())
240-
Http(system).singleRequest(request)
245+
//Use generic name for startup, no id present at this point
246+
Http(system).singleRequest(request.withHeaders(RawHeader("Authorization",s"Bearer ${AuthProvider.generateJwt(useGenericName = true)}")))
241247
} catch {
242248
case dx: DeserializationException =>
243249
log.warning(s"Failed to deregister to Instance Registry, exception: $dx")

src/main/scala/de/upb/cs/swt/delphi/webapi/Configuration.scala

+2
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,8 @@ class Configuration( //Server and Elasticsearch configuration
8888
}
8989
lazy val instanceId: Option[Long] = InstanceRegistry.handleInstanceStart(configuration = this)
9090

91+
val jwtSecretKey: String = sys.env.getOrElse("DELPHI_JWT_SECRET","changeme")
92+
9193
}
9294

9395

src/main/scala/de/upb/cs/swt/delphi/webapi/Server.scala

+2-6
Original file line numberDiff line numberDiff line change
@@ -31,17 +31,13 @@ object Server extends HttpApp with JsonSupport with AppLogging {
3131

3232

3333
def main(args: Array[String]): Unit = {
34-
sys.addShutdownHook({
35-
log.warning("Received shutdown signal.")
36-
InstanceRegistry.handleInstanceStop(configuration)
37-
})
3834

3935
StartupCheck.check(configuration)
4036
Server.startServer(configuration.bindHost, configuration.bindPort, system)
4137

42-
val terminationFuture = system.terminate()
38+
InstanceRegistry.handleInstanceStop(configuration)
4339

44-
terminationFuture.onComplete {
40+
system.terminate().onComplete{
4541
sys.exit(0)
4642
}
4743
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Copyright (C) 2018 The Delphi Team.
2+
// See the LICENCE file distributed with this work for additional
3+
// information regarding copyright ownership.
4+
//
5+
// Licensed under the Apache License, Version 2.0 (the "License");
6+
// you may not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing, software
12+
// distributed under the License is distributed on an "AS IS" BASIS,
13+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
// See the License for the specific language governing permissions and
15+
// limitations under the License.
16+
package de.upb.cs.swt.delphi.webapi.authorization
17+
18+
import de.upb.cs.swt.delphi.webapi
19+
import pdi.jwt.{Jwt, JwtAlgorithm, JwtClaim}
20+
21+
object AuthProvider {
22+
23+
def generateJwt(validFor: Long = 1, useGenericName: Boolean = false): String = {
24+
val claim = JwtClaim()
25+
.issuedNow
26+
.expiresIn(validFor * 60)
27+
.startsNow
28+
. + ("user_id", if (useGenericName) webapi.configuration.instanceName else s"${webapi.configuration.assignedID.get}")
29+
. + ("user_type", "Component")
30+
31+
32+
Jwt.encode(claim, webapi.configuration.jwtSecretKey, JwtAlgorithm.HS256)
33+
}
34+
35+
}

0 commit comments

Comments
 (0)