Skip to content
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

Add support for per-stage IAM policy files #272

Merged
merged 5 commits into from
Apr 3, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 34 additions & 13 deletions chalice/deploy/deployer.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from chalice.utils import OSUtils
from chalice.constants import DEFAULT_STAGE_NAME, LAMBDA_TRUST_POLICY
from chalice.constants import CLOUDWATCH_LOGS
from chalice.policy import AppPolicyGenerator


NULLARY = Callable[[], str]
Expand All @@ -37,7 +38,8 @@ def create_default_deployer(session, prompter=None):
osutils = OSUtils()
lambda_deploy = LambdaDeployer(
aws_client, packager, prompter, osutils,
ApplicationPolicyHandler(osutils))
ApplicationPolicyHandler(
osutils, AppPolicyGenerator(osutils)))
return Deployer(api_gateway_deploy, lambda_deploy)


Expand Down Expand Up @@ -346,16 +348,40 @@ def _deploy_api_to_stage(self, rest_api_id, api_gateway_stage, lambda_arn):


class ApplicationPolicyHandler(object):
"""Manages the IAM policy for an application."""
"""Manages the IAM policy for an application.

This class handles returning the policy that used by
used for the API handler lambda function for a given
stage.

It has several possible outcomes:

* By default, it will autogenerate a policy based on
analyzing the application source code.
* It will return a policy from a file on disk that's been
configured as the policy for the given stage.

This class has a precondition that we should be loading
some IAM policy for the the API handler function.

If a user has indicated that there's a pre-existing
role that they'd like to use for the API handler function,
this class should never be invoked. In other words,
the logic of whether or not we even need to bother with
loading an IAM policy is handled a layer above where
this class should be used.

"""

_EMPTY_POLICY = {
'Version': '2012-10-17',
'Statement': [],
}

def __init__(self, osutils):
# type: (OSUtils) -> None
def __init__(self, osutils, policy_generator):
# type: (OSUtils, AppPolicyGenerator) -> None
self._osutils = osutils
self._policy_gen = policy_generator

def generate_policy_from_app_source(self, config):
# type: (Config) -> Dict[str, Any]
Expand All @@ -367,19 +393,14 @@ def generate_policy_from_app_source(self, config):

"""
if config.autogen_policy:
app_policy = self._do_generate_from_source(config.project_dir)
app_policy = self._do_generate_from_source(config)
else:
app_policy = self.load_last_policy(config.project_dir)
return app_policy

def _do_generate_from_source(self, project_dir):
# type: (str) -> Dict[str, Any]
app_py = os.path.join(project_dir, 'app.py')
assert self._osutils.file_exists(app_py)
app_source = self._osutils.get_file_contents(app_py, binary=False)
app_policy = policy.policy_from_source_code(app_source)
app_policy['Statement'].append(CLOUDWATCH_LOGS)
return app_policy
def _do_generate_from_source(self, config):
# type: (Config) -> Dict[str, Any]
return self._policy_gen.generate_policy(config)

def load_last_policy(self, project_dir):
# type: (str) -> Dict[str, Any]
Expand Down
7 changes: 5 additions & 2 deletions chalice/package.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from chalice.utils import OSUtils
from chalice.config import Config # noqa
from chalice.app import Chalice # noqa
from chalice.policy import AppPolicyGenerator


def create_app_packager(config):
Expand All @@ -26,9 +27,11 @@ def create_app_packager(config):
CFNSwaggerGenerator('{region}', '{lambda_arn}'),
PreconfiguredPolicyGenerator(
config,
ApplicationPolicyHandler(osutils))),
ApplicationPolicyHandler(
osutils, AppPolicyGenerator(osutils)))),
LambdaDeploymentPackager(),
ApplicationPolicyHandler(osutils),
# TODO: remove duplication here.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you miss a todo here? Seems like you can share the instance of the policy generator.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ended up being removed due to refactoring in the env var PR (https://github.com/awslabs/chalice/pull/273/files#diff-7637287bcfcc038a2e99fb26ac726348R32)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good

ApplicationPolicyHandler(osutils, AppPolicyGenerator(osutils)),
config.chalice_app,
config.project_dir,
config.autogen_policy)
Expand Down
26 changes: 25 additions & 1 deletion chalice/policy.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,12 @@
import uuid

from typing import Any, List, Dict, Set # noqa

import botocore.session

from chalice.constants import CLOUDWATCH_LOGS
from chalice.utils import OSUtils # noqa
from chalice.config import Config # noqa


def policy_from_source_code(source_code):
# type: (str) -> Dict[str, Any]
Expand Down Expand Up @@ -57,6 +60,27 @@ def _create_simple_format(policy):
return actions


class AppPolicyGenerator(object):
def __init__(self, osutils):
# type: (OSUtils) -> None
self._osutils = osutils

def generate_policy(self, config):
# type: (Config) -> Dict[str, Any]
"""Auto generate policy for an application."""
# Admittedly, this is pretty bare bones logic for the time
# being. All it really does it work out, given a Config instance,
# which files need to analyzed and then delegates to the
# appropriately analyzer functions to do the real work.
# This may change in the future.
app_py = os.path.join(config.project_dir, 'app.py')
assert self._osutils.file_exists(app_py)
app_source = self._osutils.get_file_contents(app_py, binary=False)
app_policy = policy_from_source_code(app_source)
app_policy['Statement'].append(CLOUDWATCH_LOGS)
return app_policy


class PolicyBuilder(object):
VERSION = '2012-10-17'

Expand Down
5 changes: 4 additions & 1 deletion tests/unit/deploy/test_deployer.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from chalice.app import Chalice
from chalice.awsclient import TypedAWSClient
from chalice.config import Config, DeployedResources
from chalice.policy import AppPolicyGenerator
from chalice.deploy.deployer import APIGatewayDeployer
from chalice.deploy.deployer import ApplicationPolicyHandler
from chalice.deploy.deployer import Deployer
Expand Down Expand Up @@ -62,7 +63,9 @@ def in_memory_osutils():

@fixture
def app_policy(in_memory_osutils):
return ApplicationPolicyHandler(in_memory_osutils)
return ApplicationPolicyHandler(
in_memory_osutils,
AppPolicyGenerator(in_memory_osutils))


def stubbed_client(service_name):
Expand Down