Skip to content
This repository has been archived by the owner on Apr 10, 2019. It is now read-only.

Commit

Permalink
#3, refactor: Extracted specification-related part from security trait
Browse files Browse the repository at this point in the history
  • Loading branch information
slavaschmidt committed Mar 7, 2016
1 parent 2c97099 commit 8ebb31f
Show file tree
Hide file tree
Showing 15 changed files with 270 additions and 412 deletions.
23 changes: 12 additions & 11 deletions compiler/src/main/resources/play_scala_controller_security.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -7,28 +7,29 @@ import de.zalando.play.controllers.PlayBodyParsing
import {{import.name}}
{{/for}}

trait SecurityExtractors {
{{for extractor in security_extractors.extractors}}
def {{extractor.name}}[User >: Any](header: RequestHeader): Option[User] = ???
{{/for}}

}

{{for controller in controllers}}

trait {{controller.security_trait}} {
trait {{controller.security_trait}} extends SecurityExtractors {
val unauthorizedContent = ???
val mimeType: String = ???
{{for m in controller.methods}}
{{if m.needs_security}}
{{for security_check in m.security_checks}}
def {{security_check.name}}[User >: Any](header: RequestHeader): Option[User] = ???
{{/for}}

val {{m.secure_checks}} = Seq({{for security_check in m.security_checks}}{{security_check.name}} _{{if security_check.isNotLast}}, {{/if}}{{/for}})
{{for m in controller.methods}}{{if m.needs_security}}
object {{m.secure_action}} extends AuthenticatedBuilder(
req => {
val individualChecks = {{m.secure_checks}}.map(_.apply(req))
val secureChecks = Seq({{for security_check in m.security_checks}}{{security_check.name}} _{{if security_check.isNotLast}}, {{/if}}{{/for}})
val individualChecks = secureChecks.map(_.apply(req))
individualChecks.find(_.isEmpty).getOrElse(Option(individualChecks.flatten))
},
onUnauthorized(mimeType, unauthorizedContent)
)
{{/if}}
{{/for}}
{{/if}}{{/for}}

private def onUnauthorized[C](mimeType: String, content: C): RequestHeader => Result =_ => {
implicit val writeable = PlayBodyParsing.anyToWritable[C](mimeType)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ class ScalaPlayTypeEnricher(val app: StrictModel) extends Transformation[Type] w
/**
* Enriches AST with information related to the specification as whole
*/
class ScalaPlaySpecEnricher(val app: StrictModel) extends Transformation[StrictModel] with MarshallersStep {
class ScalaPlaySpecEnricher(val app: StrictModel) extends Transformation[StrictModel]
with MarshallersStep with SecurityStep {

override def data = Seq(Reference.root -> app)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package de.zalando.apifirst.generators

import de.zalando.apifirst.Application.StrictModel
import de.zalando.apifirst.generators.DenotationNames._
import de.zalando.apifirst.{Security, ParameterPlace, ScalaName}
import de.zalando.play.controllers.WriterFactories

import scala.ref.WeakReference

/**
* @author slasch
* @since 07.03.2016
*/
trait SecurityStep extends EnrichmentStep[StrictModel] with SecurityCommons {

override def steps = security +: super.steps

/**
* Puts security related information into the denotation table
*
* @return
*/
protected val security: SingleStep = spec => table => {
val extractors: Iterable[Map[String, String]] = spec._2.securityDefinitionsTable.map { case (name, definition) =>
Map("name" -> securityCheck(name))
}
if (extractors.isEmpty) Map.empty
else Map("security_extractors" -> Map("extractors" -> extractors))
}
}

trait SecurityCommons {
def securityCheck(name: String) =
ScalaName.escape(name + "_Extractor")

def securityName(suffix: String, security: Security.Constraint*): String =
security.map(_.name).mkString("", "_", "_"+suffix)
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package de.zalando.apifirst.generators

import de.zalando.apifirst.Application.{ApiCall, Parameter, ParameterRef}
import de.zalando.apifirst.Domain._
import de.zalando.apifirst.{Security, ScalaName, Http, ParameterPlace}
import de.zalando.apifirst.{Http, ParameterPlace}
import de.zalando.apifirst.ScalaName._
import de.zalando.apifirst.generators.DenotationNames._
import de.zalando.apifirst.naming.Reference
Expand All @@ -12,7 +12,7 @@ import de.zalando.play.controllers.WriterFactories
* @author slasch
* @since 31.12.2015.
*/
trait CallControllersStep extends EnrichmentStep[ApiCall] with ControllersCommons {
trait CallControllersStep extends EnrichmentStep[ApiCall] with ControllersCommons with SecurityCommons {

override def steps = controllers +: super.steps

Expand Down Expand Up @@ -83,18 +83,10 @@ trait CallControllersStep extends EnrichmentStep[ApiCall] with ControllersCommon

private def securityData(call: ApiCall)(implicit table: DenotationTable): Map[String, Any] = Map(
"needs_security" -> call.security.nonEmpty,
"secure_action" -> securityName(call.security, "Action"),
"secure_checks" -> securityName(call.security, "Checks"),
"security_checks" -> call.security.map(securityCheck)
"secure_action" -> nameWithSuffix(call, "SecureAction"),
"security_checks" -> call.security.map(s => Map("name" -> securityCheck(s.name)))
)

private def securityCheck(constraint: Security.Constraint): Map[String, Any] = Map(
"name" -> securityName(Set(constraint), "Extractor")
)

private def securityName(security: Set[Security.Constraint], suffix: String): String =
security.map(_.name).mkString("", "_", "_"+suffix)

private def needsCustom(mimeTypes: Set[Http.MimeType]): Boolean =
mimeTypes.map(_.name).diff(WriterFactories.factories.keySet).nonEmpty

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,9 @@ class ScalaGenerator(val strictModel: StrictModel) extends PlayScalaControllerAn
val unmarshallers = ReShaper.filterByType("unmarshallers", denotationTable)
val grouppedunMarshallers = ReShaper.groupByType(unmarshallers.toSeq).toMap

val securityExtractors = ReShaper.filterByType("security_extractors", denotationTable)
val extractors = ReShaper.groupByType(securityExtractors.toSeq).toMap

val (unmanagedParts: Map[ApiCall, UnmanagedPart], unmanagedImports: Seq[String]) =
analyzeController(currentController, denotationTable)

Expand All @@ -126,6 +129,7 @@ class ScalaGenerator(val strictModel: StrictModel) extends PlayScalaControllerAn
"unmanaged_imports" -> unmanagedImports.map(i => Map("name" -> i))
)

val securityChecks = ReShaper.groupByType(controllersList.toSeq).toMap

val singlePackage = Map(
"classes" -> ReShaper.filterByType("classes", denotationTable),
Expand All @@ -136,6 +140,7 @@ class ScalaGenerator(val strictModel: StrictModel) extends PlayScalaControllerAn
"tests" -> ReShaper.filterByType("tests", denotationTable),
"marshallers" -> grouppedMarshallers,
"unmarshallers" -> grouppedunMarshallers,
"security_extractors" -> extractors,
"bindings" -> bindingsByType
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,14 @@ import scala.util._




trait BasicAuthApiYamlBase extends Controller with PlayBodyParsing with BasicAuthApiYamlSecurity {
private type getActionRequestType = (Unit)
private type getActionType = getActionRequestType => Try[(Int, Any)]

private val errorToStatusget: PartialFunction[Throwable, Status] = PartialFunction.empty[Throwable, Status]


def getAction = (f: getActionType) => basicAuth_Action { request =>
def getAction = (f: getActionType) => getSecureAction { request =>
val providedTypes = Seq[String]()

negotiateContent(request.acceptedTypes, providedTypes).map { getResponseMimeType =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import de.zalando.play.controllers.PlayPathBindables




trait FullPetstoreApiYamlBase extends Controller with PlayBodyParsing with FullPetstoreApiYamlSecurity {
private type findPetsByTagsActionRequestType = (PetsFindByStatusGetStatus)
private type findPetsByTagsActionType = findPetsByTagsActionRequestType => Try[(Int, Any)]
Expand All @@ -26,7 +25,7 @@ trait FullPetstoreApiYamlBase extends Controller with PlayBodyParsing with Full
}


def findPetsByTagsAction = (f: findPetsByTagsActionType) => (tags: PetsFindByStatusGetStatus) => petstore_auth_Action { request =>
def findPetsByTagsAction = (f: findPetsByTagsActionType) => (tags: PetsFindByStatusGetStatus) => findPetsByTagsSecureAction { request =>
val providedTypes = Seq[String]("application/json", "application/xml")

negotiateContent(request.acceptedTypes, providedTypes).map { findPetsByTagsResponseMimeType =>
Expand Down Expand Up @@ -458,7 +457,7 @@ trait FullPetstoreApiYamlBase extends Controller with PlayBodyParsing with Full
optionParser[Pet](bodyMimeType, customParsers, "Invalid PetsPostBody", maxLength)
}

def updatePetAction = (f: updatePetActionType) => petstore_auth_Action(updatePetParser(Seq[String]("application/json", "application/xml"))) { request =>
def updatePetAction = (f: updatePetActionType) => updatePetSecureAction(updatePetParser(Seq[String]("application/json", "application/xml"))) { request =>
val providedTypes = Seq[String]("application/json", "application/xml")

negotiateContent(request.acceptedTypes, providedTypes).map { updatePetResponseMimeType =>
Expand Down Expand Up @@ -525,7 +524,7 @@ trait FullPetstoreApiYamlBase extends Controller with PlayBodyParsing with Full
optionParser[Pet](bodyMimeType, customParsers, "Invalid PetsPostBody", maxLength)
}

def addPetAction = (f: addPetActionType) => petstore_auth_Action(addPetParser(Seq[String]("application/json", "application/xml"))) { request =>
def addPetAction = (f: addPetActionType) => addPetSecureAction(addPetParser(Seq[String]("application/json", "application/xml"))) { request =>
val providedTypes = Seq[String]("application/json", "application/xml")

negotiateContent(request.acceptedTypes, providedTypes).map { addPetResponseMimeType =>
Expand Down Expand Up @@ -789,7 +788,7 @@ trait FullPetstoreApiYamlBase extends Controller with PlayBodyParsing with Full
}


def getPetByIdAction = (f: getPetByIdActionType) => (petId: Long) => api_key_petstore_auth_Action { request =>
def getPetByIdAction = (f: getPetByIdActionType) => (petId: Long) => getPetByIdSecureAction { request =>
val providedTypes = Seq[String]("application/json", "application/xml")

negotiateContent(request.acceptedTypes, providedTypes).map { getPetByIdResponseMimeType =>
Expand Down Expand Up @@ -842,7 +841,7 @@ trait FullPetstoreApiYamlBase extends Controller with PlayBodyParsing with Full
}


def updatePetWithFormAction = (f: updatePetWithFormActionType) => (petId: String, name: String, status: String) => petstore_auth_Action { request =>
def updatePetWithFormAction = (f: updatePetWithFormActionType) => (petId: String, name: String, status: String) => updatePetWithFormSecureAction { request =>
val providedTypes = Seq[String]("application/json", "application/xml")

negotiateContent(request.acceptedTypes, providedTypes).map { updatePetWithFormResponseMimeType =>
Expand Down Expand Up @@ -893,7 +892,7 @@ trait FullPetstoreApiYamlBase extends Controller with PlayBodyParsing with Full
}


def deletePetAction = (f: deletePetActionType) => (petId: Long) => petstore_auth_Action { request =>
def deletePetAction = (f: deletePetActionType) => (petId: Long) => deletePetSecureAction { request =>
val providedTypes = Seq[String]("application/json", "application/xml")

negotiateContent(request.acceptedTypes, providedTypes).map { deletePetResponseMimeType =>
Expand Down Expand Up @@ -953,7 +952,7 @@ trait FullPetstoreApiYamlBase extends Controller with PlayBodyParsing with Full
}


def findPetsByStatusAction = (f: findPetsByStatusActionType) => (status: PetsFindByStatusGetStatus) => petstore_auth_Action { request =>
def findPetsByStatusAction = (f: findPetsByStatusActionType) => (status: PetsFindByStatusGetStatus) => findPetsByStatusSecureAction { request =>
val providedTypes = Seq[String]("application/json", "application/xml")

negotiateContent(request.acceptedTypes, providedTypes).map { findPetsByStatusResponseMimeType =>
Expand Down
Loading

0 comments on commit 8ebb31f

Please sign in to comment.