Skip to content
This repository was archived by the owner on Nov 3, 2020. It is now read-only.

Commit 5d73761

Browse files
committed
Merge branch 'release/0.10.0'
2 parents 49a540f + 0c7a2ef commit 5d73761

File tree

6 files changed

+2148
-1226
lines changed

6 files changed

+2148
-1226
lines changed

.eslintrc.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
extends: airbnb-base
2+
rules:
3+
no-use-before-define:
4+
- off
5+
max-len:
6+
- error
7+
- code: 120

.flake8

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[flake8]
2+
exclude = .git
3+
max-line-length = 119

basic_auth.py

Lines changed: 28 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,56 @@
11
import base64
2-
import json
3-
import re
42
import boto3
53

64

75
def basicAuth(event, context):
6+
# Return a policy which allows this user to access to this api
7+
# this call is cached for all authenticated calls, so we need to give
8+
# access to the whole api. This could be done by having a policyDocument
9+
# for each available function, but I don't really care :)
10+
arn = "%s/*" % "/".join(event["methodArn"].split("/")[0:2])
11+
812
# if a basic auth header is set, use that to find the correct user/token
9-
if 'Authorization' in event['headers']:
10-
authorizationHeader = event['headers']['Authorization']
11-
b64_token = authorizationHeader.split(' ')[-1]
13+
if "Authorization" in event["headers"]:
14+
authorizationHeader = event["headers"]["Authorization"]
15+
b64_token = authorizationHeader.split(" ")[-1]
1216

1317
# decode the base64 encoded header value
14-
username, token = base64.b64decode(b64_token).decode("utf-8").split(':')
15-
18+
username, token = base64.b64decode(b64_token).decode("utf-8").split(":")
1619
# search for the given api key
17-
client = boto3.client('apigateway')
20+
client = boto3.client("apigateway")
1821
response = client.get_api_keys(nameQuery=username, includeValues=True)
1922

2023
# if no keys found, deny access
21-
if len(response['items']) != 1:
24+
if len(response["items"]) != 1:
2225
print("Couldn't find key")
23-
raise Exception('Unauthorized!')
26+
raise Exception("Unauthorized")
2427

2528
# if the key value does not match, deny access
26-
if response['items'][0]['value'] != token:
29+
if response["items"][0]["value"] != token:
2730
print("Key value mismatch")
28-
raise Exception('Unauthorized!!')
31+
raise Exception("Unauthorized")
2932

3033
# check if an x-api-token header is set, if so, take it as-is, api gateway
3134
# will check the validity
32-
elif 'x-api-key' in event['headers']:
33-
username = 'token'
34-
token = event['headers']['x-api-key']
35+
elif "x-api-key" in event["headers"]:
36+
print("x-api-key received")
37+
username = "token"
38+
token = event["headers"]["x-api-key"]
3539

3640
# no authentication headers found, deny
3741
else:
3842
print("No authentication header found")
39-
raise Exception('Please provide authentication details')
40-
41-
# Return a policy which allows this user to access to this api
42-
# this call is cached for all authenticated calls, so we need to give
43-
# access to the whole api. This could be done by having a policyDocument
44-
# for each available function, but I don't really care :)
45-
arn = "%s/*" % '/'.join(event['methodArn'].split("/")[0:2])
43+
raise Exception("Unauthorized")
4644

4745
authResponse = {
48-
'principalId': username,
49-
'usageIdentifierKey': token,
50-
'policyDocument': {
51-
'Version': '2012-10-17',
52-
'Statement': [{
53-
'Action': 'execute-api:Invoke',
54-
'Effect': 'Allow',
55-
'Resource': arn
56-
}]
57-
}
46+
"principalId": username,
47+
"usageIdentifierKey": token,
48+
"policyDocument": {
49+
"Version": "2012-10-17",
50+
"Statement": [
51+
{"Action": "execute-api:Invoke", "Effect": "Allow", "Resource": arn}
52+
],
53+
},
5854
}
5955
print("Authentication response: %s" % authResponse)
6056

index.js

Lines changed: 77 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1,109 +1,101 @@
1-
'use strict'
21
const fs = require('fs');
32
const chalk = require('chalk');
43

5-
6-
class SetupBasicAuthentication {
7-
constructor (serverless, options) {
4+
module.exports = class SetupBasicAuthentication {
5+
constructor(serverless, options) {
6+
this.options = options;
7+
this.serverless = serverless;
88

99
// add the basic authentication function to the functions as soon as possible
10-
injectBasicAuthFunction(serverless);
10+
this.injectBasicAuthFunction(serverless);
1111

1212
this.hooks = {
13-
'before:package:initialize': function () {
14-
// add our custom authenticator
15-
addAuthFileToPackage(serverless);
16-
17-
addAuthorizerFunctionToPrivateFunctions(serverless);
18-
},
19-
'after:package:createDeploymentArtifacts': function () {
20-
// remove the custom authenticator
21-
removeFileFromPackage(serverless)
22-
},
23-
'before:deploy:deploy': function() {
24-
// // add the basic authenticator function
25-
// injectBasicAuthFunction(serverless);
26-
27-
// configure api gateway to check for the right place for the key
28-
configureApiGatewayKeySource(serverless);
29-
}
30-
}
13+
'before:package:initialize': this.addAuthorizer.bind(this),
14+
'after:package:createDeploymentArtifacts': this.removeAuthorizer.bind(this),
15+
'before:deploy:deploy': this.configureApiGatewayKeySource.bind(this),
16+
};
3117
}
32-
}
3318

34-
function removeFileFromPackage(serverless) {
35-
serverless.cli.consoleLog('Basic Authentication: ' + chalk.yellow('Removing Symlink for Basic Authenticator'));
36-
fs.unlinkSync(serverless.config.servicePath + "/basic_auth.py")
37-
}
19+
addAuthorizer() {
20+
// add our custom authenticator
21+
this.addAuthFileToPackage();
3822

39-
function addAuthFileToPackage(serverless) {
40-
if(!serverless.package) {
41-
serverless.package = {}
23+
this.addAuthorizerFunctionToPrivateFunctions();
4224
}
43-
if(!serverless.package.include) {
44-
serverless.package.include = []
25+
26+
removeAuthorizer() {
27+
this.serverless.cli.consoleLog(`Basic Authentication: ${chalk.yellow('Removing Symlink for Basic Authenticator')}`);
28+
fs.unlinkSync(`${this.serverless.config.servicePath}/basic_auth.py`);
4529
}
4630

47-
serverless.cli.consoleLog('Basic Authentication: ' + chalk.yellow('Adding Symlink for Basic Authenticator'));
48-
// @TODO: Make target filename randomized with something, to prevent overriding
49-
// any files
31+
addAuthFileToPackage() {
32+
if (!this.serverless.package) {
33+
this.serverless.package = {};
34+
}
35+
36+
if (!this.serverless.package.include) {
37+
this.serverless.package.include = [];
38+
}
5039

51-
// append our auth.py file to the package
52-
serverless.package.include.push(__dirname + "/auth.py")
53-
fs.symlinkSync(__dirname + "/basic_auth.py", serverless.config.servicePath + "/basic_auth.py")
54-
}
40+
this.serverless.cli.consoleLog(`Basic Authentication: ${chalk.yellow('Adding Symlink for Basic Authenticator')}`);
41+
// @TODO: Make target filename randomized with something, to prevent overriding
42+
// any files
5543

56-
function injectBasicAuthFunction (serverless) {
57-
serverless.cli.consoleLog('Basic Authentication: ' + chalk.yellow('Adding function for Basic Authenticator'));
58-
var basicAuthenticator = {
59-
handler: 'basic_auth.basicAuth',
60-
runtime: 'python3.6'
44+
// append our auth.py file to the package
45+
this.serverless.package.include.push(`${__dirname}/auth.py`);
46+
fs.symlinkSync(`${__dirname}/basic_auth.py`, `${this.serverless.config.servicePath}/basic_auth.py`);
6147
}
6248

63-
// add the basic authenticator function
64-
serverless.service.functions.basicAuthenticator = basicAuthenticator;
65-
}
66-
67-
function addAuthorizerFunctionToPrivateFunctions(serverless) {
68-
// for each function which is marked as 'private', set the basic authenticator
69-
// if it doesn't have a custom authenticator yet
70-
for(let function_name in serverless.service.functions) {
49+
injectBasicAuthFunction() {
50+
this.serverless.cli.consoleLog(`Basic Authentication: ${chalk.yellow('Adding function for Basic Authenticator')}`);
51+
const basicAuthenticator = {
52+
handler: 'basic_auth.basicAuth',
53+
runtime: 'python3.6',
54+
};
7155

72-
// ignore our own function
73-
if(function_name == 'basicAuthenticator') {
74-
continue;
75-
}
56+
// add the basic authenticator function
57+
this.serverless.service.functions.basicAuthenticator = basicAuthenticator;
58+
}
7659

77-
var fnctn = serverless.service.functions[function_name];
78-
79-
// check if any of the http events is marked as private, and if that event
80-
// also doesn't have a custom authorizer already, apply our authenticator
81-
for(let fnctn_event in fnctn['events']) {
82-
if(
83-
serverless.service.functions[function_name].events[fnctn_event].http != null &&
84-
serverless.service.functions[function_name].events[fnctn_event].http.private == true &&
85-
serverless.service.functions[function_name].events[fnctn_event].http.authorizer == null
86-
) {
87-
serverless.service.functions[function_name].events[fnctn_event].http.authorizer = {
88-
name: 'basicAuthenticator',
89-
identitySource: '', // this is only valid if we set cache ttl to 0
90-
resultTtlInSeconds: 0,
91-
type: 'REQUEST'
60+
addAuthorizerFunctionToPrivateFunctions() {
61+
// for each function which is marked as 'private', set the basic authenticator
62+
// if it doesn't have a custom authenticator yet
63+
Object.keys(this.serverless.service.functions).forEach((functionName) => {
64+
// ignore our own function
65+
if (functionName === 'basicAuthenticator') {
66+
return;
67+
}
9268

69+
// get all function configs
70+
const fnctn = this.serverless.service.functions[functionName];
71+
72+
// check if any of the http events is marked as private, and if that event
73+
// also doesn't have a custom authorizer already, apply our authenticator
74+
Object.keys(fnctn.events).forEach((fnctnEvent) => {
75+
if (
76+
this.serverless.service.functions[functionName].events[fnctnEvent].http != null
77+
&& this.serverless.service.functions[functionName].events[fnctnEvent].http.private === true
78+
&& this.serverless.service.functions[functionName].events[fnctnEvent].http.authorizer == null
79+
) {
80+
this.serverless.service.functions[functionName].events[fnctnEvent].http.authorizer = {
81+
name: 'basicAuthenticator',
82+
identitySource: '', // this is only valid if we set cache ttl to 0
83+
resultTtlInSeconds: 0,
84+
type: 'REQUEST',
85+
};
86+
this.serverless.cli.consoleLog(`Basic Authentication: ${chalk.yellow(`Enabled for ${functionName}`)}`);
9387
}
94-
serverless.cli.consoleLog('Basic Authentication: ' + chalk.yellow('Enabled for ' + function_name));
95-
}
96-
}
88+
});
89+
});
9790
}
98-
}
9991

100-
function configureApiGatewayKeySource(serverless) {
101-
var template = serverless.service.provider.compiledCloudFormationTemplate;
102-
if(template.Resources.ApiGatewayRestApi != null) {
103-
serverless.cli.consoleLog('Basic Authentication: ' + chalk.yellow('Configuring Api Gateway for Basic Authenticator'));
104-
template.Resources.ApiGatewayRestApi.Properties.ApiKeySourceType = 'AUTHORIZER'
92+
configureApiGatewayKeySource() {
93+
const template = this.serverless.service.provider.compiledCloudFormationTemplate;
94+
if (template.Resources.ApiGatewayRestApi != null) {
95+
this.serverless.cli.consoleLog(
96+
`Basic Authentication: ${chalk.yellow('Configuring Api Gateway for Basic Authenticator')}`,
97+
);
98+
template.Resources.ApiGatewayRestApi.Properties.ApiKeySourceType = 'AUTHORIZER';
99+
}
105100
}
106-
}
107-
108-
// now we need to make our plugin object available to the framework to execute
109-
module.exports = SetupBasicAuthentication
101+
};

0 commit comments

Comments
 (0)