awslabs / aws-apigateway-lambda-authorizer-blueprints Goto Github PK
View Code? Open in Web Editor NEWBlueprints and examples for Lambda-based custom Authorizers for use in API Gateway.
License: Apache License 2.0
Blueprints and examples for Lambda-based custom Authorizers for use in API Gateway.
License: Apache License 2.0
Would be nice to have a typescript blueprint.
I even took the time and made something from the node.js blueprint.
Note it should probably need to be reviewed and maybe tested first, I hope I nailed the types correctly, not sure at all about the conditions type:
import {
APIGatewayAuthorizerEvent,
APIGatewayAuthorizerCallback,
APIGatewayEventDefaultAuthorizerContext,
PolicyDocument, ConditionBlock,
AuthResponse,
} from 'aws-lambda';
type Method = {
resourceArn: string;
conditions: ConditionBlock[] | null;
};
type ApiOptions = { restApiId?: any; region?: any; stage?: any; }
export const handler = (event: APIGatewayAuthorizerEvent, _context: APIGatewayEventDefaultAuthorizerContext, callback: APIGatewayAuthorizerCallback) => {
// Do not print the auth token unless absolutely necessary
// console.log('Client token: ' + event.authorizationToken);
console.log('Method ARN: ' + event.methodArn);
// validate the incoming token
// and produce the principal user identifier associated with the token
// this could be accomplished in a number of ways:
// 1. Call out to OAuth provider
// 2. Decode a JWT token inline
// 3. Lookup in a self-managed DB
const principalId = 'user|a1b2c3d4';
// you can send a 401 Unauthorized response to the client by failing like so:
// callback("Unauthorized", null);
// if the token is valid, a policy must be generated which will allow or deny access to the client
// if access is denied, the client will receive a 403 Access Denied response
// if access is allowed, API Gateway will proceed with the backend integration configured on the method that was called
// build apiOptions for the AuthPolicy
const apiOptions:ApiOptions = {};
const tmp = event.methodArn.split(':');
const apiGatewayArnTmp = tmp[5].split('/');
const awsAccountId = tmp[4];
apiOptions.region = tmp[3];
apiOptions.restApiId = apiGatewayArnTmp[0];
apiOptions.stage = apiGatewayArnTmp[1];
const method = apiGatewayArnTmp[2];
const resource = '/'; // root resource
if (apiGatewayArnTmp[3]) {
resource += apiGatewayArnTmp.slice(3, apiGatewayArnTmp.length).join('/');
}
// this function must generate a policy that is associated with the recognized principal user identifier.
// depending on your use case, you might store policies in a DB, or generate them on the fly
// keep in mind, the policy is cached for 5 minutes by default (TTL is configurable in the authorizer)
// and will apply to subsequent calls to any method/resource in the RestApi
// made with the same token
// the example policy below denies access to all resources in the RestApi
const policy = new AuthPolicy(principalId, awsAccountId, apiOptions);
policy.denyAllMethods();
// policy.allowMethod(AuthPolicy.HttpVerb.GET, "/users/username");
// finally, build the policy
const authResponse = policy.build();
// new! -- add additional key-value pairs
// these are made available by APIGW like so: $context.authorizer.<key>
// additional context is cached
authResponse.context = {
key : 'value', // $context.authorizer.key -> value
number : 1,
bool: true
};
// authResponse.context.arr = ['foo']; <- this is invalid, APIGW will not accept it
// authResponse.context.obj = {'foo':'bar'}; <- also invalid
callback(null, authResponse);
};
/**
* AuthPolicy receives a set of allowed and denied methods and generates a valid
* AWS policy for the API Gateway authorizer. The constructor receives the calling
* user principal, the AWS account ID of the API owner, and an apiOptions object.
* The apiOptions can contain an API Gateway RestApi Id, a region for the RestApi, and a
* stage that calls should be allowed/denied for. For example
* {
* restApiId: "xxxxxxxxxx",
* region: "us-east-1",
* stage: "dev"
* }
*
* const testPolicy = new AuthPolicy("[principal user identifier]", "[AWS account id]", apiOptions);
* testPolicy.allowMethod(AuthPolicy.HttpVerb.GET, "/users/username");
* testPolicy.denyMethod(AuthPolicy.HttpVerb.POST, "/pets");
* context.succeed(testPolicy.build());
*
* @class AuthPolicy
* @constructor
*/
class AuthPolicy {
public readonly awsAccountId: string;
public readonly principalId: string;
public readonly version: string;
public readonly pathRegex: RegExp;
public readonly allowMethods: Method[];
public readonly denyMethods: Method[];
public readonly restApiId: string;
public readonly region: string;
public readonly stage: string;
private static HttpVerb: { HEAD: string; ALL: string; DELETE: string; POST: string; GET: string; OPTIONS: string; PUT: string; PATCH: string };
/**
* A set of existing HTTP verbs supported by API Gateway. This property is here
* only to avoid spelling mistakes in the policy.
*
* @property HttpVerb
* @type {Object}
*/
HttpVerb = {
GET : "GET",
POST : "POST",
PUT : "PUT",
PATCH : "PATCH",
HEAD : "HEAD",
DELETE : "DELETE",
OPTIONS : "OPTIONS",
ALL : "*"
};
constructor(principal: string, awsAccountId: string, apiOptions: ApiOptions) {
/**
* The AWS account id the policy will be generated for. This is used to create
* the method ARNs.
*
* @property awsAccountId
* @type {String}
*/
this.awsAccountId = awsAccountId;
/**
* The principal used for the policy, this should be a unique identifier for
* the end user.
*
* @property principalId
* @type {String}
*/
this.principalId = principal;
/**
* The policy version used for the evaluation. This should always be "2012-10-17"
*
* @property version
* @type {String}
* @default "2012-10-17"
*/
this.version = "2012-10-17";
/**
* The regular expression used to validate resource paths for the policy
*
* @property pathRegex
* @type {RegExp}
* @default '^\/[/.a-zA-Z0-9-\*]+$'
*/
this.pathRegex = new RegExp('^[/.a-zA-Z0-9-\*]+$');
// these are the internal lists of allowed and denied methods. These are lists
// of objects and each object has 2 properties: A resource ARN and a nullable
// conditions statement.
// the build method processes these lists and generates the appropriate
// statements for the final policy
this.allowMethods = [];
this.denyMethods = [];
if (!apiOptions || !apiOptions.restApiId) {
// Replace the placeholder value with a default API Gateway API id to be used in the policy.
// Beware of using '*' since it will not simply mean any API Gateway API id, because stars will greedily expand over '/' or other separators.
// See https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_resource.html for more details.
this.restApiId = "<<restApiId>>";
} else {
this.restApiId = apiOptions.restApiId;
}
if (!apiOptions || !apiOptions.region) {
// Replace the placeholder value with a default region to be used in the policy.
// Beware of using '*' since it will not simply mean any region, because stars will greedily expand over '/' or other separators.
// See https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_resource.html for more details.
this.region = "<<region>>";
} else {
this.region = apiOptions.region;
}
if (!apiOptions || !apiOptions.stage) {
// Replace the placeholder value with a default stage to be used in the policy.
// Beware of using '*' since it will not simply mean any stage, because stars will greedily expand over '/' or other separators.
// See https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_resource.html for more details.
this.stage = "<<stage>>";
} else {
this.stage = apiOptions.stage;
}
}
/**
* Adds a method to the internal lists of allowed or denied methods. Each object in
* the internal list contains a resource ARN and a condition statement. The condition
* statement can be null.
*
* @method addMethod
* @param {String} effect: The effect for the policy. This can only be "Allow" or "Deny".
* @param {String} verb: The HTTP verb for the method, this should ideally come from the
* AuthPolicy.HttpVerb object to avoid spelling mistakes
* @param {String} resource: The resource path. For example "/pets"
* @param {Object} conditions: The conditions object in the format specified by the AWS docs.
* @return {void}
*/
addMethod = (effect:string, verb:string, resource:string, conditions:ConditionBlock[] | null):void => {
if (verb != "*" && !AuthPolicy.HttpVerb.hasOwnProperty(verb)) {
throw new Error("Invalid HTTP verb " + verb + ". Allowed verbs in AuthPolicy.HttpVerb");
}
if (!this.pathRegex.test(resource)) {
throw new Error("Invalid resource path: " + resource + ". Path should match " + this.pathRegex);
}
let cleanedResource = resource;
if (resource.substring(0, 1) == "/") {
cleanedResource = resource.substring(1, resource.length);
}
const resourceArn = "arn:aws:execute-api:" +
this.region + ":" +
this.awsAccountId + ":" +
this.restApiId + "/" +
this.stage + "/" +
verb + "/" +
cleanedResource;
if (effect.toLowerCase() == "allow") {
this.allowMethods.push({ resourceArn, conditions });
} else if (effect.toLowerCase() == "deny") {
this.denyMethods.push({ resourceArn, conditions });
}
}
/**
* Returns an empty statement object prepopulated with the correct action and the
* desired effect.
*
* @method getEmptyStatement
* @param {String} effect: The effect of the statement, this can be "Allow" or "Deny"
* @return {Object} An empty statement object with the Action, Effect, and Resource
* properties prepopulated.
*/
getEmptyStatement = (effect:string):any => {
return {
Action: "execute-api:Invoke",
Effect: effect.substring(0, 1).toUpperCase() + effect.substring(1, effect.length).toLowerCase(),
Resource: [],
};
};
/**
* This function loops over an array of objects containing a resourceArn and
* conditions statement and generates the array of statements for the policy.
*
* @method getStatementsForEffect
* @param {String} effect: The desired effect. This can be "Allow" or "Deny"
* @param {Array} methods: An array of method objects containing the ARN of the resource
* and the conditions for the policy
* @return {Array} an array of formatted statements for the policy.
*/
getStatementsForEffect = (effect:string, methods:Method[]):any[] => {
const statements = [];
if (methods.length > 0) {
const statement = this.getEmptyStatement(effect);
for (let i = 0; i < methods.length; i++) {
const curMethod = methods[i];
if (curMethod.conditions === null || curMethod.conditions.length === 0) {
statement.Resource.push(curMethod.resourceArn);
} else {
const conditionalStatement = this.getEmptyStatement(effect);
conditionalStatement.Resource.push(curMethod.resourceArn);
conditionalStatement.Condition = curMethod.conditions;
statements.push(conditionalStatement);
}
}
if (statement.Resource !== null && statement.Resource.length > 0) {
statements.push(statement);
}
}
return statements;
};
/**
* Adds an allow "*" statement to the policy.
*
* @method allowAllMethods
*/
allowAllMethods = ():void => {
this.addMethod.call(this, "allow", "*", "*", null);
}
/**
* Adds a deny "*" statement to the policy.
*
* @method denyAllMethods
*/
denyAllMethods = ():void => {
this.addMethod.call(this, "deny", "*", "*", null);
}
/**
* Adds an API Gateway method (Http verb + Resource path) to the list of allowed
* methods for the policy
*
* @method allowMethod
* @param {String} verb: The HTTP verb for the method, this should ideally come from the
* AuthPolicy.HttpVerb object to avoid spelling mistakes
* @param {string} resource: The resource path. For example "/pets"
* @return {void}
*/
allowMethod = (verb:string, resource:string):void => {
this.addMethod.call(this, "allow", verb, resource, null);
}
/**
* Adds an API Gateway method (Http verb + Resource path) to the list of denied
* methods for the policy
*
* @method denyMethod
* @param {String} verb: The HTTP verb for the method, this should ideally come from the
* AuthPolicy.HttpVerb object to avoid spelling mistakes
* @param {string} resource: The resource path. For example "/pets"
* @return {void}
*/
denyMethod = (verb:string, resource:string):void => {
this.addMethod.call(this, "deny", verb, resource, null);
}
/**
* Adds an API Gateway method (Http verb + Resource path) to the list of allowed
* methods and includes a condition for the policy statement. More on AWS policy
* conditions here: http://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements.html#Condition
*
* @method allowMethodWithConditions
* @param {String} verb: The HTTP verb for the method, this should ideally come from the
* AuthPolicy.HttpVerb object to avoid spelling mistakes
* @param {string} resource: The resource path. For example "/pets"
* @param {Object} conditions: The conditions object in the format specified by the AWS docs
* @return {void}
*/
allowMethodWithConditions = (verb:string, resource:string, conditions:ConditionBlock[]):void => {
this.addMethod.call(this, "allow", verb, resource, conditions);
}
/**
* Adds an API Gateway method (Http verb + Resource path) to the list of denied
* methods and includes a condition for the policy statement. More on AWS policy
* conditions here: http://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements.html#Condition
*
* @method denyMethodWithConditions
* @param {String} verb: The HTTP verb for the method, this should ideally come from the
* AuthPolicy.HttpVerb object to avoid spelling mistakes
* @param {string} resource: The resource path. For example "/pets"
* @param {Object} conditions: The conditions object in the format specified by the AWS docs
* @return {void}
*/
denyMethodWithConditions = (verb:string, resource:string, conditions:ConditionBlock[]):void => {
this.addMethod.call(this, "deny", verb, resource, conditions);
}
/**
* Generates the policy document based on the internal lists of allowed and denied
* conditions. This will generate a policy with two main statements for the effect:
* one statement for Allow and one statement for Deny.
* Methods that includes conditions will have their own statement in the policy.
*
* @method build
* @return {Object} The policy object that can be serialized to JSON.
*/
build = ():AuthResponse => {
if ((!this.allowMethods || this.allowMethods.length === 0) &&
(!this.denyMethods || this.denyMethods.length === 0)) {
throw new Error("No statements defined for the policy");
}
const doc:PolicyDocument = {
Statement: [],
Version: this.version,
};
doc.Statement = doc.Statement.concat(this.getStatementsForEffect.call(this, "Allow", this.allowMethods));
doc.Statement = doc.Statement.concat(this.getStatementsForEffect.call(this, "Deny", this.denyMethods));
return {
principalId: this.principalId,
policyDocument: doc,
};
}
}
I'm an AWS SA based in China. My customer used the python blueprint to try out authorizer in Beijing region and it didn't work (it is indeed listed as Lambda blueprint in China region as well). It turns out the resourceArn in the returned policy should be in the format of 'arn:aws-cn:execute-api...' for China regions. So the code in question is below:
resourceArn = 'arn:aws:execute-api:{}:{}:{}/{}/{}/{}'.format(self.region, self.awsAccountId, self.restApiId, self.stage, verb, resource)
Besides, the input methodArn in China region is in the following format, which contains 'aws-cn':
{ "type": "TOKEN", "methodArn": "arn:aws-cn:execute-api:cn-north-1:182470276418:{rest_api_id}/...", "authorizationToken": "..." }
Resource should not be [] it is string , that causes user not access the resource
{
"principalId": "user",
"policyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Action": "execute-api:Invoke",
"Effect": "Deny",
"Resource": "arn:aws:execute-api:us-west-2:123456789012:ymy8tbxw7b/*/GET/"
}
]
}
}
Using APIGatewayCustomAuthorizerResponse
and related objects from Amazon.Lambda.APIGatewayEvents
would greatly simplify the dotnet example.
After creating an authorizer, you do of course want to unit test that the authorizer is producing policy documents that reflect the methods/resources that should allowed/denied based on the input.
I can't seem to find any tools that can be used locally to evaluate an IAM policy document given an action on a particular resource (or merely a request to an API endpoint).
Yes, you could treat the output of the authorizer as merely a JSON document, and test its content, but this does not provide for testing the semantics of policy evaluation.
Are there any tools that would allow an IAM policy document to be evaluated locally for local testing?
pep8 flags this https://github.com/awslabs/aws-apigateway-lambda-authorizer-blueprints/blob/master/blueprints/python/api-gateway-authorizer-python.py
api-gateway-authorizer-python.py|92 col 17 warning| W605 invalid escape sequence '*' [pep8]
Hi,
I just tried the nodejs blueprint and I think I've come across an error.
When parsing event.methodArn
, the apiGatewayArnTmp
is assigned via:
tmp[5].split('/')
Later on (on line 44), when getting the resource, you take apiGatewayArnTmp[3]
.
If the resource has a slash in it (which it is likely to do since it's a path), you'll only get the first part of the path:
ARN: arn:aws:execute-api:eu-west-1:1234567890:abc123/*/GET/test/deep/path
Yeilds: resource: /test
when it should be /test/deep/path
Changing line 43-45 to the following should fix this issue:
if (apiGatewayArnTmp[3]) {
resource += apiGatewayArnTmp.slice(3, apiGatewayArnTmp.length).join('/');
}
This will correctly yield resource: /test/deep/path
I'm settting some variables (redis data) in the additional key-value pair. I'm able to access those like $context.authorizer.key
in API gateway.
But If I have hundreds of key-value pair then how can I loop it so that it can be pass it to the endpoint.
Calling getPolicyDocument in AuthPolicy.java causing multiple statement duplicates in the returned map. If I call it more than one time (e.g. to log some of policyDocument info or to make multiple assertions in tests) the returned Map (and list of statements of PolicyDocument) contains multiple entries of same statements. This is because getStatement() doesn't just get, it actually adds allow and deny statements on every get call.
When I test in AWS console -> API Gateway -> Authorizers -> my-authorizer with correct token, I receive following policy document:
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "execute-api:Invoke",
"Effect": "Allow",
"Resource": [
"arn:aws:execute-api:us-west-2:hidden:3y5h5l33u1/null/*/*"
]
},
{
"Action": "execute-api:Invoke",
"Effect": "Deny",
"Resource": [
"arn:aws:execute-api:us-west-2:hidden:3y5h5l33u1/null/DELETE/locations",
"arn:aws:execute-api:us-west-2:hidden:3y5h5l33u1/null/DELETE/locations/*",
"arn:aws:execute-api:us-west-2:hidden:3y5h5l33u1/null/POST/locations",
"arn:aws:execute-api:us-west-2:hidden:3y5h5l33u1/null/POST/locations/*"
]
}
]
}
As one can see the apiGatewayArnPartials arrives as "arn:aws:execute-api:us-west-2:hidden:3y5h5l33u1/null//", so,
String stage = apiGatewayArnPartials[1];
parses to null, and all resources receive null value, e.g. "arn:aws:execute-api:us-west-2:hidden:3y5h5l33u1/null/POST/locations"
I even tried to work around by doing this:
stage = ((stage == null) ||(stage.equals("")))? "*":stage;
but it changed nothing.
When I test same authorizer, but from Lambda (AWS console -> Lambda -> Functions -> my-authorizer), everything looks fine (what makes me think that the issue is API Gateway specific):
{
"principalId": "68372295-99bf-4259-b168-b8f702f4fb10",
"policyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Action": "execute-api:Invoke",
"Effect": "Allow",
"Resource": [
"arn:aws:execute-api:us-west-2:hidden:3y5h5l33u1/*/*/*"
]
},
{
"Action": "execute-api:Invoke",
"Effect": "Deny",
"Resource": [
"arn:aws:execute-api:us-west-2:hidden:3y5h5l33u1/*/DELETE/locations",
"arn:aws:execute-api:us-west-2:hidden:3y5h5l33u1/*/DELETE/locations/*",
"arn:aws:execute-api:us-west-2:hidden:3y5h5l33u1/*/POST/locations",
"arn:aws:execute-api:us-west-2:hidden:3y5h5l33u1/*/POST/locations/*"
]
}
]
}
}
This may not be an issue of the blueprints and rather AWS API Gateway internal issue. Please refer me to the right place where I can register it or address it internally to the team if so.
Hello, thank you for the blueprint.
I'm using the nodejs example.
When I want to return a 401 e.g. for expired tokens
callback("Unauthorized", null);
will end the lambda with a Failed Invocation.
We have Alarms on all our Lambdas to get notified on Failed Invocations, and now we get notified on all 401.
How do you guys monitor your Lambda Authorizer ?
Thanks
Hello and thank you for the blueprints.
I'm using the Rust blueprint and I'm trying to authorize all requests (for testing) by using allow_all_methods()
but it's always returning unauthorized.
#![allow(dead_code)]
#[cfg_attr(test, macro_use)]
extern crate lambda_runtime as lambda;
#[cfg_attr(test, macro_use)]
extern crate serde_derive;
#[macro_use]
extern crate log;
extern crate simple_logger;
use lambda::error::HandlerError;
use serde_json::json;
use serde::{Serialize, Deserialize};
use lambda_runtime::lambda;
use std::error::Error;
static POLICY_VERSION: &str = "2012-10-17"; // override if necessary
fn handler(
event: APIGatewayCustomAuthorizerRequest,
_ctx: lambda::Context,
) -> Result<APIGatewayCustomAuthorizerResponse, HandlerError> {
info!("Client token: {}", event.authorization_token);
info!("Method ARN: {}", event.method_arn);
// validate the incoming token
// and produce the principal user identifier associated with the token
// this could be accomplished in a number of ways:
// 1. Call out to OAuth provider
// 2. Decode a JWT token inline
// 3. Lookup in a self-managed DB
let principal_id = "user|a1b2c3d4";
// you can send a 401 Unauthorized response to the client by failing like so:
// Err(HandlerError{ msg: "Unauthorized".to_string(), backtrace: None });
// if the token is valid, a policy must be generated which will allow or deny access to the client
// if access is denied, the client will recieve a 403 Access Denied response
// if access is allowed, API Gateway will proceed with the backend integration configured on the method that was called
// this function must generate a policy that is associated with the recognized principal user identifier.
// depending on your use case, you might store policies in a DB, or generate them on the fly
// keep in mind, the policy is cached for 5 minutes by default (TTL is configurable in the authorizer)
// and will apply to subsequent calls to any method/resource in the RestApi
// made with the same token
//the example policy below denies access to all resources in the RestApi
let tmp: Vec<&str> = event.method_arn.split(":").collect();
let api_gateway_arn_tmp: Vec<&str> = tmp[5].split("/").collect();
let aws_account_id = tmp[4];
let region = tmp[3];
let rest_api_id = api_gateway_arn_tmp[0];
let stage = api_gateway_arn_tmp[1];
let policy = APIGatewayPolicyBuilder::new(region, aws_account_id, rest_api_id, stage)
.allow_all_methods()
.build();
// new! -- add additional key-value pairs associated with the authenticated principal
// these are made available by APIGW like so: $context.authorizer.<key>
// additional context is cached
Ok(APIGatewayCustomAuthorizerResponse {
principal_id: principal_id.to_string(),
policy_document: policy,
context: json!({
"stringKey": "stringval",
"numberKey": 123,
"booleanKey": true
}),
})
}
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
struct APIGatewayCustomAuthorizerRequest {
#[serde(rename = "type")]
_type: String,
authorization_token: String,
method_arn: String,
}
#[derive(Serialize, Deserialize)]
#[allow(non_snake_case)]
struct APIGatewayCustomAuthorizerPolicy {
Version: String,
Statement: Vec<IAMPolicyStatement>,
}
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
struct APIGatewayCustomAuthorizerResponse {
principal_id: String,
policy_document: APIGatewayCustomAuthorizerPolicy,
context: serde_json::Value,
}
#[derive(Serialize, Deserialize)]
#[allow(non_snake_case)]
struct IAMPolicyStatement {
Action: Vec<String>,
Effect: Effect,
Resource: Vec<String>,
}
struct APIGatewayPolicyBuilder {
region: String,
aws_account_id: String,
rest_api_id: String,
stage: String,
policy: APIGatewayCustomAuthorizerPolicy,
}
#[derive(Serialize, Deserialize)]
enum Method {
#[serde(rename = "GET")]
Get,
#[serde(rename = "POST")]
Post,
#[serde(rename = "*PUT")]
Put,
#[serde(rename = "DELETE")]
Delete,
#[serde(rename = "PATCH")]
Patch,
#[serde(rename = "HEAD")]
Head,
#[serde(rename = "OPTIONS")]
Options,
#[serde(rename = "*")]
All,
}
#[derive(Serialize, Deserialize)]
enum Effect {
Allow,
Deny,
}
impl APIGatewayPolicyBuilder {
pub fn new(
region: &str,
account_id: &str,
api_id: &str,
stage: &str,
) -> APIGatewayPolicyBuilder {
Self {
region: region.to_string(),
aws_account_id: account_id.to_string(),
rest_api_id: api_id.to_string(),
stage: stage.to_string(),
policy: APIGatewayCustomAuthorizerPolicy {
Version: POLICY_VERSION.to_string(),
Statement: vec![],
},
}
}
pub fn add_method<T: Into<String>>(
mut self,
effect: Effect,
method: Method,
resource: T,
) -> Self {
let resource_arn = format!(
"arn:aws:execute-api:{}:{}:{}/{}/{}/{}",
&self.region,
&self.aws_account_id,
&self.rest_api_id,
&self.stage,
serde_json::to_string(&method).unwrap(),
resource.into().trim_start_matches("/")
);
let stmt = IAMPolicyStatement {
Effect: effect,
Action: vec!["execute-api:Invoke".to_string()],
Resource: vec![resource_arn],
};
self.policy.Statement.push(stmt);
self
}
pub fn allow_all_methods(self) -> Self {
self.add_method(Effect::Allow, Method::All, "*")
}
pub fn deny_all_methods(self) -> Self {
self.add_method(Effect::Deny, Method::All, "*")
}
pub fn allow_method(self, method: Method, resource: String) -> Self {
self.add_method(Effect::Allow, method, resource)
}
pub fn deny_method(self, method: Method, resource: String) -> Self {
self.add_method(Effect::Deny, method, resource)
}
// Creates and executes a new child thread.
pub fn build(self) -> APIGatewayCustomAuthorizerPolicy {
self.policy
}
}
fn main() -> Result<(), Box<dyn Error>> {
simple_logger::init_with_level(log::Level::Debug)?;
lambda!(handler);
Ok(())
}
With the rust code, when building a policy the add_method uses this code to serialize the Method enum
serde_json::to_string(&method).unwrap()
this creates a policy with double quotes in the method ARN
"arn:aws:execute-api:ap-southeast-2:XXXXXXXXXX:YYYYYYYYYY/ESTestInvoke-stage/\"*\"/*"
Consider using something like this instead ?
json!(&method).as_str().unwrap()
Can we have some unit tests? The logic has quite a lot of conditions.
In Java blue print,
public Map<String, Object> getPolicyDocument() {}
I am wondering if retrieving policy document using IAM sdk would be easy option? why is it not preferred here?
For example AmazonIdentityManagementClient.GetRolePolicyResult() .getPolicyDocument()
Accordingly with the aws documentation : https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-custom-authorizer-output.html
The custom authorizer's Lambda function returns an output that must include the principal identifier (principalId) and a policy document (policyDocument) containing a list of policy statements. The output can also include a context map containing key-value pairs.
The context map containing key-value pairs is missing in the Java implementation. Is it possibile to add it in order to retrive the key-value map in the lamba function that handles the response ?
The authPolicy constructor should be something like:
AuthPolicy(String principalId, PolicyDocument policyDocumentObject, HashMap<String, String> context).
This would be really useful in case of JWT Custom authorizer that need to pass some token extracted information to the lambda.
Hi there, I'm a fellow Amazon developer, I'm wondering if you're open to pull requests to update the nodejs
example to modern JavaScript? I just spent a few hours completing a ES7 rewrite of the module that's supported in the Node.js v8.10 runtime. I feel like it may better lay out some of the core concepts this example library is trying to convey, and update some of the functional foundations around syntax like let
and const
as well as arrow functions and other JS features that ES7 and AWS Lambda now support.
If you are, just let me know and I'll put a PR up. Cheers
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.