Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
markpeek committed Mar 27, 2013
0 parents commit 78b4419
Show file tree
Hide file tree
Showing 18 changed files with 632 additions and 0 deletions.
35 changes: 35 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
*.py[cod]

# C extensions
*.so

# Packages
*.egg
*.egg-info
dist
build
eggs
parts
bin
var
sdist
develop-eggs
.installed.cfg
lib
lib64

# Installer logs
pip-log.txt

# Unit test / coverage reports
.coverage
.tox
nosetests.xml

# Translations
*.mo

# Mr Developer
.mr.developer.cfg
.project
.pydevproject
23 changes: 23 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
Copyright (c) 2012-2013, Mark Peek <[email protected]>
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
4 changes: 4 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
include LICENSE
include README.md
include .gitignore
recursive-include examples *.py
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
awacs - Amazon Web Access Control Subsystem

The awacs library allows for easier creation of AWS Access Policy
Language JSON by writing Python code to describe the AWS policies.
To facilitate catching policy format or JSON errors early the
library has property and type checking built into the classes.
126 changes: 126 additions & 0 deletions awacs/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
# Copyright (c) 2012-2013, Mark Peek <[email protected]>
# All rights reserved.
#
# See LICENSE file for full license.


import json
import re
import types

__version__ = "0.1.0"


valid_names = re.compile(r'^[a-zA-Z0-9]+$')


class AWSObject(object):
def __init__(self, name, type=None, dictname=None, props={}, **kwargs):
self.name = name
self.props = props
# Cache the keys for validity checks
self.propnames = props.keys()

# unset/None is also legal
if name and not valid_names.match(name):
raise ValueError('Name not alphanumeric')

# Create the list of properties set on this object by the user
self.properties = {}
if dictname:
self.resource = {
dictname: self.properties,
}
else:
self.resource = self.properties
self.__initialized = True

# Now that it is initialized, populate it with the kwargs
for k, v in kwargs.items():
self.__setattr__(k, v)

def __getattr__(self, name):
try:
return self.properties.__getitem__(name)
except KeyError:
raise AttributeError(name)

def __setattr__(self, name, value):
if '_AWSObject__initialized' not in self.__dict__:
return dict.__setattr__(self, name, value)
elif name in self.propnames:
# Check the type of the object and compare against what we were
# expecting.
expected_type = self.props[name][0]

# If it's a function, call it...
if isinstance(expected_type, types.FunctionType):
value = expected_type(value)
return self.properties.__setitem__(name, value)

# If it's a list of types, check against those types...
elif isinstance(expected_type, list):
# If we're expecting a list, then make sure it is a list
if not isinstance(value, list):
self._raise_type(name, value, expected_type)

# Iterate over the list and make sure it matches our
# type checks
for v in value:
if not isinstance(v, tuple(expected_type)):
self._raise_type(name, v, expected_type)
# Validated so assign it
return self.properties.__setitem__(name, value)

# Single type so check the type of the object and compare against
# what we were expecting. Special case AWS helper functions.
elif isinstance(value, expected_type) or \
isinstance(value, AWSHelperFn):
return self.properties.__setitem__(name, value)
else:
self._raise_type(name, value, expected_type)

raise AttributeError("%s object does not support attribute %s" %
(self.type, name))

def _raise_type(self, name, value, expected_type):
raise TypeError('%s is %s, expected %s' %
(name, type(value), expected_type))

def validate(self):
pass

def JSONrepr(self):
for k, v in self.props.items():
if v[1] and k not in self.properties:
raise ValueError("Resource %s required in type %s" %
(k, self.type))
self.validate()
return self.resource


class AWSProperty(AWSObject):
"""
Used for CloudFormation Resource Property objects
http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/
aws-product-property-reference.html
"""

def __init__(self, **kwargs):
sup = super(AWSProperty, self)
sup.__init__(None, props=self.props, **kwargs)


class AWSHelperFn(object):
def getdata(self, data):
if isinstance(data, AWSObject):
return data.name
else:
return data


class awsencode(json.JSONEncoder):
def default(self, obj):
if hasattr(obj, 'JSONrepr'):
return obj.JSONrepr()
return json.JSONEncoder.default(self, obj)
150 changes: 150 additions & 0 deletions awacs/aws.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
# Copyright (c) 2012-2013, Mark Peek <[email protected]>
# All rights reserved.
#
# See LICENSE file for full license.

import json
from . import AWSHelperFn, AWSProperty, awsencode


# Policy effect constants.
Allow = "Allow"
Deny = "Deny"

# Policy principal constants.
Everybody = "*"

# Policy condition key constants.
CurrentTime = "aws:CurrentTime"
EpochTime = "aws:EpochTime"
MultiFactorAuthAge = "aws:MultiFactorAuthAge"
Referer = "aws:Referer"
SecureTransport = "aws:SecureTransport"
SourceArn = "aws:SourceArn"
SourceIp = "aws:SourceIp"
UserAgent = "aws:UserAgent"


class Action(AWSHelperFn):
def __init__(self, action):
self.data = action

def JSONrepr(self):
return self.data


class ARN(AWSHelperFn):
def __init__(self, resource, region, account, data):
self.data = "arn:aws:%s:%s:%s:%s" % (resource, region, account, data)

def JSONrepr(self):
return self.data


class ConditionElement(AWSHelperFn):
def __init__(self, key, value):
self.key = key
self.value = value


class Condition(AWSHelperFn):
def __init__(self, conditions):
if isinstance(conditions, ConditionElement):
self.conditions = [conditions]
elif isinstance(conditions, list):
for c in conditions:
if not isinstance(c, ConditionElement):
raise ValueError(
"ConditionElement is type %s" % (type(c),))
self.conditions = conditions
else:
raise TypeError

def JSONrepr(self):
d = {}
for c in self.conditions:
d[c.condition] = {c.key: c.value}
return d


class Principal(AWSHelperFn):
def __init__(self, principal, resources):
self.data = {principal: resources}

def JSONrepr(self):
return self.data


class AWSPrincipal(Principal):
def __init__(self, principals):
sup = super(AWSPrincipal, self)
sup.__init__('AWS', principals)


def effect(x):
if x not in [Allow, Deny]:
raise ValueError(x)
return x


class Statement(AWSProperty):
props = {
'Action': ([Action], False),
'Condition': (Condition, False),
'Effect': (effect, True),
'NotAction': (list, False),
'NotPrincipal': ([Principal], False),
'Principal': ([Principal], False),
'Resource': (list, False),
'NotResource': (list, False),
'Sid': (basestring, False),
}


class Policy(AWSProperty):
props = {
'Id': (basestring, False),
'Statement': ([Statement], True),
'Version': (basestring, False),
}

def to_json(self, indent=4, sort_keys=True):
p = self.properties
return json.dumps(p, cls=awsencode, indent=indent, sort_keys=sort_keys)

def JSONrepr(self):
return self.properties


_condition_strings = [
"ArnEquals",
"ArnNotEquals",
"ArnLike",
"ArnNotLike",
"Bool",
"DateEquals",
"DateNotEquals",
"DateLessThan",
"DateLessThanEquals",
"DateGreaterThan",
"DateGreaterThanEquals",
"IpAddress",
"NotIpAddress",
"NumericEquals",
"NumericNotEquals",
"NumericLessThan",
"NumericLessThanEquals",
"NumericGreaterThan",
"NumericGreaterThanEquals",
"StringEquals",
"StringNotEquals",
"StringEqualsIgnoresCase",
"StringLike",
"StringNotLike",
]

# Create condition classes
for i in _condition_strings:
globals()[i] = type(i, (ConditionElement,), dict(condition=i))
i = i + "IfExists"
globals()[i] = type(i, (ConditionElement,), dict(condition=i))
20 changes: 20 additions & 0 deletions awacs/cloudformation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Copyright (c) 2012-2013, Mark Peek <[email protected]>
# All rights reserved.
#
# See LICENSE file for full license.

from aws import Action


CreateStack = Action("cloudformation:CreateStack")
DeleteStack = Action("cloudformation:DeleteStack")
DescribeStackEvents = Action("cloudformation:DescribeStackEvents")
DescribeStackResource = Action("cloudformation:DescribeStackResource")
DescribeStackResources = Action("cloudformation:DescribeStackResources")
DescribeStacks = Action("cloudformation:DescribeStacks")
EstimateTemplateCost = Action("cloudformation:EstimateTemplateCost")
GetTemplate = Action("cloudformation:GetTemplate")
ListStackResources = Action("cloudformation:ListStackResources")
ListStacks = Action("cloudformation:ListStacks")
UpdateStack = Action("cloudformation:UpdateStack")
ValidateTemplate = Action("cloudformation:ValidateTemplate")
21 changes: 21 additions & 0 deletions awacs/directconnect.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Copyright (c) 2012-2013, Mark Peek <[email protected]>
# All rights reserved.
#
# See LICENSE file for full license.

from aws import Action


CreateConnection = Action("directconnect:CreateConnection")
CreatePrivateVirtualInterface = \
Action("directconnect:CreatePrivateVirtualInterface")
CreatePublicVirtualInterface = \
Action("directconnect:CreatePublicVirtualInterface")
DeleteConnection = Action("directconnect:DeleteConnection")
DeleteVirtualInterface = Action("directconnect:DeleteVirtualInterface")
DescribeConnectionDetail = Action("directconnect:DescribeConnectionDetail")
DescribeConnections = Action("directconnect:DescribeConnections")
DescribeOfferingDetail = Action("directconnect:DescribeOfferingDetail")
DescribeOfferings = Action("directconnect:DescribeOfferings")
DescribeVirtualGateways = Action("directconnect:DescribeVirtualGateways")
DescribeVirtualInterfaces = Action("directconnect:DescribeVirtualInterfaces")
Loading

0 comments on commit 78b4419

Please sign in to comment.