-
-
Notifications
You must be signed in to change notification settings - Fork 53
feature: Added APIGatewayAuthorizerEvent & results #639
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
0cc469f
095daff
f50b8a2
1129e13
adde434
1e45212
50bf1ae
c56c8ce
4bf57ad
80d98e8
2aed831
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,221 @@ | ||
| /* | ||
| * Copyright 2021 Typelevel | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
|
|
||
| package feral.lambda | ||
| package events | ||
|
|
||
| import com.comcast.ip4s.Hostname | ||
| import feral.lambda.KernelSource | ||
| import io.circe.Decoder | ||
| import natchez.Kernel | ||
| import org.typelevel.ci.CIString | ||
|
|
||
| import codecs.decodeHostname | ||
| import codecs.decodeKeyCIString | ||
|
|
||
| sealed abstract class AuthorizerRequestContext { | ||
| def resourceId: String | ||
| def resourcePath: String | ||
| def httpMethod: String | ||
| def extendedRequestId: String | ||
| def requestTime: String | ||
| def path: String | ||
| def accountId: String | ||
| def protocol: String | ||
| def stage: String | ||
| def domainPrefix: String | ||
| def requestTimeEpoch: Long | ||
| def requestId: String | ||
| def identity: Map[String, Option[String]] | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This type looks a bit strange 🤔 I wonder in what case would there be a key would no value? (instead of just not having the key at all).
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. From the Input format of a Lambda function for proxy integration there are such cases . Also, I'd specifically update the
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Interesting, I see. It looks like it would be better to define a new class for |
||
| def domainName: Hostname | ||
| def deploymentId: String | ||
| def apiId: String | ||
| } | ||
|
|
||
| object AuthorizerRequestContext { | ||
|
|
||
| def apply( | ||
| resourceId: String, | ||
| resourcePath: String, | ||
| httpMethod: String, | ||
| extendedRequestId: String, | ||
| requestTime: String, | ||
| path: String, | ||
| accountId: String, | ||
| protocol: String, | ||
| stage: String, | ||
| domainPrefix: String, | ||
| requestTimeEpoch: Long, | ||
| requestId: String, | ||
| identity: Map[String, Option[String]], | ||
| domainName: Hostname, | ||
| deploymentId: String, | ||
| apiId: String | ||
| ): AuthorizerRequestContext = | ||
| new Impl( | ||
| resourceId, | ||
| resourcePath, | ||
| httpMethod, | ||
| extendedRequestId, | ||
| requestTime, | ||
| path, | ||
| accountId, | ||
| protocol, | ||
| stage, | ||
| domainPrefix, | ||
| requestTimeEpoch, | ||
| requestId, | ||
| identity, | ||
| domainName, | ||
| deploymentId, | ||
| apiId | ||
| ) | ||
|
|
||
| implicit def decoder: Decoder[AuthorizerRequestContext] = Decoder.forProduct16( | ||
| "resourceId", | ||
| "resourcePath", | ||
| "httpMethod", | ||
| "extendedRequestId", | ||
| "requestTime", | ||
| "path", | ||
| "accountId", | ||
| "protocol", | ||
| "stage", | ||
| "domainPrefix", | ||
| "requestTimeEpoch", | ||
| "requestId", | ||
| "identity", | ||
| "domainName", | ||
| "deploymentId", | ||
| "apiId" | ||
| )(AuthorizerRequestContext.apply) | ||
|
|
||
| private case class Impl( | ||
| resourceId: String, | ||
| resourcePath: String, | ||
| httpMethod: String, | ||
| extendedRequestId: String, | ||
| requestTime: String, | ||
| path: String, | ||
| accountId: String, | ||
| protocol: String, | ||
| stage: String, | ||
| domainPrefix: String, | ||
| requestTimeEpoch: Long, | ||
| requestId: String, | ||
| identity: Map[String, Option[String]], | ||
| domainName: Hostname, | ||
| deploymentId: String, | ||
| apiId: String | ||
| ) extends AuthorizerRequestContext { | ||
| override def productPrefix = "AuthorizerRequestContext" | ||
| } | ||
| } | ||
|
|
||
| sealed abstract class ApiGatewayCustomAuthorizerEvent { | ||
| def eventType: CustomAuthorizerEventType | ||
| def methodArn: String | ||
| def resource: String | ||
| def path: String | ||
| def httpMethod: String | ||
| def headers: Option[Map[CIString, String]] | ||
| def multiValueHeaders: Map[CIString, List[String]] | ||
| def queryStringParameters: Map[CIString, Option[String]] | ||
| def multiValueQueryStringParameters: Map[CIString, Option[List[String]]] | ||
| def pathParameters: Map[CIString, String] | ||
| def stageVariables: Map[CIString, String] | ||
| def requestContext: AuthorizerRequestContext | ||
| } | ||
|
|
||
| object ApiGatewayCustomAuthorizerEvent { | ||
|
|
||
| def apply( | ||
| eventType: CustomAuthorizerEventType, | ||
| methodArn: String, | ||
| resource: String, | ||
| path: String, | ||
| httpMethod: String, | ||
| headers: Option[Map[CIString, String]], | ||
| multiValueHeaders: Map[CIString, List[String]], | ||
| queryStringParameters: Map[CIString, Option[String]], | ||
| multiValueQueryStringParameters: Map[CIString, Option[List[String]]], | ||
| pathParameters: Map[CIString, String], | ||
| stageVariables: Map[CIString, String], | ||
| requestContext: AuthorizerRequestContext | ||
| ): ApiGatewayCustomAuthorizerEvent = | ||
| new Impl( | ||
| eventType, | ||
| methodArn, | ||
| resource, | ||
| path, | ||
| httpMethod, | ||
| headers, | ||
| multiValueHeaders, | ||
| queryStringParameters, | ||
| multiValueQueryStringParameters, | ||
| pathParameters, | ||
| stageVariables, | ||
| requestContext | ||
| ) | ||
|
|
||
| implicit def kernelSource: KernelSource[ApiGatewayCustomAuthorizerEvent] = | ||
| e => Kernel(e.headers.getOrElse(Map.empty)) | ||
|
|
||
| implicit def decoder: Decoder[ApiGatewayCustomAuthorizerEvent] = Decoder.forProduct12( | ||
| "type", | ||
| "methodArn", | ||
| "resource", | ||
| "path", | ||
| "httpMethod", | ||
| "headers", | ||
| "multiValueHeaders", | ||
| "queryStringParameters", | ||
| "multiValueQueryStringParameters", | ||
| "pathParameters", | ||
| "stageVariables", | ||
| "requestContext" | ||
| )(ApiGatewayCustomAuthorizerEvent.apply) | ||
|
|
||
| private case class Impl( | ||
| eventType: CustomAuthorizerEventType, | ||
| methodArn: String, | ||
| resource: String, | ||
| path: String, | ||
| httpMethod: String, | ||
| headers: Option[Map[CIString, String]], | ||
| multiValueHeaders: Map[CIString, List[String]], | ||
| queryStringParameters: Map[CIString, Option[String]], | ||
| multiValueQueryStringParameters: Map[CIString, Option[List[String]]], | ||
| pathParameters: Map[CIString, String], | ||
| stageVariables: Map[CIString, String], | ||
| requestContext: AuthorizerRequestContext | ||
| ) extends ApiGatewayCustomAuthorizerEvent { | ||
| override def productPrefix = "ApiGatewayCustomAuthorizerEvent" | ||
| } | ||
| } | ||
|
|
||
| sealed abstract class CustomAuthorizerEventType | ||
|
|
||
| object CustomAuthorizerEventType { | ||
| case object Token extends CustomAuthorizerEventType | ||
| case object Request extends CustomAuthorizerEventType | ||
|
|
||
| private[events] implicit val decoder: Decoder[CustomAuthorizerEventType] = | ||
| Decoder.decodeString.map { | ||
| case "TOKEN" => Token | ||
| case "REQUEST" => Request | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,96 @@ | ||
| /* | ||
| * Copyright 2021 Typelevel | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
|
|
||
| package feral.lambda | ||
| package events | ||
|
|
||
| import io.circe.Encoder | ||
|
|
||
| import java.time.LocalDate | ||
|
|
||
| import codecs.encodeDate | ||
|
|
||
| sealed abstract class ApiGatewayCustomAuthorizerEventResult { | ||
| def principalId: String | ||
| def policyDocument: PolicyDocument | ||
| } | ||
|
|
||
| object ApiGatewayCustomAuthorizerEventResult { | ||
|
|
||
| def apply( | ||
| principalId: String, | ||
| policyDocument: PolicyDocument): ApiGatewayCustomAuthorizerEventResult = | ||
| Impl(principalId, policyDocument) | ||
|
|
||
| implicit def encoder: Encoder[ApiGatewayCustomAuthorizerEventResult] = Encoder.forProduct2( | ||
| "principalId", | ||
| "policyDocument" | ||
| )(r => (r.principalId, r.policyDocument)) | ||
|
|
||
| private case class Impl( | ||
| principalId: String, | ||
| policyDocument: PolicyDocument | ||
| ) extends ApiGatewayCustomAuthorizerEventResult { | ||
| override def productPrefix = "ApiGatewayCustomAuthorizerEventResult" | ||
| } | ||
| } | ||
|
|
||
| sealed abstract class PolicyDocument { | ||
| def version: LocalDate | ||
| def statement: List[Statement] | ||
| } | ||
|
|
||
| object PolicyDocument { | ||
| def apply(version: LocalDate, statement: List[Statement]): PolicyDocument = | ||
| Impl(version, statement) | ||
|
|
||
| implicit def encoder: Encoder[PolicyDocument] = Encoder.forProduct2( | ||
| "Version", | ||
| "Statement" | ||
| )(r => (r.version, r.statement)) | ||
|
|
||
| private case class Impl( | ||
| version: LocalDate, | ||
| statement: List[Statement] | ||
| ) extends PolicyDocument { | ||
| override def productPrefix = "PolicyDocument" | ||
| } | ||
| } | ||
|
|
||
| sealed abstract class Statement { | ||
| def action: String | ||
| def effect: String | ||
| def resource: String | ||
| } | ||
|
|
||
| object Statement { | ||
| def apply(action: String, effect: String, resource: String): Statement = | ||
| Impl(action, effect, resource) | ||
|
|
||
| implicit def encoder: Encoder[Statement] = Encoder.forProduct3( | ||
| "Action", | ||
| "Effect", | ||
| "Resource" | ||
| )(r => (r.action, r.effect, r.resource)) | ||
|
|
||
| private case class Impl( | ||
| action: String, | ||
| effect: String, | ||
| resource: String | ||
| ) extends Statement { | ||
| override def productPrefix = "Statement" | ||
| } | ||
| } |

There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see there is
requestTime: StringandrequestTimeEpoch: Long. Probably they are redundant, and we could userequestTime: Instantfor the best UX?Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think they are redundant since it's enumerated here in the output format
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What I mean by "redundant" is that the field contain identical information, just presented in different formats. We should be able to retrieve the time entirely using the epoch timestamp.