Skip to content

Instantly share code, notes, and snippets.

@ravituvar
Created August 8, 2017 09:25
Show Gist options
  • Save ravituvar/b309c3ce43c2822e9f14e7711f225315 to your computer and use it in GitHub Desktop.
Save ravituvar/b309c3ce43c2822e9f14e7711f225315 to your computer and use it in GitHub Desktop.
const jwt = require('jsonwebtoken');
const region = 'us-east-1';
exports.handler = function(event, context) {
jwt.verify(event.authorizationToken, secret, function(err, payload) {
if(err) {
context.fail("Unauthorized");
} else {
var principalId = payload.sub;
var apiOptions = {};
var tmp = event.methodArn.split(':');
var apiGatewayArnTmp = tmp[5].split('/');
var awsAccountId = tmp[4];
apiOptions.region = tmp[3];
apiOptions.restApiId = apiGatewayArnTmp[0];
apiOptions.stage = apiGatewayArnTmp[1];
var method = apiGatewayArnTmp[2];
var resource = '/';
if (apiGatewayArnTmp[3]) {
resource += apiGatewayArnTmp[3];
}
var policy = new AuthPolicy(principalId, awsAccountId, apiOptions);
policy.allowAllMethods();
context.succeed(policy.build());
}
});
};
function AuthPolicy(principal, awsAccountId, apiOptions) {
this.awsAccountId = awsAccountId;
this.principalId = principal;
this.version = "2012-10-17";
this.pathRegex = new RegExp('^[/.a-zA-Z0-9-\*]+$');
this.allowMethods = [];
this.denyMethods = [];
if (!apiOptions || !apiOptions.restApiId) {
this.restApiId = "*";
} else {
this.restApiId = apiOptions.restApiId;
}
if (!apiOptions || !apiOptions.region) {
this.region = "*";
} else {
this.region = apiOptions.region;
}
if (!apiOptions || !apiOptions.stage) {
this.stage = "*";
} else {
this.stage = apiOptions.stage;
}
};
AuthPolicy.HttpVerb = {
GET : "GET",
POST : "POST",
PUT : "PUT",
PATCH : "PATCH",
HEAD : "HEAD",
DELETE : "DELETE",
OPTIONS : "OPTIONS",
ALL : "*"
};
AuthPolicy.prototype = (function() {
var addMethod = function(effect, verb, resource, conditions) {
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);
}
var cleanedResource = resource;
if (resource.substring(0, 1) == "/") {
cleanedResource = resource.substring(1, resource.length);
}
var resourceArn = "arn:aws:execute-api:" +
this.region + ":" +
this.awsAccountId + ":" +
this.restApiId + "/" +
this.stage + "/" +
verb + "/" +
cleanedResource;
if (effect.toLowerCase() == "allow") {
this.allowMethods.push({
resourceArn: resourceArn,
conditions: conditions
});
} else if (effect.toLowerCase() == "deny") {
this.denyMethods.push({
resourceArn: resourceArn,
conditions: conditions
})
}
};
var getEmptyStatement = function(effect) {
effect = effect.substring(0, 1).toUpperCase() + effect.substring(1, effect.length).toLowerCase();
var statement = {};
statement.Action = "execute-api:Invoke";
statement.Effect = effect;
statement.Resource = [];
return statement;
};
var getStatementsForEffect = function(effect, methods) {
var statements = [];
if (methods.length > 0) {
var statement = getEmptyStatement(effect);
for (var i = 0; i < methods.length; i++) {
var curMethod = methods[i];
if (curMethod.conditions === null || curMethod.conditions.length === 0) {
statement.Resource.push(curMethod.resourceArn);
} else {
var conditionalStatement = 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;
};
return {
constructor: AuthPolicy,
allowAllMethods: function() {
addMethod.call(this, "allow", "*", "*", null);
},
denyAllMethods: function() {
addMethod.call(this, "deny", "*", "*", null);
},
allowMethod: function(verb, resource) {
addMethod.call(this, "allow", verb, resource, null);
},
denyMethod : function(verb, resource) {
addMethod.call(this, "deny", verb, resource, null);
},
allowMethodWithConditions: function(verb, resource, conditions) {
addMethod.call(this, "allow", verb, resource, conditions);
},
denyMethodWithConditions : function(verb, resource, conditions) {
addMethod.call(this, "deny", verb, resource, conditions);
},
build: function() {
if ((!this.allowMethods || this.allowMethods.length === 0) &&
(!this.denyMethods || this.denyMethods.length === 0)) {
throw new Error("No statements defined for the policy");
}
var policy = {};
policy.principalId = this.principalId;
var doc = {};
doc.Version = this.version;
doc.Statement = [];
doc.Statement = doc.Statement.concat(getStatementsForEffect.call(this, "Allow", this.allowMethods));
doc.Statement = doc.Statement.concat(getStatementsForEffect.call(this, "Deny", this.denyMethods));
policy.policyDocument = doc;
return policy;
}
};
})();
@ravituvar
Copy link
Author

var https = require('https');

exports.handler = function (event, context) {

if (!event.authorizationToken) {
    context.fail(event.authorizationToken);
}

var options = {
hostname: process.env.AuthHostName,
port: process.env.AuthPort,
path: process.env.AuthPath,
headers: {
'Authorization': event.authorizationToken
}
};
https.get(options, function (result) {
if(result.statusCode==200){
var principalId = "AuthTokenPrincipal";
var apiOptions = {};
var tmp = event.methodArn.split(':');
var apiGatewayArnTmp = tmp[5].split('/');
var awsAccountId = tmp[4];
apiOptions.region = tmp[3];
apiOptions.restApiId = apiGatewayArnTmp[0];
apiOptions.stage = apiGatewayArnTmp[1];
var method = apiGatewayArnTmp[2];
var resource = '/';
if (apiGatewayArnTmp[3]) {
resource += apiGatewayArnTmp[3];
}
var policy = new AuthPolicy(principalId, awsAccountId, apiOptions);
policy.allowAllMethods();
context.succeed(policy.build());
}else{
context.fail("Unauthorized");
}
}).on('error', function (err) {
context.fail("Unauthorized");
});
};
function AuthPolicy(principal, awsAccountId, apiOptions) {
this.awsAccountId = awsAccountId;
this.principalId = principal;
this.version = "2012-10-17";
this.pathRegex = new RegExp('^[/.a-zA-Z0-9-*]+$');
this.allowMethods = [];
this.denyMethods = [];

if (!apiOptions || !apiOptions.restApiId) {
    this.restApiId = "*";
} else {
    this.restApiId = apiOptions.restApiId;
}
if (!apiOptions || !apiOptions.region) {
    this.region = "*";
} else {
    this.region = apiOptions.region;
}
if (!apiOptions || !apiOptions.stage) {
    this.stage = "*";
} else {
    this.stage = apiOptions.stage;
}

};
AuthPolicy.HttpVerb = {
GET: "GET",
POST: "POST",
PUT: "PUT",
PATCH: "PATCH",
HEAD: "HEAD",
DELETE: "DELETE",
OPTIONS: "OPTIONS",
ALL: "*"
};

AuthPolicy.prototype = (function () {
var addMethod = function (effect, verb, resource, conditions) {
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);
    }

    var cleanedResource = resource;
    if (resource.substring(0, 1) == "/") {
        cleanedResource = resource.substring(1, resource.length);
    }
    var resourceArn = "arn:aws:execute-api:" +
        this.region + ":" +
        this.awsAccountId + ":" +
        this.restApiId + "/" +
        this.stage + "/" +
        verb + "/" +
        cleanedResource;

    if (effect.toLowerCase() == "allow") {
        this.allowMethods.push({
            resourceArn: resourceArn,
            conditions: conditions
        });
    } else if (effect.toLowerCase() == "deny") {
        this.denyMethods.push({
            resourceArn: resourceArn,
            conditions: conditions
        })
    }
};

var getEmptyStatement = function (effect) {
    effect = effect.substring(0, 1).toUpperCase() + effect.substring(1, effect.length).toLowerCase();
    var statement = {};
    statement.Action = "execute-api:Invoke";
    statement.Effect = effect;
    statement.Resource = [];

    return statement;
};

var getStatementsForEffect = function (effect, methods) {
    var statements = [];

    if (methods.length > 0) {
        var statement = getEmptyStatement(effect);

        for (var i = 0; i < methods.length; i++) {
            var curMethod = methods[i];
            if (curMethod.conditions === null || curMethod.conditions.length === 0) {
                statement.Resource.push(curMethod.resourceArn);
            } else {
                var conditionalStatement = 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;
};

return {
    constructor: AuthPolicy,
    allowAllMethods: function () {
        addMethod.call(this, "allow", "*", "*", null);
    },

    denyAllMethods: function () {
        addMethod.call(this, "deny", "*", "*", null);
    },

    allowMethod: function (verb, resource) {
        addMethod.call(this, "allow", verb, resource, null);
    },

    denyMethod: function (verb, resource) {
        addMethod.call(this, "deny", verb, resource, null);
    },

    allowMethodWithConditions: function (verb, resource, conditions) {
        addMethod.call(this, "allow", verb, resource, conditions);
    },

    denyMethodWithConditions: function (verb, resource, conditions) {
        addMethod.call(this, "deny", verb, resource, conditions);
    },

    build: function () {
        if ((!this.allowMethods || this.allowMethods.length === 0) &&
            (!this.denyMethods || this.denyMethods.length === 0)) {
            throw new Error("No statements defined for the policy");
        }

        var policy = {};
        policy.principalId = this.principalId;
        var doc = {};
        doc.Version = this.version;
        doc.Statement = [];

        doc.Statement = doc.Statement.concat(getStatementsForEffect.call(this, "Allow", this.allowMethods));
        doc.Statement = doc.Statement.concat(getStatementsForEffect.call(this, "Deny", this.denyMethods));

        policy.policyDocument = doc;

        return policy;
    }
};

})();

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment