class IAM_Policy: def __init__(self, policy_name=None, policy_path=None): self.iam = IAM() self.policy_name = policy_name self.version = "2012-10-17" self.statements = [] self.policy_path = policy_path self.account_id = self.iam.account_id() def add_cloud_watch(self, resource_arn): return self.add_statement_allow([ "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents" ], [resource_arn]) def add_statement(self, effect, actions, resources): self.statements.append({ "Effect": effect, "Action": actions, "Resource": resources }) return self def add_statement_allow(self, actions, resources): return self.add_statement('Allow', actions, resources) def create(self, delete_before_create=False): if self.policy_name is None: return {'status': 'error', 'data': 'policy name is None'} return self.iam.policy_create( self.policy_name, self.statement(), delete_before_create=delete_before_create) def delete(self): return self.iam.policy_delete(self.policy_arn()) def exists(self): return self.iam.policy_exists(self.policy_arn()) def policy_arn(self): return self.iam.policy_arn(self.policy_name, self.policy_path, self.account_id) def statement(self): return {'Version': self.version, 'Statement': self.statements} def statement_from_aws(self): return self.iam.policy_statement(self.policy_arn())
def policy_create_for_execution_role(self, role_name): region = 'eu-west-2' cloud_watch_arn = "arn:aws:logs:{0}:{1}:log-group:awslogs-*".format( region, self.account_id) role_policy = { "Version": "2008-10-17", "Statement": [{ "Effect": "Allow", "Principal": { "Service": "ecs-tasks.amazonaws.com" }, "Action": "sts:AssumeRole" }] } policy = { "Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Action": [ "ecr:GetAuthorizationToken", "ecr:BatchCheckLayerAvailability", "ecr:GetDownloadUrlForLayer", "ecr:GetRepositoryPolicy", "ecr:DescribeRepositories", "ecr:ListImages", "ecr:DescribeImages", "ecr:BatchGetImage" ], "Resource": "*" }, { "Effect": "Allow", "Action": ["logs:CreateLogStream", "logs:PutLogEvents"], "Resource": [cloud_watch_arn] }] } policy_name = 'policy_for_{0}'.format(role_name) iam = IAM(role_name=role_name) iam.role_create(role_policy) iam.policy_delete(policy_name) policy_arn = iam.policy_create(policy_name, policy).get('policy_arn') iam.role_policy_attach(policy_arn)
class Test_IAM(Test_Helper): @classmethod def setUpClass(cls): import warnings warnings.filterwarnings("ignore", category=ResourceWarning, message="unclosed.*<ssl.SSLSocket.*>") iam = IAM(user_name=test_user, role_name=test_role) if iam.user_exists() is False: iam.user_create() if iam.role_exists() is False: iam.role_create(policy_document) @classmethod def tearDownClass(cls): if delete_created: iam = IAM(user_name=test_user,role_name=test_role) iam.user_delete() assert iam.user_exists() is False iam.role_delete() assert iam.role_exists() is False assert iam.role_arn() is None def setUp(self): super().setUp() import warnings warnings.filterwarnings("ignore", category=ResourceWarning, message="unclosed.*<ssl.SSLSocket.*>") self.iam = IAM(user_name=test_user,role_name=test_role ) # ------ tests ------ @unittest.skip("Doesn't work in CodeBuild since there is only one configuration in there") def test_account_id(self): account_id_1 = self.iam.account_id('gs-detect-aws') # todo: rewrite since account_id doesn't take this parameter any more assert AWS_Config().aws_session_profile_name() == 'gs-detect-aws' self.iam._account_id = None self.iam._sts = None account_id_2 = self.iam.account_id('default') assert AWS_Config().aws_session_profile_name() == 'default' assert account_id_1 != account_id_2 self.iam._account_id = None self.iam._sts = None account_id_3 = self.iam.account_id() assert AWS_Config().aws_session_profile_name() == 'default' assert account_id_2 == account_id_3 @pytest.mark.skip('Fix test') def test_access_keys(self): assert len(self.iam.access_keys(index_by='AccessKeyId')) > 0 assert len(self.iam.access_keys(group_by='UserName' )) > 0 def test_caller_identity(self): assert set(self.iam.caller_identity()) == {'UserId', 'Account', 'Arn'} @pytest.mark.skip('Fix test') def test_groups(self): assert len(self.iam.groups()) > 5 def test_policies(self): assert len(self.iam.policies()) > 500 def test_policy_arn(self): assert self.iam.policy_arn('aaa' ) == 'arn:aws:iam::{0}:policy/aaa' .format(account_id) assert self.iam.policy_arn('aaa/bbb' ) == 'arn:aws:iam::{0}:policy/aaa/bbb'.format(account_id) assert self.iam.policy_arn('aa','/bb' ) == 'arn:aws:iam::{0}:policy/bb/aa' .format(account_id) assert self.iam.policy_arn('aa','/bb','cc') == 'arn:aws:iam::cc:policy/bb/aa' assert self.iam.policy_arn(None) is None def test_policy_create__policy_delete__policy_details(self): policy_name = 'test_policy' self.iam.policy_delete_by_name(policy_name) new_policy_document = { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "lambda:InvokeFunction", "Resource": "arn:aws:lambda:*:*:function:*" }]} result = self.iam.policy_create(policy_name, new_policy_document) expected_policy_arn = self.iam.policy_arn(policy_name) status = result.get('status') policy_arn = result.get('policy_arn') assert status == 'ok' assert policy_arn == expected_policy_arn assert self.iam.policy_info (policy_arn).get('PolicyName' ) == 'test_policy' assert self.iam.policy_details(policy_arn).get('policy_version').get('Document') == new_policy_document assert self.iam.policy_delete(policy_arn) is True def test_policy_name_exists(self): assert self.iam.policy_exists_by_name('aaa' ) is False assert self.iam.policy_exists_by_name('AWSBatchServiceRole' ) is False assert self.iam.policy_exists_by_name('AWSBatchServiceRole','/service-role' ) is False assert self.iam.policy_exists_by_name('AWSBatchServiceRole', '/service-role', 'aws') is True def test_policy_info(self): assert self.iam.policy_info('AAAAAA') is None #def test_user_create(self): # convered in setUpClass # self.iam.user_create() #def test_user_delete(self): # convered in tearDownClass # Dev.pprint(self.iam.user_delete()) def test_user_exists(self): assert self.iam .user_exists() is True assert self.iam.set_user_name('aAAA').user_exists() is False def test_user_info(self): user = self.iam.user_info() ( Assert(user).field_is_equal('Arn' , test_user_arn) .field_is_equal('Path' , '/') .field_is_equal('UserName', test_user) ) assert self.iam.set_user_name('AAAA').user_info().get('error') is not None def test_users(self): assert len(list(self.iam.users())) > 5 def test_role_create(self): self.iam.role_delete() role = self.iam.role_create(policy_document) ( Assert(role).field_is_equal('Arn' ,test_role_arn) .field_is_equal('Path' ,'/') .field_is_equal('RoleName' , test_role) .field_is_equal('AssumeRolePolicyDocument', policy_document) ) assert self.iam.role_arn() == test_role_arn def test_role_create_assume_role(self): sts = STS() current_user_arn = sts.caller_identity_arn() original_policy = {'Statement': [ { 'Action' : 'sts:AssumeRole', 'Effect' : 'Allow', 'Principal': { 'Service': 'codebuild.amazonaws.com'}}]} new_policy = {'Statement': [{'Action' : 'sts:AssumeRole', 'Effect' : 'Allow', 'Principal': {'AWS': current_user_arn } }]} test_role = IAM(role_name="temp_role_to_test_assume_role") test_role.role_create(original_policy) role_arn = test_role.role_arn() current_assume_policy = test_role.role_assume_policy() test_role.role_assume_policy_update(new_policy) for i in range(0,15): with Catch(log_exception=False): sts.assume_role(role_arn=role_arn) sts.assume_role(role_arn=role_arn) sts.assume_role(role_arn=role_arn) sts.assume_role(role_arn=role_arn) sts.assume_role(role_arn=role_arn) sts.assume_role(role_arn=role_arn) sts.assume_role(role_arn=role_arn) sts.assume_role(role_arn=role_arn) pprint('got credentials') break print(f'after {i} seconds') wait(1) assert sts.assume_role(role_arn=role_arn).get('Credentials') is not None test_role.role_assume_policy_update(current_assume_policy) assert test_role.role_assume_policy() == current_assume_policy test_role.role_delete() # credentials = sts.assume_role(role_arn=role_arn).get('Credentials') # aws_access_key_id = credentials.get('AccessKeyId') # aws_secret_access_key = credentials.get('SecretAccessKey') # aws_session_token = credentials.get('SessionToken') # # import boto3 # session = boto3.Session(aws_access_key_id=aws_access_key_id, # aws_secret_access_key=aws_secret_access_key, # aws_session_token=aws_session_token) def test_role_info(self): role = self.iam.set_role_name(test_role).role_info() # also tests the set_role_name function assert role.get('Arn' ) == test_role_arn assert role.get('RoleName') == test_role @pytest.mark.skip('Fix test') def test_role_policies(self): policies = self.iam.role_policies() assert len(set(policies)) == 0 iam = IAM(role_name='AWSServiceRoleForAPIGateway') assert iam.role_policies() == {'APIGatewayServiceRolePolicy': 'arn:aws:iam::aws:policy/aws-service-role/APIGatewayServiceRolePolicy'} assert len(iam.role_policies_statements().get('APIGatewayServiceRolePolicy')[0].get('Action')) > 10 def test_role_policies_attach__role_policies_detach(self): policy_name = 'test_policy' policy_document = {"Version": "2012-10-17", # refactor this with policy helper "Statement": [{ "Effect": "Allow", "Action": "lambda:InvokeFunction", "Resource": "arn:aws:lambda:*:*:function:*"}]} policy_arn = self.iam.policy_create(policy_name, policy_document).get('policy_arn') assert len(list(self.iam.role_policies())) == 0 self.iam.role_policy_attach(policy_arn) assert list(self.iam.role_policies()) == ['test_policy'] self.iam.role_policy_detach(policy_arn) assert self.iam.policy_exists(policy_arn) is True assert self.iam.policy_delete(policy_arn) is True # this will not delete a policy that is attached assert self.iam.policy_exists(policy_arn) is False #@pytest.mark.skip('Fix test') def test_roles(self): assert len(self.iam.roles()) > 5 def test_user_access_key_create__delete(self): access_key = self.iam.user_access_key_create() assert self.iam.access_key__wait_until_key_is_working (access_key,success_count=1) is True self.iam.user_access_keys_delete_all() assert self.iam.access_key__wait_until_key_is_not_working(access_key, success_count=1) is True
class CodeBuild: def __init__(self, project_name, role_name): self.codebuild = Session().client('codebuild') self.iam = IAM(role_name=role_name) self.project_name = project_name return def _invoke_via_paginator(self, method, field_id, use_paginator, **kwargs): paginator = self.codebuild.get_paginator(method) for page in paginator.paginate(**kwargs): for id in page.get(field_id): yield id if use_paginator is False: return def all_builds_ids(self, use_paginator = False): return self._invoke_via_paginator('list_builds','ids',use_paginator) def build_info(self, build_id): builds = self.codebuild.batch_get_builds(ids=[build_id]).get('builds') return Misc.array_pop(builds,0) def build_start(self): kvargs = { 'projectName': self.project_name } return self.codebuild.start_build(**kvargs).get('build').get('arn') def build_wait_for_completion(self, build_id, sleep_for=0.5, max_attempts=20, log_status=False): for i in range(0,max_attempts): build_info = self.build_info(build_id) build_status = build_info.get('buildStatus') current_phase = build_info.get('currentPhase') if log_status: Dev.pprint("[{0}] {1} {2}".format(i,build_status,current_phase)) if build_status != 'IN_PROGRESS': return build_info sleep(sleep_for) return None def policies_create(self, policies): # does not update, only add new ones policies_arns = [] role_policies = list(self.iam.role_policies().keys()) for base_name, policy in policies.items(): policy_name = "{0}_{1}".format(base_name, self.project_name) if policy_name in role_policies: continue policies_arns.append(self.iam.policy_create(policy_name,policy).get('policy_arn')) return policies_arns def project_builds(self,ids): return self.codebuild.batch_get_builds(ids=ids) def project_create(self, project_repo, service_role): kvargs = { 'name': self.project_name, 'source': {'type': 'GITHUB', 'location': project_repo}, 'artifacts': {'type': 'NO_ARTIFACTS'}, 'environment': {'type': 'LINUX_CONTAINER', 'image': 'aws/codebuild/python:3.7.1-1.7.0', 'computeType': 'BUILD_GENERAL1_SMALL'}, 'serviceRole': service_role } return self.codebuild.create_project(**kvargs) def project_delete(self): if self.project_exists() is False: return False self.codebuild.delete_project(name=self.project_name) return self.project_exists() is False def project_exists(self): return self.project_name in self.projects() def project_info(self): projects = Misc.get_value(self.codebuild.batch_get_projects(names=[self.project_name]),'projects',[]) return Misc.array_pop(projects,0) def project_builds_ids(self, project_name, use_paginator=False): if use_paginator: kwargs = { 'projectName' : project_name } else: kwargs = { 'projectName' : project_name , 'sortOrder' : 'DESCENDING' } return self._invoke_via_paginator('list_builds_for_project', 'ids',use_paginator, **kwargs) def projects(self): return self.codebuild.list_projects().get('projects')
class Test_IAM(TestCase): @classmethod def setUpClass(cls): import warnings warnings.filterwarnings("ignore", category=ResourceWarning, message="unclosed.*<ssl.SSLSocket.*>") iam = IAM(user_name=test_user, role_name=test_role) if iam.user_exists() is False: iam.user_create() if iam.role_exists() is False: iam.role_create(policy_document) @classmethod def tearDownClass(cls): if delete_created: iam = IAM(user_name=test_user, role_name=test_role) iam.user_delete() assert iam.user_exists() is False iam.role_delete() assert iam.role_exists() is False assert iam.role_arn() is None def setUp(self): import warnings warnings.filterwarnings("ignore", category=ResourceWarning, message="unclosed.*<ssl.SSLSocket.*>") self.iam = IAM(user_name=test_user, role_name=test_role) @unittest.skip( "Doesn't work in CodeBuild since there is only one configuration in there" ) def test_account_id(self): account_id_1 = self.iam.account_id('gs-detect-aws') assert Globals.aws_session_profile_name == 'gs-detect-aws' self.iam._account_id = None self.iam._sts = None account_id_2 = self.iam.account_id('default') assert Globals.aws_session_profile_name == 'default' assert account_id_1 != account_id_2 self.iam._account_id = None self.iam._sts = None account_id_3 = self.iam.account_id() assert Globals.aws_session_profile_name == 'default' assert account_id_2 == account_id_3 # def test_assume_role(self): # getting `(AccessDenied) when calling the AssumeRole operation: Access denied` # role_arn = 'arn:aws:iam::244560807427:role/temp_role_for_lambda_invocation' # needs to be non account_id specific # role_session_name = 'temp_role_for_test' # Dev.pprint(self.iam.assume_role(role_arn,role_session_name)) def test_caller_identity(self): assert set(self.iam.caller_identity()) == {'UserId', 'Account', 'Arn'} def test_groups(self): assert len(self.iam.groups()) > 5 def test_policies(self): assert len(self.iam.policies()) > 500 def test_policy_arn(self): #account_id = self.iam.account_id() assert self.iam.policy_arn( 'aaa') == 'arn:aws:iam::{0}:policy/aaa'.format(account_id) assert self.iam.policy_arn( 'aaa/bbb') == 'arn:aws:iam::{0}:policy/aaa/bbb'.format(account_id) assert self.iam.policy_arn( 'aa', '/bb') == 'arn:aws:iam::{0}:policy/bb/aa'.format(account_id) assert self.iam.policy_arn('aa', '/bb', 'cc') == 'arn:aws:iam::cc:policy/bb/aa' assert self.iam.policy_arn(None) is None self.iam.set_account_id('12345') assert self.iam.policy_arn( 'aaa') == 'arn:aws:iam::{0}:policy/aaa'.format('12345') def test_policy_create__policy_delete__policy_details(self): policy_name = 'test_policy' self.iam.policy_delete_by_name(policy_name) new_policy_document = { "Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Action": "lambda:InvokeFunction", "Resource": "arn:aws:lambda:*:*:function:*" }] } result = self.iam.policy_create(policy_name, new_policy_document) expected_policy_arn = self.iam.policy_arn(policy_name) status = result.get('status') policy_arn = result.get('policy_arn') assert status == 'ok' assert policy_arn == expected_policy_arn assert self.iam.policy_info(policy_arn).get( 'PolicyName') == 'test_policy' assert self.iam.policy_details(policy_arn).get('policy_version').get( 'Document') == new_policy_document assert self.iam.policy_delete(policy_arn) is True def test_policy_name_exists(self): assert self.iam.policy_exists_by_name('aaa') is False assert self.iam.policy_exists_by_name('AWSBatchServiceRole') is False assert self.iam.policy_exists_by_name('AWSBatchServiceRole', '/service-role') is False assert self.iam.policy_exists_by_name('AWSBatchServiceRole', '/service-role', 'aws') is True def test_policy_info(self): assert self.iam.policy_info('AAAAAA') is None #def test_user_create(self): # self.iam.user_create() #def test_user_delete(self): # Dev.pprint(self.iam.user_delete()) def test_user_exists(self): assert self.iam.user_exists() is True assert self.iam.set_user_name('aAAA').user_exists() is False def test_user_info(self): user = self.iam.user_info() (Assert(user).field_is_equal('Arn', test_user_arn).field_is_equal( 'Path', '/').field_is_equal('UserName', test_user)) assert self.iam.set_user_name('AAAA').user_info() is None def test_users(self): assert len(self.iam.users()) > 10 def test_role_create(self): self.iam.role_delete() role = self.iam.role_create(policy_document) (Assert(role).field_is_equal('Arn', test_role_arn).field_is_equal( 'Path', '/').field_is_equal('RoleName', test_role).field_is_equal( 'AssumeRolePolicyDocument', policy_document)) assert self.iam.role_arn() == test_role_arn def test_role_info(self): role = self.iam.set_role_name( test_role).role_info() # also tests the set_role_name function assert role.get('Arn') == test_role_arn assert role.get('RoleName') == test_role def test_role_policies(self): policies = self.iam.role_policies() assert len(set(policies)) == 0 def test_role_policies(self): iam = IAM(role_name='AWSBatchServiceRole') assert iam.role_policies() == { 'AWSBatchServiceRole': 'arn:aws:iam::aws:policy/service-role/AWSBatchServiceRole' } assert len(iam.role_policies_statements().get('AWSBatchServiceRole') [0].get('Action')) == 59 def test_role_policies_attach__role_policies_detach(self): policy_name = 'test_policy' policy_document = { "Version": "2012-10-17", # refactor this with policy helper "Statement": [{ "Effect": "Allow", "Action": "lambda:InvokeFunction", "Resource": "arn:aws:lambda:*:*:function:*" }] } policy_arn = self.iam.policy_create(policy_name, policy_document).get('policy_arn') assert len(list(self.iam.role_policies())) == 0 self.iam.role_policy_attach(policy_arn) assert list(self.iam.role_policies()) == ['test_policy'] self.iam.role_policy_detach(policy_arn) assert self.iam.policy_exists(policy_arn) is True assert self.iam.policy_delete( policy_arn ) is True # this will not delete a policy that is attached assert self.iam.policy_exists(policy_arn) is False def test_roles(self): assert len(self.iam.roles()) > 70
class IAM_Role: def __init__(self, role_name=None): self.role_name = role_name or f"osbot_temp_role_{random_string()}" self.iam = IAM(role_name=self.role_name) def add_policy_for__lambda(self): temp_policy_name = 'policy_{0}'.format(self.role_name) cloud_watch_arn = f'arn:aws:logs:{AWS_Config().aws_session_region_name()}:{AWS_Config().aws_session_account_id()}:log-group:/aws/lambda/*' iam_policy = IAM_Policy(temp_policy_name) policy_arn = iam_policy.add_cloud_watch(cloud_watch_arn).create().get( 'policy_arn') self.iam.role_policy_attach(policy_arn) return policy_arn def arn(self): return self.iam.role_arn() def attach_policy(self, policy_name, policy_document): self.delete_policy(policy_name=policy_name) result_create = self.iam.policy_create(policy_name=policy_name, policy_document=policy_document) policy_arn = result_create.get('policy_arn') self.iam.role_policy_attach(policy_arn=policy_arn) return policy_arn def create(self, policy_document, skip_if_exists=True): self.iam.role_create(policy_document=policy_document, skip_if_exists=skip_if_exists) return self.exists() def create_for__lambda(self): result = self.create_for_service__assume_role('lambda.amazonaws.com') if result.get('status') == 'ok': self.add_policy_for__lambda() return result def create_for__code_build(self): return self.create_for_service__assume_role('codebuild.amazonaws.com') def create_for_service__assume_role(self, service): statement = { 'Action': 'sts:AssumeRole', 'Effect': 'Allow', 'Principal': { 'Service': service } } return self.create_from_statement(statement) def create_for_service(self, service, statement): statement['Principal'] = {'Service': service} return self.create_from_statement(statement) def create_for_service_with_policies(self, service, policies, project_name, recreate_policy=False): role = self.create_for_service__assume_role(service) role_arn = role.get('role_arn') policies_arns = self.iam.policies_create(policies, project_name, recreate_policy) self.iam.role_policies_attach(policies_arns) return {"role_arn": role_arn, "policies_arns": policies_arns} def create_from_statement(self, statement): return self.create_from_statements([statement]) def create_from_statements(self, statement): role_arn = self.iam.role_arn() if role_arn: return { 'status': 'warning', 'data': 'role already exists', 'role_name': self.iam.role_name, 'role_arn': role_arn } else: policy_document = {'Statement': statement} data = self.iam.role_create(policy_document) return { 'status': 'ok', 'data': data, 'role_name': self.iam.role_name, 'role_arn': data.get('Arn') } def delete(self): return self.iam.role_delete() def delete_policy(self, policy_arn=None, policy_name=None): return self.iam.policy_delete(policy_arn=policy_arn, policy_name=policy_name) def exists(self): return self.iam.role_exists() def info(self): return self.iam.role_info() def not_exists(self): return self.iam.role_not_exists() def policies_statements(self): return self.iam.role_policies_statements()