Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
13 changed files
with
593 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -101,6 +101,7 @@ local.properties | |
# .idea/modules.xml | ||
# .idea/*.iml | ||
# .idea/modules | ||
*.iml | ||
|
||
# CMake | ||
cmake-build-*/ | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,6 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<FindBugsFilter/> | ||
<FindBugsFilter> | ||
<Match> | ||
<Source name="~.*\.scala" /> | ||
</Match> | ||
</FindBugsFilter> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
62 changes: 62 additions & 0 deletions
62
shared/src/main/scala/org/pac4j/lagom/scaladsl/LagomWebContext.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
package org.pac4j.lagom.scaladsl | ||
|
||
import java.util | ||
|
||
import com.lightbend.lagom.scaladsl.api.transport.RequestHeader | ||
import org.pac4j.core.context.session.SessionStore | ||
import org.pac4j.core.context.{Cookie, WebContext} | ||
import org.pac4j.core.exception.TechnicalException | ||
|
||
/** | ||
* <p>Implementation web context of PAC4J for Lagom framework.</p> | ||
* <p>Context is immutable and the {@link SessionStore} is not supported.</p> | ||
* | ||
* @author Vladimir Kornyshev | ||
* @since 1.0.0 | ||
*/ | ||
class LagomWebContext(requestHeader: RequestHeader) extends WebContext { | ||
|
||
override def getSessionStore: SessionStore[_ <: WebContext] = throw new TechnicalException("Operation not supported") | ||
|
||
override def getRequestParameter(s: String): String = throw new TechnicalException("Operation not supported") | ||
|
||
override def getRequestParameters: util.Map[String, Array[String]] = throw new TechnicalException("Operation not supported") | ||
|
||
override def getRequestAttribute(s: String): AnyRef = throw new TechnicalException("Operation not supported") | ||
|
||
override def setRequestAttribute(s: String, o: Any): Unit = throw new TechnicalException("Operation not supported") | ||
|
||
override def getRequestHeader(name: String): String = requestHeader.getHeader(name) match { | ||
case Some(value) => value | ||
case None => throw new TechnicalException(s"Header $name not found") | ||
} | ||
|
||
override def getRequestMethod: String = requestHeader.method.name | ||
|
||
override def getRemoteAddr: String = throw new TechnicalException("Operation not supported") | ||
|
||
override def writeResponseContent(s: String): Unit = throw new TechnicalException("Operation not supported") | ||
|
||
override def setResponseStatus(i: Int): Unit = throw new TechnicalException("Operation not supported") | ||
|
||
override def setResponseHeader(s: String, s1: String): Unit = throw new TechnicalException("Operation not supported") | ||
|
||
override def setResponseContentType(s: String): Unit = throw new TechnicalException("Operation not supported") | ||
|
||
override def getServerName: String = throw new TechnicalException("Operation not supported") | ||
|
||
override def getServerPort: Int = throw new TechnicalException("Operation not supported") | ||
|
||
override def getScheme: String = throw new TechnicalException("Operation not supported") | ||
|
||
override def isSecure: Boolean = false | ||
|
||
override def getFullRequestURL: String = throw new TechnicalException("Operation not supported") | ||
|
||
override def getRequestCookies: util.Collection[Cookie] = throw new TechnicalException("Operation not supported") | ||
|
||
override def addResponseCookie(cookie: Cookie): Unit = throw new TechnicalException("Operation not supported") | ||
|
||
override def getPath: String = requestHeader.uri.getPath | ||
|
||
} |
137 changes: 137 additions & 0 deletions
137
shared/src/main/scala/org/pac4j/lagom/scaladsl/SecuredService.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
package org.pac4j.lagom.scaladsl | ||
|
||
import java.util.Collections.singletonList | ||
|
||
import com.lightbend.lagom.scaladsl.api.transport.Forbidden | ||
import com.lightbend.lagom.scaladsl.server.ServerServiceCall | ||
import org.pac4j.core.authorization.authorizer.Authorizer | ||
import org.pac4j.core.client.Client | ||
import org.pac4j.core.config.Config | ||
import org.pac4j.core.credentials.Credentials | ||
import org.pac4j.core.profile.{AnonymousProfile, CommonProfile} | ||
|
||
/** | ||
* <p> | ||
* Interface, that implement cross-cutting security concerns for Lagom services. | ||
* </p> | ||
* <p> | ||
* More information about service call composition in Lagom <a href="https://www.lagomframework.com/documentation/current/scala/ServiceImplementation.html#Service-call-composition">documentation</a>. | ||
* </p> | ||
* | ||
* @author Vladimir Kornyshev | ||
* @since 1.0.0 | ||
*/ | ||
trait SecuredService { | ||
|
||
/** | ||
* Get configuration of pac4j for this service. | ||
* | ||
* @return pac4j configuration | ||
*/ | ||
def securityConfig: Config | ||
|
||
/** | ||
* Service call composition for authentication. | ||
* | ||
* @param serviceCall Service call | ||
* @tparam Request Type of request | ||
* @tparam Response Type of response | ||
* @return Service call with authentication logic | ||
*/ | ||
def authenticate[Request, Response]( | ||
serviceCall: CommonProfile => ServerServiceCall[Request, Response]): ServerServiceCall[Request, Response] = | ||
authenticate(securityConfig.getClients.getDefaultSecurityClients, serviceCall) | ||
|
||
/** | ||
* Service call composition for authentication. | ||
* | ||
* @param clientName Name of authentication client | ||
* @param serviceCall Service call | ||
* @tparam Request Type of request | ||
* @tparam Response Type of response | ||
* @return Service call with authentication logic | ||
*/ | ||
def authenticate[Request, Response]( | ||
clientName: String, serviceCall: CommonProfile => ServerServiceCall[Request, Response]): ServerServiceCall[Request, Response] = | ||
ServerServiceCall.compose { requestHeader => | ||
val profile = try { | ||
val clients = securityConfig.getClients | ||
val defaultClient = clients.findClient(clientName).asInstanceOf[Client[Credentials, CommonProfile]] | ||
val context = new LagomWebContext(requestHeader) | ||
val credentials = defaultClient.getCredentials(context) | ||
defaultClient.getUserProfile(credentials, context) | ||
} catch { | ||
case ex: Exception => | ||
// We can throw only TransportException. | ||
// Otherwise exception will be sent to the client with stack trace. | ||
new AnonymousProfile | ||
} | ||
|
||
serviceCall.apply(Option(profile).getOrElse(new AnonymousProfile)) | ||
} | ||
|
||
/** | ||
* Service call composition for authorization. | ||
* | ||
* @param authorizer Authorizer (may be composite) | ||
* @param serviceCall Service call | ||
* @tparam Request Type of request | ||
* @tparam Response Type of response | ||
* @return Service call with authorization logic | ||
*/ | ||
def authorize[Request, Response]( | ||
authorizer: Authorizer[CommonProfile], serviceCall: CommonProfile => ServerServiceCall[Request, Response]): ServerServiceCall[Request, Response] = | ||
authorize(securityConfig.getClients.getDefaultSecurityClients, authorizer, serviceCall) | ||
|
||
/** | ||
* Service call composition for authorization. | ||
* | ||
* @param clientName Name of authentication client | ||
* @param authorizer Authorizer (may be composite) | ||
* @param serviceCall Service call | ||
* @tparam Request Type of request | ||
* @tparam Response Type of response | ||
* @return Service call with authorization logic | ||
*/ | ||
def authorize[Request, Response]( | ||
clientName: String, authorizer: Authorizer[CommonProfile], serviceCall: CommonProfile => ServerServiceCall[Request, Response]): ServerServiceCall[Request, Response] = | ||
authenticate(clientName, (profile: CommonProfile) => ServerServiceCall.compose { requestHeader => | ||
val authorized = try { | ||
authorizer != null && authorizer.isAuthorized(new LagomWebContext(requestHeader), singletonList(profile)) | ||
} catch { | ||
case ex: Exception => | ||
// We can throw only TransportException. | ||
// Otherwise exception will be sent to the client with stack trace. | ||
false | ||
} | ||
if (!authorized) throw Forbidden("Authorization failed") | ||
serviceCall.apply(profile) | ||
}) | ||
|
||
/** | ||
* Service call composition for authorization. | ||
* | ||
* @param authorizerName Name of authorizer, registered in security config | ||
* @param serviceCall Service call | ||
* @tparam Request Type of request | ||
* @tparam Response Type of response | ||
* @return Service call with authorization logic | ||
*/ | ||
def authorize[Request, Response]( | ||
authorizerName: String, serviceCall: CommonProfile => ServerServiceCall[Request, Response]): ServerServiceCall[Request, Response] = | ||
authorize(securityConfig.getAuthorizers.get(authorizerName).asInstanceOf[Authorizer[CommonProfile]], serviceCall) | ||
|
||
/** | ||
* Service call composition for authorization. | ||
* | ||
* @param clientName Name of authentication client | ||
* @param authorizerName Name of authorizer, registered in security config | ||
* @param serviceCall Service call | ||
* @tparam Request Type of request | ||
* @tparam Response Type of response | ||
* @return Service call with authorization logic | ||
*/ | ||
def authorize[Request, Response]( | ||
clientName: String, authorizerName: String, serviceCall: CommonProfile => ServerServiceCall[Request, Response]): ServerServiceCall[Request, Response] = | ||
authorize(clientName, securityConfig.getAuthorizers.get(authorizerName).asInstanceOf[Authorizer[CommonProfile]], serviceCall) | ||
} |
14 changes: 14 additions & 0 deletions
14
shared/src/test/scala/org/pac4j/lagom/scaladsl/ClientNames.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package org.pac4j.lagom.scaladsl | ||
|
||
/** | ||
* Names of clients, that will be used for tests. | ||
* | ||
* @author Vladimir Kornyshev | ||
* @since 1.0.0 | ||
*/ | ||
object ClientNames { | ||
|
||
val HEADER_CLIENT = "simple_header" | ||
val HEADER_JWT_CLIENT = "jwt_header" | ||
|
||
} |
Oops, something went wrong.