Skip to content

Commit ca8dedf

Browse files
Merge pull request #164 from fullfacing/develop
Release 2.1.0
2 parents 83486fb + bba3f63 commit ca8dedf

File tree

23 files changed

+172
-128
lines changed

23 files changed

+172
-128
lines changed

.circleci/config.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ jobs:
33
test_213:
44
working_directory: /code
55
docker:
6-
- image: openjdk:10
6+
- image: openjdk:11
77
- image: jboss/keycloak:latest
88
environment:
99
KEYCLOAK_USER: "admin"
@@ -40,7 +40,7 @@ jobs:
4040
test_212:
4141
working_directory: /code
4242
docker:
43-
- image: openjdk:10
43+
- image: openjdk:11
4444
- image: jboss/keycloak:latest
4545
environment:
4646
KEYCLOAK_USER: "admin"

CHANGELOG.md

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,20 @@ All notable changes to this project will be documented in this file.
33

44
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
55

6+
## [2.1.0] - 2020-03-28
7+
### Changed
8+
- updated various dependencies
9+
### Fixed
10+
- deprecation warnings for Scala Test
11+
12+
## [2.0.2] - 2020-02-13
13+
### Changed
14+
- `issueAccessToken` function in `TokenManager` made public.
15+
### Fixed
16+
- Renamed `RequiredAction` case class to `AuthRequiredAction` due to naming conflict with an enum.
17+
- `User` case class no longer uses incorrect type for `requiredActions` field.
18+
- `Users.sendActionsEmail` - changed `actions` parameter to `List[RequiredAction]`.
19+
620
## [2.0.0] - 2019-12-06
721
#### The following changes were made to support Keycloak 8.0.0:
822
- Added `plainText` implicit def to `BodyMagnet` to support sending text/plain contentType.
@@ -16,10 +30,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
1630
- Fixed `User.Update` case class - made all fields optional and defaulted to `None`.
1731

1832
## [1.2.5] - 2019-11-27
19-
### Updated
20-
- SBT Plugins
21-
22-
## [1.2.4] - 2019-11-20
2333
### Changed
2434
- Dependency updates
2535
- Support for cross compiling added so that Scala 2.12 and 2.13 versions can be published.

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,10 @@ The project is split into the following modules, each as a separate dependency:
3232
## Installation
3333

3434
Each module can be pulled into a project separately using the following SBT dependencies:
35-
* keycloak4s-core: `"com.fullfacing" %% "keycloak4s-core" % "2.0.0"`
36-
* keycloak4s-admin: `"com.fullfacing" %% "keycloak4s-admin" % "2.0.0"`
37-
* keycloak4s-admin-monix: `"com.fullfacing" %% "keycloak4s-admin-monix" % "2.0.0"`
38-
* keycloak4s-auth-akka-http: `"com.fullfacing" %% "keycloak4s-auth-akka-http" % "2.0.0"`
35+
* keycloak4s-core: `"com.fullfacing" %% "keycloak4s-core" % "2.0.2"`
36+
* keycloak4s-admin: `"com.fullfacing" %% "keycloak4s-admin" % "2.0.2"`
37+
* keycloak4s-admin-monix: `"com.fullfacing" %% "keycloak4s-admin-monix" % "2.0.2"`
38+
* keycloak4s-auth-akka-http: `"com.fullfacing" %% "keycloak4s-auth-akka-http" % "2.0.2"`
3939

4040
The core module is a dependency for all other modules, and is automatically pulled in when using any other module.
4141

build.sbt

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import xerial.sbt.Sonatype.GitHubHosting
44

55
lazy val global = {
66
Seq(
7-
version := "2.0.2",
7+
version := "2.1.0",
88
scalaVersion := "2.13.1",
99
organization := "com.fullfacing",
1010
scalacOptions ++= (CrossVersion.partialVersion(scalaVersion.value) match {
@@ -89,16 +89,16 @@ val scalac212Opts = baseScalaOpts ++ Seq("-Ypartial-unification")
8989
// Library Versions //
9090
// ---------------------------------- //
9191
val akkaHttpVersion = "10.1.11"
92-
val akkaStreamsVersion = "2.6.3"
93-
val catsEffectVersion = "2.1.1"
94-
val catsCoreVersion = "2.1.0"
92+
val akkaStreamsVersion = "2.6.4"
93+
val catsEffectVersion = "2.1.2"
94+
val catsCoreVersion = "2.1.1"
9595
val enumeratumVersion = "1.5.15"
9696
val json4sVersion = "3.6.7"
9797
val logbackVersion = "1.2.3"
9898
val monixVersion = "3.1.0"
99-
val nimbusVersion = "8.6"
100-
val scalaTestVersion = "3.1.0"
101-
val sttpVersion = "1.7.2"
99+
val nimbusVersion = "8.11"
100+
val scalaTestVersion = "3.1.1"
101+
val sttpVersion = "2.0.6"
102102

103103
// -------------------------------------- //
104104
// Library Dependencies //
@@ -144,19 +144,19 @@ val scalaTest: Seq[ModuleID] = Seq(
144144
)
145145

146146
val sttpAkka: Seq[ModuleID] = Seq(
147-
"com.softwaremill.sttp" %% "akka-http-backend" % sttpVersion,
148-
"com.softwaremill.sttp" %% "core" % sttpVersion,
149-
"com.softwaremill.sttp" %% "json4s" % sttpVersion,
147+
"com.softwaremill.sttp.client" %% "akka-http-backend" % sttpVersion,
148+
"com.softwaremill.sttp.client" %% "core" % sttpVersion,
149+
"com.softwaremill.sttp.client" %% "json4s" % sttpVersion,
150150
"com.typesafe.akka" %% "akka-stream" % akkaStreamsVersion
151151
)
152152

153153
val sttpMonix: Seq[ModuleID] = Seq(
154-
"com.softwaremill.sttp" %% "core" % sttpVersion,
155-
"com.softwaremill.sttp" %% "json4s" % sttpVersion
154+
"com.softwaremill.sttp.client" %% "core" % sttpVersion,
155+
"com.softwaremill.sttp.client" %% "json4s" % sttpVersion
156156
)
157157

158158
val sttpAkkaMonix: Seq[ModuleID] = Seq(
159-
"com.fullfacing" %% "sttp-akka-monix" % "1.0.3"
159+
"com.fullfacing" %% "sttp-akka-monix" % "1.1.0"
160160
)
161161

162162
// --------------------------------------------- //
@@ -213,7 +213,7 @@ lazy val `keycloak4s-akka-http` = (project in file("./keycloak4s-auth/akka-http"
213213
// Project and configuration for keycloak4s-playground //
214214
// --------------------------------------------------- //
215215
lazy val `keycloak4s-playground` = (project in file("./keycloak4s-playground"))
216-
.settings(scalaVersion := "2.13.0")
216+
.settings(scalaVersion := "2.13.1")
217217
.settings(skip in publish := true)
218218
.settings(libraryDependencies ++= sttpAkkaMonix ++ scalaTest ++ akkaTestKit)
219219
.settings(coverageEnabled := false)

keycloak4s-admin-monix/src/main/scala/com/fullfacing/keycloak4s/admin/monix/client/KeycloakClient.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@ import cats.implicits._
44
import com.fullfacing.keycloak4s.admin.client.{KeycloakClient => KeycloakClientA}
55
import com.fullfacing.keycloak4s.admin.monix.utilities.ObservableExtensions.ObservableExtensions
66
import com.fullfacing.keycloak4s.core.models._
7-
import com.softwaremill.sttp.SttpBackend
8-
import com.softwaremill.sttp.Uri.QueryFragment.KeyValue
97
import monix.eval.Task
108
import monix.reactive.Observable
9+
import sttp.client.{NothingT, SttpBackend}
10+
import sttp.model.Uri.QuerySegment.KeyValue
1111

1212
import scala.collection.immutable.Seq
1313
import scala.reflect._
1414

15-
class KeycloakClient[T](config: ConfigWithAuth)(implicit client: SttpBackend[Task, Observable[T]]) extends KeycloakClientA[Task, Observable[T]](config) {
15+
class KeycloakClient[T](config: ConfigWithAuth)(implicit client: SttpBackend[Task, Observable[T], NothingT]) extends KeycloakClientA[Task, Observable[T]](config) {
1616

1717
/**
1818
* Used for calls that return a sequence of items, this sequentially makes calls to retrieve and process

keycloak4s-admin-monix/src/main/scala/com/fullfacing/keycloak4s/admin/monix/services/package.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package com.fullfacing.keycloak4s.admin.monix
22

3-
import com.softwaremill.sttp.Uri.QueryFragment.KeyValue
43
import monix.reactive.Consumer
4+
import sttp.model.Uri.QuerySegment.KeyValue
55

66
import scala.collection.immutable.Seq
77

keycloak4s-admin/src/main/scala/com/fullfacing/keycloak4s.admin/client/KeycloakClient.scala

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,55 +10,58 @@ import com.fullfacing.keycloak4s.admin.handles.Logging
1010
import com.fullfacing.keycloak4s.admin.handles.Logging.logLeft
1111
import com.fullfacing.keycloak4s.core.models._
1212
import com.fullfacing.keycloak4s.core.serialization.JsonFormats.default
13-
import com.softwaremill.sttp.Uri.QueryFragment.KeyValue
14-
import com.softwaremill.sttp.{Id, RequestT, SttpBackend, Uri, asString, sttp}
1513
import org.json4s.jackson.Serialization.read
14+
import sttp.client.{Identity, NothingT, RequestT, SttpBackend, asString, _}
15+
import sttp.model.Uri
16+
import sttp.model.Uri.QuerySegment.KeyValue
1617

1718
import scala.collection.immutable.Seq
1819
import scala.reflect._
1920
import scala.reflect.runtime.universe.{TypeTag, typeOf}
2021
import scala.util.control.NonFatal
2122

22-
class KeycloakClient[F[+_] : Concurrent, -S](config: ConfigWithAuth)(implicit client: SttpBackend[F, S]) extends TokenManager[F, S](config) {
23+
class KeycloakClient[F[+_] : Concurrent, -S](config: ConfigWithAuth)(implicit client: SttpBackend[F, S, NothingT]) extends TokenManager[F, S](config) {
2324

2425
val realm: String = config.realm
2526

2627
/* Extracts the Manifest out of an implicit Anything. **/
2728
private implicit def ma[A : Anything]: Manifest[A] = implicitly[Anything[A]].manifest
2829

2930
/* URI Builder **/
30-
private[client] def createUri(path: Seq[String], query: Seq[KeyValue]) = Uri(
31+
private[client] def createUri(path: Seq[String], query: Seq[KeyValue]) = Uri.notValidated(
3132
scheme = config.scheme,
3233
userInfo = None,
3334
host = config.host,
3435
port = Some(config.port),
3536
path = Seq("auth", "admin", "realms") ++ path,
36-
queryFragments = query,
37+
querySegments = query,
3738
fragment = None
3839
)
3940

4041
/* HTTP Call Builders **/
4142

42-
private def setResponse[A <: Any : Manifest](request: RequestT[Id, String, Nothing])(implicit tag: TypeTag[A], cId: UUID)
43-
: F[Either[KeycloakSttpException, RequestT[Id, A, Nothing]]] = {
43+
private def setResponse[A <: Any : Manifest](request: RequestT[Identity, Either[String, String], Nothing])(implicit tag: TypeTag[A], cId: UUID)
44+
: F[Either[KeycloakSttpException, RequestT[Identity, Either[String, A], Nothing]]] = {
4445

4546
val respAs = asString.mapWithMetadata { case (raw, meta) =>
46-
Logging.requestSuccessful(raw, cId)
47+
raw.map { body =>
48+
Logging.requestSuccessful(body, cId)
4749

48-
if (tag.tpe =:= typeOf[Unit]) read[A]("null")
49-
else if (tag.tpe =:= typeOf[Headers]) meta.headers.toMap.asInstanceOf[A]
50-
else read[A](raw)
50+
if (tag.tpe =:= typeOf[Unit]) read[A]("null")
51+
else if (tag.tpe =:= typeOf[Headers]) meta.headers.map(h => h.name -> h.value).toMap.asInstanceOf[A]
52+
else read[A](body)
53+
}
5154
}
5255

5356
withAuth(request.response(respAs))
5457
}
5558

56-
private def call[B <: Any : Manifest](request: RequestT[Id, String, Nothing], requestInfo: RequestInfo): F[Either[KeycloakError, B]] = {
59+
private def call[B <: Any : Manifest](request: RequestT[Identity, Either[String, String], Nothing], requestInfo: RequestInfo): F[Either[KeycloakError, B]] = {
5760
implicit val cId: UUID = UUID.randomUUID()
5861

5962
val resp = setResponse[B](request.header("Accept", "application/json"))
6063

61-
def sendWithLogging(req: RequestT[Id, B, Nothing]) = {
64+
def sendWithLogging(req: RequestT[Identity, Either[String, B], Nothing]) = {
6265
Logging.requestSent(requestInfo, cId)
6366
req.send()
6467
}
@@ -75,24 +78,24 @@ class KeycloakClient[F[+_] : Concurrent, -S](config: ConfigWithAuth)(implicit cl
7578

7679
/* REST Protocol Calls **/
7780
def get[A : Anything](path: Seq[String], query: Seq[KeyValue] = Seq.empty[KeyValue]): F[Either[KeycloakError, A]] = {
78-
val request = sttp.get(createUri(path, query))
81+
val request = basicRequest.get(createUri(path, query))
7982
call[A](request, buildRequestInfo(path, "GET", ()))
8083
}
8184

8285
def put[A : Anything](path: Seq[String], payload: BodyMagnet = (), query: Seq[KeyValue] = Seq.empty[KeyValue]): F[Either[KeycloakError, A]] = {
83-
val request = sttp.put(createUri(path, query))
86+
val request = basicRequest.put(createUri(path, query))
8487
val injected = payload.apply(request)
8588
call[A](injected, buildRequestInfo(path, "PUT", injected.body))
8689
}
8790

8891
def post[A : Anything](path: Seq[String], payload: BodyMagnet = (), query: Seq[KeyValue] = Seq.empty[KeyValue]): F[Either[KeycloakError, A]] = {
89-
val request = sttp.post(createUri(path, query))
92+
val request = basicRequest.post(createUri(path, query))
9093
val injected = payload.apply(request)
9194
call[A](injected, buildRequestInfo(path, "POST", injected.body))
9295
}
9396

9497
def delete[A : Anything](path: Seq[String], payload: BodyMagnet = (), query: Seq[KeyValue] = Seq.empty[KeyValue]): F[Either[KeycloakError, A]] = {
95-
val request = sttp.delete(createUri(path, query))
98+
val request = basicRequest.delete(createUri(path, query))
9699
val injected = payload.apply(request)
97100
call[A](injected, buildRequestInfo(path, "DELETE", injected.body))
98101
}

keycloak4s-admin/src/main/scala/com/fullfacing/keycloak4s.admin/client/TokenManager.scala

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package com.fullfacing.keycloak4s.admin.client
22

3-
import java.nio.charset.StandardCharsets
43
import java.time.Instant
54
import java.util.UUID
65
import java.util.concurrent.atomic.AtomicReference
@@ -10,13 +9,14 @@ import cats.implicits._
109
import com.fullfacing.keycloak4s.admin.client.TokenManager.{Token, TokenResponse}
1110
import com.fullfacing.keycloak4s.admin.handles.Logging
1211
import com.fullfacing.keycloak4s.admin.handles.Logging.handleLogging
12+
import com.fullfacing.keycloak4s.core.models.{ConfigWithAuth, KeycloakConfig, KeycloakSttpException, RequestInfo}
1313
import com.fullfacing.keycloak4s.core.serialization.JsonFormats.default
14-
import com.fullfacing.keycloak4s.core.models.{KeycloakConfig, ConfigWithAuth, KeycloakSttpException, RequestInfo}
15-
import com.softwaremill.sttp.json4s.asJson
16-
import com.softwaremill.sttp.{SttpBackend, _}
1714
import org.json4s.jackson.Serialization
15+
import sttp.client.json4s._
16+
import sttp.client.monad.MonadError
17+
import sttp.client.{Identity, NoBody, NothingT, RequestT, Response, SttpBackend, _}
1818

19-
abstract class TokenManager[F[_] : Concurrent, -S](config: ConfigWithAuth)(implicit client: SttpBackend[F, S]) {
19+
abstract class TokenManager[F[_] : Concurrent, -S](config: ConfigWithAuth)(implicit client: SttpBackend[F, S, NothingT]) {
2020

2121
protected implicit val serialization: Serialization.type = org.json4s.jackson.Serialization
2222

@@ -34,18 +34,18 @@ abstract class TokenManager[F[_] : Concurrent, -S](config: ConfigWithAuth)(impli
3434
)
3535
}
3636

37-
protected def buildError(response: Response[_], requestInfo: RequestInfo): KeycloakSttpException = {
37+
protected def buildError(response: Response[_], leftBody: String, requestInfo: RequestInfo): KeycloakSttpException = {
3838
KeycloakSttpException(
39-
code = response.code,
40-
headers = response.headers,
41-
body = response.rawErrorBody.fold(new String(_, StandardCharsets.UTF_8), _ => "N/A"),
39+
code = response.code.code,
40+
headers = response.headers.map(h => h.name -> h.value),
41+
body = leftBody,
4242
statusText = response.statusText,
4343
requestInfo = requestInfo
4444
)
4545
}
4646

47-
protected def liftM[A](response: Response[A], requestInfo: RequestInfo): Either[KeycloakSttpException, A] = {
48-
response.body.leftMap(_ => buildError(response, requestInfo))
47+
protected def liftM[A](response: Response[Either[String, A]], requestInfo: RequestInfo): Either[KeycloakSttpException, A] = {
48+
response.body.leftMap(l => buildError(response, l, requestInfo))
4949
}
5050

5151
private val tokenEndpoint =
@@ -96,7 +96,7 @@ abstract class TokenManager[F[_] : Concurrent, -S](config: ConfigWithAuth)(impli
9696
val sendF = Concurrent[F].unit.flatMap { _ =>
9797
Logging.tokenRequest(config.realm, cId)
9898

99-
sttp.post(tokenEndpoint)
99+
basicRequest.post(tokenEndpoint)
100100
.body(password)
101101
.response(asJson[TokenResponse])
102102
.mapResponse(mapToToken)
@@ -118,7 +118,7 @@ abstract class TokenManager[F[_] : Concurrent, -S](config: ConfigWithAuth)(impli
118118
val sendF = Concurrent[F].unit.flatMap { _ =>
119119
Logging.tokenRefresh(config.realm, cId)
120120

121-
sttp.post(tokenEndpoint)
121+
basicRequest.post(tokenEndpoint)
122122
.body(body)
123123
.response(asJson[TokenResponse])
124124
.mapResponse(mapToToken)
@@ -139,15 +139,15 @@ abstract class TokenManager[F[_] : Concurrent, -S](config: ConfigWithAuth)(impli
139139
* @param response the oidc token response.
140140
* @return a new token instance.
141141
*/
142-
private def mapToToken(response: TokenResponse): Token = {
142+
private def mapToToken(response: Either[ResponseError[Exception], TokenResponse]): Either[String, Token] = response.map { res =>
143143
val instant = Instant.now()
144144
Token(
145-
access = response.access_token,
146-
refresh = response.refresh_token,
147-
refreshAt = instant.plusSeconds(response.expires_in),
148-
authenticateAt = instant.plusSeconds(response.refresh_expires_in)
145+
access = res.access_token,
146+
refresh = res.refresh_token,
147+
refreshAt = instant.plusSeconds(res.expires_in),
148+
authenticateAt = instant.plusSeconds(res.refresh_expires_in)
149149
)
150-
}
150+
}.leftMap(_.body)
151151

152152
/**
153153
* Inspect the status of the token, reissuing a new access token using the password
@@ -180,7 +180,7 @@ abstract class TokenManager[F[_] : Concurrent, -S](config: ConfigWithAuth)(impli
180180
}
181181
}
182182

183-
def withAuth[A](request: RequestT[Id, A, Nothing])(implicit cId: UUID): F[Either[KeycloakSttpException, RequestT[Id, A, Nothing]]] = {
183+
def withAuth[A](request: RequestT[Identity, A, Nothing])(implicit cId: UUID): F[Either[KeycloakSttpException, RequestT[Identity, A, Nothing]]] = {
184184
Concurrent[F].map(validateToken())(_.map(tkn => request.auth.bearer(tkn.access)))
185185
}
186186
}

0 commit comments

Comments
 (0)