Beispiel #1
0
def test_role_get_permissions_for_policy_version_no_policies(
        mock_get_permissions_in_policy, role_dict):
    r = Role(**role_dict)
    r.policies = {}
    r.get_permissions_for_policy_version()

    mock_get_permissions_in_policy.assert_not_called()
Beispiel #2
0
    def test_calculate_repo_scores(self, mock_get_repoable_permissions,
                                   mock_get_role_permissions):
        roles = [Role(ROLES[0]), Role(ROLES[1]), Role(ROLES[2])]
        roles[0].disqualified_by = []
        roles[0].aa_data = 'some_aa_data'

        # disqualified by a filter
        roles[1].policies = [{'Policy': ROLE_POLICIES['unused_ec2']}]
        roles[1].disqualified_by = ['some_filter']
        roles[1].aa_data = 'some_aa_data'

        # no AA data
        roles[2].policies = [{'Policy': ROLE_POLICIES['all_services_used']}]
        roles[2].disqualified_by = []
        roles[2].aa_data = None

        mock_get_role_permissions.side_effect = [[
            'iam:AddRoleToInstanceProfile', 'iam:AttachRolePolicy',
            'ec2:AllocateHosts', 'ec2:AssociateAddress'
        ], ['iam:AddRoleToInstanceProfile', 'iam:AttachRolePolicy'
            ], ['iam:AddRoleToInstanceProfile', 'iam:AttachRolePolicy']]

        mock_get_repoable_permissions.side_effect = [
            set(['iam:AddRoleToInstanceProfile', 'iam:AttachRolePolicy'])
        ]

        minimum_age = 90
        repokid.utils.roledata._calculate_repo_scores(roles, minimum_age)

        assert roles[0].repoable_permissions == 2
        assert roles[0].repoable_services == ['iam']
        assert roles[1].repoable_permissions == 0
        assert roles[1].repoable_services == []
        assert roles[2].repoable_permissions == 0
        assert roles[2].repoable_services == []
Beispiel #3
0
def test_role_is_eligible_for_repo_stale_aa_data(mock_stale_aa_services, role_dict):
    mock_stale_aa_services.return_value = ["service1", "service2"]
    r = Role(**role_dict)
    eligible, reason = r.is_eligible_for_repo()
    mock_stale_aa_services.assert_called_once()
    assert not eligible
    assert reason == "stale Access Advisor data for service1, service2"
Beispiel #4
0
def test_role_fetch_aa_data_no_arn(role_dict):
    role_data = copy.deepcopy(role_dict)
    role_data.pop("arn")
    role_data.pop("account")
    r = Role(**role_data)
    with pytest.raises(ModelError):
        r.fetch_aa_data()
Beispiel #5
0
def test_role_is_eligible_for_repo_no_aa_data(mock_stale_aa_services, role_dict):
    r = Role(**role_dict)
    r.aa_data = []
    eligible, reason = r.is_eligible_for_repo()
    mock_stale_aa_services.assert_not_called()
    assert not eligible
    assert reason == "no Access Advisor data available"
Beispiel #6
0
def cancel_scheduled_repo(account_number, dynamo_table, role_name=None, is_all=None):
    """
    Cancel scheduled repo for a role in an account
    """
    if not is_all and not role_name:
        LOGGER.error('Either a specific role to cancel or all must be provided')
        return

    if is_all:
        roles = Roles([Role(get_role_data(dynamo_table, roleID))
                      for roleID in role_ids_for_account(dynamo_table, account_number)])

        # filter to show only roles that are scheduled
        roles = [role for role in roles if (role.repo_scheduled)]

        for role in roles:
            set_role_data(dynamo_table, role.role_id, {'RepoScheduled': 0, 'ScheduledPerms': []})

        LOGGER.info('Canceled scheduled repo for roles: {}'.format(', '.join([role.role_name for role in roles])))
        return

    role_id = find_role_in_cache(dynamo_table, account_number, role_name)
    if not role_id:
        LOGGER.warn('Could not find role with name {} in account {}'.format(role_name, account_number))
        return

    role = Role(get_role_data(dynamo_table, role_id))

    if not role.repo_scheduled:
        LOGGER.warn('Repo was not scheduled for role {} in account {}'.format(role.role_name, account_number))
        return

    set_role_data(dynamo_table, role.role_id, {'RepoScheduled': 0, 'ScheduledPerms': []})
    LOGGER.info('Successfully cancelled scheduled repo for role {} in account {}'.format(role.role_name,
                role.account))
Beispiel #7
0
def list_role_rollbacks(message: Message) -> ResponderReturn:
    role_id = find_role_in_cache(message.role_name, message.account)

    if not role_id:
        return ResponderReturn(
            successful=False,
            return_message="Unable to find role {} in account {}".format(
                message.role_name, message.account
            ),
        )

    role = Role(role_id=role_id)
    role.fetch(fields=["Policies"])
    return_val = "Restorable versions for role {} in account {}\n".format(
        message.role_name, message.account
    )
    for index, policy_version in enumerate(role.policies):
        total_permissions, _ = get_permissions_in_policy(policy_version["Policy"])
        return_val += "({:>3}):  {:<5}     {:<15}  {}\n".format(
            index,
            len(total_permissions),
            policy_version["Discovered"],
            policy_version["Source"],
        )
    return ResponderReturn(successful=True, return_message=return_val)
Beispiel #8
0
def test_role_update_opt_out_future(role_dict):
    r = Role(**role_dict)
    future_dt = datetime.datetime.now() + datetime.timedelta(days=1)
    r.opt_out = {"expire": future_dt.timestamp()}
    r._update_opt_out()
    # opt out should not have been touched since it is not expired
    assert r.opt_out == {"expire": future_dt.timestamp()}
Beispiel #9
0
def test_role_mark_inactive(mock_store, role_dict):
    r = Role(**role_dict)
    r.active = True
    r.mark_inactive()
    assert not r.active
    mock_store.assert_called_once()
    assert mock_store.call_args[1]["fields"] == ["Active"]
Beispiel #10
0
def test_role_is_eligible_for_repo_disqualified(mock_stale_aa_services, role_dict):
    r = Role(**role_dict)
    r.disqualified_by = ["filter1", "filter2"]
    eligible, reason = r.is_eligible_for_repo()
    mock_stale_aa_services.assert_not_called()
    assert not eligible
    assert reason == "disqualified by filter1, filter2"
Beispiel #11
0
def test_role_stale_aa_services(role_dict):
    r = Role(**role_dict)
    r.config["repo_requirements"] = {"oldest_aa_data_days": 5}
    recent_dt = datetime.datetime.now() - datetime.timedelta(days=1)
    older_dt = datetime.datetime.now() - datetime.timedelta(days=14)

    r.aa_data = [
        {
            "serviceName": "service1",
            "lastUpdated": recent_dt.isoformat()
        },
        {
            "serviceName": "service2",
            "lastUpdated": recent_dt.isoformat()
        },
        {
            "serviceName": "service3",
            "lastUpdated": older_dt.isoformat()
        },
        {
            "serviceName": "service4",
            "lastUpdated": older_dt.isoformat()
        },
    ]
    stale = r._stale_aa_services()
    assert "service1" not in stale
    assert "service2" not in stale
    assert "service3" in stale
    assert "service4" in stale
Beispiel #12
0
def test_role_is_eligible_for_repo(mock_stale_aa_services, role_dict):
    mock_stale_aa_services.return_value = []
    r = Role(**role_dict)
    eligible, reason = r.is_eligible_for_repo()
    mock_stale_aa_services.assert_called_once()
    assert eligible
    assert not reason
Beispiel #13
0
def _repo_role(
    account_number: str,
    role_name: str,
    config: RepokidConfig,
    hooks: RepokidHooks,
    commit: bool = False,
    scheduled: bool = False,
) -> List[str]:
    """
    Calculate what repoing can be done for a role and then actually do it if commit is set
      1) Check that a role exists, it isn't being disqualified by a filter, and that is has fresh AA data
      2) Get the role's current permissions, repoable permissions, and the new policy if it will change
      3) Make the changes if commit is set
    Args:
        account_number (string)
        role_name (string)
        commit (bool)

    Returns:
        None
    """
    role_id = find_role_in_cache(role_name, account_number)
    # only load partial data that we need to determine if we should keep going
    role = Role(role_id=role_id, config=config)
    role.fetch()
    return role.repo(hooks, commit=commit, scheduled=scheduled)
Beispiel #14
0
def list_repoable_services(message: Message) -> ResponderReturn:
    role_id = find_role_in_cache(message.role_name, message.account)

    if not role_id:
        return ResponderReturn(
            successful=False,
            return_message="Unable to find role {} in account {}".format(
                message.role_name, message.account),
        )
    else:
        role = Role(role_id=role_id)
        role.fetch(fields=["RepoableServices"])

        (
            repoable_permissions,
            repoable_services,
        ) = get_services_and_permissions_from_repoable(role.repoable_services)

        return ResponderReturn(
            successful=True,
            return_message=
            ("Role {} in account {} has:\n    Repoable Services: \n{}\n\n    Repoable Permissions: \n{}"
             .format(
                 message.role_name,
                 message.account,
                 "\n".join([service for service in repoable_services]),
                 "\n".join([perm for perm in repoable_permissions]),
             )),
        )
Beispiel #15
0
def test_role_calculate_repo_scores(
    mock_get_permissions_for_policy_version,
    mock_get_repoable_permissions,
    mock_convert_repoable_perms_to_perms_and_services,
    role_dict,
):
    mock_get_permissions_for_policy_version.return_value = (
        {
            "service1:action1", "service1:action2", "service2",
            "service3:action3"
        },
        {"service1:action2", "service2", "service3:action3"},
    )
    mock_get_repoable_permissions.return_value = {
        "service1:action2", "service2"
    }
    mock_convert_repoable_perms_to_perms_and_services.return_value = (
        {"service1:action2"},
        {"service2"},
    )

    r = Role(**role_dict)
    r.calculate_repo_scores(0, {})

    mock_get_permissions_for_policy_version.assert_called_once()
    mock_get_repoable_permissions.assert_called_once()
    mock_convert_repoable_perms_to_perms_and_services.assert_called_once()
    assert r.total_permissions == 4
    assert r.repoable_services == ["service1:action2", "service2"]
    assert r.repoable_permissions == 2
Beispiel #16
0
    def test_log_during_repoable_calculation_batch_hooks(self):
        hooks = {
            "DURING_REPOABLE_CALCULATION_BATCH":
            [log_during_repoable_calculation_batch_hooks]
        }

        input_dict = {
            "role_batch": [Role.parse_obj(ROLES[0]), "def"],
            "potentially_repoable_permissions": [],
            "minimum_age": 1,
        }

        with pytest.raises(repokid.hooks.MissingHookParamaeter):
            # role_batch', 'potentially_repoable_permissions', 'minimum_age'
            repokid.hooks.call_hooks(hooks,
                                     "DURING_REPOABLE_CALCULATION_BATCH",
                                     input_dict)

        input_dict["role_batch"] = [
            Role.parse_obj(ROLES[0]),
            Role.parse_obj(ROLES[1]),
            Role.parse_obj(ROLES[2]),
        ]
        assert input_dict == repokid.hooks.call_hooks(
            hooks, "DURING_REPOABLE_CALCULATION_BATCH", input_dict)
Beispiel #17
0
    def test_get_role_permissions(self, mock_all_permissions,
                                  mock_get_actions_from_statement,
                                  mock_expand_policy):
        test_role = Role(ROLES[0])

        all_permissions = [
            "ec2:associateaddress",
            "ec2:attachvolume",
            "ec2:createsnapshot",
            "s3:createbucket",
            "s3:getobject",
        ]

        # empty policy to make sure we get the latest
        test_role.policies = [
            {
                "Policy": ROLE_POLICIES["all_services_used"]
            },
            {
                "Policy": ROLE_POLICIES["unused_ec2"]
            },
        ]

        mock_all_permissions.return_value = all_permissions
        mock_get_actions_from_statement.return_value = ROLE_POLICIES[
            "unused_ec2"]["ec2_perms"]
        mock_expand_policy.return_value = ROLE_POLICIES["unused_ec2"][
            "ec2_perms"]

        total_permissions, eligible_permissions = repokid.utils.roledata._get_role_permissions(
            test_role)
        assert total_permissions == set(
            ROLE_POLICIES["unused_ec2"]["ec2_perms"])
        assert eligible_permissions == set(
            ROLE_POLICIES["unused_ec2"]["ec2_perms"])
Beispiel #18
0
    def test_get_role_permissions(self, mock_all_permissions,
                                  mock_get_actions_from_statement,
                                  mock_expand_policy):
        test_role = Role(ROLES[0])

        all_permissions = [
            'ec2:associateaddress', 'ec2:attachvolume', 'ec2:createsnapshot',
            's3:createbucket', 's3:getobject'
        ]

        # empty policy to make sure we get the latest
        test_role.policies = [{
            'Policy': ROLE_POLICIES['all_services_used']
        }, {
            'Policy': ROLE_POLICIES['unused_ec2']
        }]

        mock_all_permissions.return_value = all_permissions
        mock_get_actions_from_statement.return_value = ROLE_POLICIES[
            'unused_ec2']['ec2_perms']
        mock_expand_policy.return_value = ROLE_POLICIES['unused_ec2'][
            'ec2_perms']

        permissions = repokid.utils.roledata._get_role_permissions(test_role)
        assert permissions == set(ROLE_POLICIES['unused_ec2']['ec2_perms'])
Beispiel #19
0
def test_role_fetch(mock_get_role_by_id, role_dict):
    stored_role_data = copy.deepcopy(role_dict)
    stored_role_data["repoable_permissions"] = 20
    mock_get_role_by_id.return_value = stored_role_data
    r = Role(**role_dict)
    assert r.repoable_permissions == 5
    r.fetch()
    assert r.repoable_permissions == 20
Beispiel #20
0
def test_role_is_eligible_for_repo_no_repoable_permissions(
        mock_stale_aa_services, role_dict):
    r = Role(**role_dict)
    r.repoable_permissions = []
    eligible, reason = r.is_eligible_for_repo()
    mock_stale_aa_services.assert_not_called()
    assert not eligible
    assert reason == "no repoable permissions"
Beispiel #21
0
def test_role_fetch_not_found(role_dict):
    local_role_data = copy.deepcopy(role_dict)
    local_role_data.pop("role_id")
    local_role_data.pop("role_name")
    local_role_data.pop("account")
    r = Role(**local_role_data)
    with pytest.raises(ModelError):
        r.fetch()
Beispiel #22
0
def test_role_get_permissions_for_policy_version(
        mock_get_permissions_in_policy, role_dict):
    r = Role(**role_dict)
    r.get_permissions_for_policy_version()

    mock_get_permissions_in_policy.assert_called_once()
    assert mock_get_permissions_in_policy.call_args[0][0] == vars.policies[-1][
        "Policy"]
    assert not mock_get_permissions_in_policy.call_args[1]["warn_unknown_perms"]
Beispiel #23
0
def test_role_fetch_not_found(mock_get_role_by_arn, role_dict):
    mock_get_role_by_arn.side_effect = RoleNotFoundError
    local_role_data = copy.deepcopy(role_dict)
    local_role_data.pop("role_id")
    local_role_data.pop("role_name")
    local_role_data.pop("account")
    r = Role(**local_role_data)
    with pytest.raises(RoleNotFoundError):
        r.fetch()
Beispiel #24
0
def test_role_get_repoed_policy_no_repoable_services(
        mock_get_services_and_permissions_from_repoable,
        mock_get_repoed_policy, role_dict):
    r = Role(**role_dict)
    r.repoable_services = []
    with pytest.raises(MissingRepoableServices):
        r.get_repoed_policy()
    mock_get_repoed_policy.assert_not_called()
    mock_get_services_and_permissions_from_repoable.assert_not_called()
Beispiel #25
0
def test_role_fetch_no_id(mock_get_role_by_name, role_dict):
    stored_role_data = copy.deepcopy(role_dict)
    stored_role_data["repoable_permissions"] = 20
    mock_get_role_by_name.return_value = stored_role_data
    local_role_data = copy.deepcopy(role_dict)
    local_role_data.pop("role_id")
    r = Role(**local_role_data)
    assert r.repoable_permissions == 5
    r.fetch()
    assert r.repoable_permissions == 20
Beispiel #26
0
    def test_calculate_repo_scores(self, mock_call_hooks,
                                   mock_get_repoable_permissions,
                                   mock_get_role_permissions):
        roles = [Role(ROLES[0]), Role(ROLES[1]), Role(ROLES[2])]
        roles[0].disqualified_by = []
        roles[0].aa_data = "some_aa_data"

        # disqualified by a filter
        roles[1].policies = [{"Policy": ROLE_POLICIES["unused_ec2"]}]
        roles[1].disqualified_by = ["some_filter"]
        roles[1].aa_data = "some_aa_data"

        # no AA data
        roles[2].policies = [{"Policy": ROLE_POLICIES["all_services_used"]}]
        roles[2].disqualified_by = []
        roles[2].aa_data = None

        hooks = {}

        mock_get_role_permissions.side_effect = [
            (
                [
                    "iam:AddRoleToInstanceProfile",
                    "iam:AttachRolePolicy",
                    "ec2:AllocateHosts",
                    "ec2:AssociateAddress",
                ],
                ["iam:AddRoleToInstanceProfile", "iam:AttachRolePolicy"],
            ),
            (
                ["iam:AddRoleToInstanceProfile", "iam:AttachRolePolicy"],
                ["iam:AddRoleToInstanceProfile", "iam:AttachRolePolicy"],
            ),
            (
                ["iam:AddRoleToInstanceProfile", "iam:AttachRolePolicy"],
                ["iam:AddRoleToInstanceProfile", "iam:AttachRolePolicy"],
            ),
        ]

        mock_call_hooks.return_value = set(
            ["iam:AddRoleToInstanceProfile", "iam:AttachRolePolicy"])
        mock_get_repoable_permissions.side_effect = [
            set(["iam:AddRoleToInstanceProfile", "iam:AttachRolePolicy"])
        ]

        minimum_age = 90
        repokid.utils.roledata._calculate_repo_scores(roles, minimum_age,
                                                      hooks)

        assert roles[0].repoable_permissions == 2
        assert roles[0].repoable_services == ["iam"]
        assert roles[1].repoable_permissions == 0
        assert roles[1].repoable_services == []
        assert roles[2].repoable_permissions == 0
        assert roles[2].repoable_services == []
Beispiel #27
0
def test_role_get_repoed_policy(
    mock_get_services_and_permissions_from_repoable, mock_get_repoed_policy, role_dict
):
    mock_get_repoed_policy.return_value = ({"repoed": "woohoo"}, ["old_policy_name"])
    r = Role(**role_dict)
    repoed_policies, deleted_policy_names = r.get_repoed_policy(scheduled=False)
    mock_get_repoed_policy.assert_called_once()
    mock_get_services_and_permissions_from_repoable.assert_not_called()
    assert mock_get_repoed_policy.call_args[0][0] == vars.policies[-1]["Policy"]
    assert mock_get_repoed_policy.call_args[0][1] == set(vars.repoable_services)
    assert repoed_policies == {"repoed": "woohoo"}
    assert deleted_policy_names == ["old_policy_name"]
Beispiel #28
0
def test_role_add_policy_version_duplicate(mock_store,
                                           mock_calculate_no_repo_permissions,
                                           role_dict):
    r = Role(**role_dict)
    source = "Fixture"
    fake_policy = vars.policies[0]["Policy"]
    assert len(r.policies) == 1
    r.add_policy_version(fake_policy, source=source, store=True)
    assert len(r.policies) == 1
    assert r.policies[0]["Policy"] == fake_policy
    mock_calculate_no_repo_permissions.assert_not_called()
    mock_store.assert_not_called()
Beispiel #29
0
def test_role_store_create(mock_create_dynamodb_entry, mock_get_role_by_id,
                           role_dict, role_dict_with_aliases):
    expected = copy.deepcopy(role_dict_with_aliases)
    mock_get_role_by_id.side_effect = RoleNotFoundError
    r = Role(**role_dict)
    r.store()
    mock_create_dynamodb_entry.assert_called_once()

    # Remove LastUpdated from the fn call and expected dict so we can compare the rest
    mock_create_dynamodb_entry.call_args[0][0].pop("LastUpdated")
    expected.pop("LastUpdated")

    assert mock_create_dynamodb_entry.call_args[0][0] == expected
Beispiel #30
0
def test_role_store_remote_updated(mock_get_role_by_id, role_dict,
                                   role_dict_with_aliases):
    expected = copy.deepcopy(role_dict_with_aliases)
    expected.pop("RoleId")
    expected.pop("RoleName")
    expected.pop("Account")

    # simulate the record having been updated in DynamoDB since we last fetched it
    last_updated = (vars.last_updated +
                    datetime.timedelta(hours=2)).strftime("%Y-%m-%d %H:%M")
    mock_get_role_by_id.return_value = {"LastUpdated": last_updated}
    r = Role(**role_dict)
    with pytest.raises(IntegrityError):
        r.store()