Beispiel #1
0
def repo_stats(output_file, dynamo_table, account_number=None):
    """
    Create a csv file with stats about roles, total permissions, and applicable filters over time

    Args:
        output_file (string): the name of the csv file to write
        account_number (string): if specified only display roles from selected account, otherwise display all

    Returns:
        None
    """
    roleIDs = (role_ids_for_account(dynamo_table, account_number) if account_number else
               role_ids_for_all_accounts(dynamo_table))
    headers = ['RoleId', 'Role Name', 'Account', 'Active', 'Date', 'Source', 'Permissions Count',
               'Repoable Permissions Count', 'Disqualified By']
    rows = []

    for roleID in roleIDs:
        role_data = get_role_data(dynamo_table, roleID, fields=['RoleId', 'RoleName', 'Account', 'Active', 'Stats'])
        for stats_entry in role_data.get('Stats', []):
            rows.append([role_data['RoleId'], role_data['RoleName'], role_data['Account'], role_data['Active'],
                         stats_entry['Date'], stats_entry['Source'], stats_entry['PermissionsCount'],
                         stats_entry.get('RepoablePermissionsCount'), stats_entry.get('DisqualifiedBy', [])])

    try:
        with open(output_file, 'wb') as csvfile:
            csv_writer = csv.writer(csvfile)
            csv_writer.writerow(headers)
            for row in rows:
                csv_writer.writerow(row)
    except IOError as e:
        LOGGER.error('Unable to write file {}: {}'.format(output_file, e))
    else:
        LOGGER.info('Successfully wrote stats to {}'.format(output_file))
Beispiel #2
0
def show_scheduled_roles(account_number, dynamo_table):
    """
    Show scheduled repos for a given account.  For each scheduled show whether scheduled time is elapsed or not.
    """
    roles = Roles([
        Role(get_role_data(dynamo_table, roleID))
        for roleID in tqdm(role_ids_for_account(dynamo_table, account_number))
    ])

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

    header = ["Role name", "Scheduled", "Scheduled Time Elapsed?"]
    rows = []

    curtime = int(time.time())

    for role in roles:
        rows.append([
            role.role_name,
            dt.fromtimestamp(role.repo_scheduled).strftime("%Y-%m-%d %H:%M"),
            role.repo_scheduled < curtime,
        ])

    print(tabulate(rows, headers=header))
Beispiel #3
0
def schedule_repo(account_number, dynamo_table, config, hooks):
    """
    Schedule a repo for a given account.  Schedule repo for a time in the future (default 7 days) for any roles in
    the account with repoable permissions.
    """
    scheduled_roles = []

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

    scheduled_time = int(time.time()) + (86400 * config.get('repo_schedule_period_days', 7))
    for role in roles:
        if role.repoable_permissions > 0 and not role.repo_scheduled:
            role.repo_scheduled = scheduled_time
            # freeze the scheduled perms to whatever is repoable right now
            set_role_data(dynamo_table, role.role_id,
                          {'RepoScheduled': scheduled_time, 'ScheduledPerms': role.repoable_services})

            scheduled_roles.append(role)

    LOGGER.info("Scheduled repo for {} days from now for account {} and these roles:\n\t{}".format(
                config.get('repo_schedule_period_days', 7),
                account_number,
                ', '.join([r.role_name for r in scheduled_roles])))

    repokid.hooks.call_hooks(hooks, 'AFTER_SCHEDULE_REPO', {'roles': scheduled_roles})
Beispiel #4
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 #5
0
def _display_roles(account_number, dynamo_table, inactive=False):
    """
    Display a table with data about all roles in an account and write a csv file with the data.

    Args:
        account_number (string)
        inactive (bool): show roles that have historically (but not currently) existed in the account if True

    Returns:
        None
    """

    headers = [
        "Name",
        "Refreshed",
        "Disqualified By",
        "Can be repoed",
        "Permissions",
        "Policies Repoable",
        "Services",
        "Repoed",
        "Managed Permissions",
        "Managed Policies Repoable"
        "Managed Services",
    ]

    rows = list()

    roles = Roles([
        Role.parse_obj(get_role_data(dynamo_table, roleID))
        for roleID in tqdm(role_ids_for_account(dynamo_table, account_number))
    ])

    if not inactive:
        roles = roles.filter(active=True)

    for role in roles:
        rows.append([
            role.role_name,
            role.refreshed,
            role.disqualified_by,
            len(role.disqualified_by) == 0,
            role.total_permissions,
            role.repoable_permissions,
            role.repoable_services,
            role.repoed,
            role.total_managed_permissions,
            role.repoable_managed_permissions,
            role.repoable_managed_services,
        ])

    rows = sorted(rows, key=lambda x: (x[5], x[0], x[4]))
    rows.insert(0, headers)
    # print tabulate(rows, headers=headers)
    t.view(rows)
    with open("table.csv", "w") as csvfile:
        csv_writer = csv.writer(csvfile)
        csv_writer.writerow(headers)
        for row in rows:
            csv_writer.writerow(row)
Beispiel #6
0
def repo_all_roles(account_number,
                   dynamo_table,
                   config,
                   hooks,
                   commit=False,
                   scheduled=True):
    """
    Repo all scheduled or eligible roles in an account.  Collect any errors and display them at the end.

    Args:
        account_number (string)
        dynamo_table
        config
        commit (bool): actually make the changes
        scheduled (bool): if True only repo the scheduled roles, if False repo all the (eligible) roles

    Returns:
        None
    """
    errors = []

    role_ids_in_account = role_ids_for_account(dynamo_table, account_number)
    roles = Roles([])
    for role_id in role_ids_in_account:
        roles.append(
            Role(
                get_role_data(dynamo_table,
                              role_id,
                              fields=['Active', 'RoleName', 'RepoScheduled'])))

    roles = roles.filter(active=True)

    cur_time = int(time.time())
    if scheduled:
        roles = [
            role for role in roles
            if (role.repo_scheduled and cur_time > role.repo_scheduled)
        ]

    LOGGER.info('Repoing these {}roles from account {}:\n\t{}'.format(
        'scheduled ' if scheduled else '', account_number,
        ', '.join([role.role_name for role in roles])))

    for role in roles:
        error = repo_role(account_number,
                          role.role_name,
                          dynamo_table,
                          config,
                          hooks,
                          commit=commit)
        if error:
            errors.append(error)

    if errors:
        LOGGER.error('Error(s) during repo: \n{}'.format(errors))
    else:
        LOGGER.info('Everything successful!')
Beispiel #7
0
def _repo_stats(output_file, dynamo_table, account_number=None):
    """
    Create a csv file with stats about roles, total permissions, and applicable filters over time

    Args:
        output_file (string): the name of the csv file to write
        account_number (string): if specified only display roles from selected account, otherwise display all

    Returns:
        None
    """
    roleIDs = (role_ids_for_account(dynamo_table, account_number)
               if account_number else role_ids_for_all_accounts(dynamo_table))
    headers = [
        "RoleId",
        "Role Name",
        "Account",
        "Active",
        "Date",
        "Source",
        "Permissions Count",
        "Repoable Permissions Count",
        "Disqualified By",
    ]
    rows = []

    for roleID in roleIDs:
        role_data = get_role_data(
            dynamo_table,
            roleID,
            fields=["RoleId", "RoleName", "Account", "Active", "Stats"],
        )
        for stats_entry in role_data.get("Stats", []):
            rows.append([
                role_data["RoleId"],
                role_data["RoleName"],
                role_data["Account"],
                role_data["Active"],
                stats_entry["Date"],
                stats_entry["Source"],
                stats_entry["PermissionsCount"],
                stats_entry.get("RepoablePermissionsCount"),
                stats_entry.get("DisqualifiedBy", []),
            ])

    try:
        with open(output_file, "w") as csvfile:
            csv_writer = csv.writer(csvfile)
            csv_writer.writerow(headers)
            for row in rows:
                csv_writer.writerow(row)
    except IOError as e:
        LOGGER.error("Unable to write file {}: {}".format(output_file, e),
                     exc_info=True)
    else:
        LOGGER.info("Successfully wrote stats to {}".format(output_file))
Beispiel #8
0
def _schedule_repo(account_number, dynamo_table, config, hooks):
    """
    Schedule a repo for a given account.  Schedule repo for a time in the future (default 7 days) for any roles in
    the account with repoable permissions.
    """
    scheduled_roles = []

    roles = Roles([
        Role.parse_obj(get_role_data(dynamo_table, roleID))
        for roleID in tqdm(role_ids_for_account(dynamo_table, account_number))
    ])

    scheduled_time = int(
        time.time()) + (86400 * config.get("repo_schedule_period_days", 7))
    for role in roles:
        if not role.aa_data:
            LOGGER.warning("Not scheduling %s; missing Access Advisor data",
                           role.arn)
            continue
        if not role.repoable_permissions > 0:
            LOGGER.debug("Not scheduling %s; no repoable permissions",
                         role.arn)
            continue
        if role.repo_scheduled:
            LOGGER.debug(
                "Not scheduling %s; already scheduled for %s",
                role.arn,
                role.repo_scheduled,
            )
            continue

        role.repo_scheduled = scheduled_time
        # freeze the scheduled perms to whatever is repoable right now
        set_role_data(
            dynamo_table,
            role.role_id,
            {
                "RepoScheduled": scheduled_time,
                "ScheduledPerms": role.repoable_services,
            },
        )

        scheduled_roles.append(role)

    LOGGER.info(
        "Scheduled repo for {} days from now for account {} and these roles:\n\t{}"
        .format(
            config.get("repo_schedule_period_days", 7),
            account_number,
            ", ".join([r.role_name for r in scheduled_roles]),
        ))

    repokid.hooks.call_hooks(hooks, "AFTER_SCHEDULE_REPO",
                             {"roles": scheduled_roles})
Beispiel #9
0
def find_and_mark_inactive(dynamo_table, account_number, active_roles):
    """
    Mark roles in the account that aren't currently active inactive. Do this by getting all roles in the account and
    subtracting the active roles, any that are left are inactive and should be marked thusly.

    Args:
        account_number (string)
        active_roles (set): the currently active roles discovered in the most recent scan

    Returns:
        None
    """

    active_roles = set(active_roles)
    known_roles = set(role_ids_for_account(dynamo_table, account_number))
    inactive_roles = known_roles - active_roles

    for roleID in inactive_roles:
        role_dict = get_role_data(dynamo_table, roleID, fields=['Active', 'Arn'])
        if role_dict.get('Active'):
            set_role_data(dynamo_table, roleID, {'Active': False})
Beispiel #10
0
def schedule_repo(account_number, dynamo_table, config):
    """
    Schedule a repo for a given account.  Schedule repo for a time in the future (default 7 days) for any roles in
    the account with repoable permissions.
    """
    scheduled_roles = []

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

    scheduled_time = int(
        time.time()) + (86400 * config.get('repo_schedule_period_days', 7))
    for role in roles:
        if role.repoable_permissions > 0:
            set_role_data(dynamo_table, role.role_id,
                          {'RepoScheduled': scheduled_time})
            scheduled_roles.append(role.role_name)

    LOGGER.info(
        "Scheduled repo for {} days from now for these roles:\n\t{}".format(
            config.get('repo_schedule_period_days', 7),
            ', '.join([r for r in scheduled_roles])))
Beispiel #11
0
def _repo_all_roles(account_number,
                    dynamo_table,
                    config,
                    hooks,
                    commit=False,
                    scheduled=True,
                    limit=-1):
    """
    Repo all scheduled or eligible roles in an account.  Collect any errors and display them at the end.

    Args:
        account_number (string)
        dynamo_table
        config
        commit (bool): actually make the changes
        scheduled (bool): if True only repo the scheduled roles, if False repo all the (eligible) roles
        limit (int): limit number of roles to be repoed per run (< 0 is unlimited)

    Returns:
        None
    """
    errors = []

    role_ids_in_account = role_ids_for_account(dynamo_table, account_number)
    roles = Roles([])
    for role_id in role_ids_in_account:
        roles.append(
            Role(
                get_role_data(
                    dynamo_table,
                    role_id,
                    fields=["Active", "RoleName", "RepoScheduled"],
                )))

    roles = roles.filter(active=True)

    cur_time = int(time.time())

    if scheduled:
        roles = [
            role for role in roles
            if (role.repo_scheduled and cur_time > role.repo_scheduled)
        ]

    LOGGER.info("Repoing these {}roles from account {}:\n\t{}".format(
        "scheduled " if scheduled else "",
        account_number,
        ", ".join([role.role_name for role in roles]),
    ))

    repokid.hooks.call_hooks(hooks, "BEFORE_REPO_ROLES", {
        "account_number": account_number,
        "roles": roles
    })

    count = 0
    repoed = Roles([])
    for role in roles:
        if limit >= 0 and count == limit:
            break
        error = _repo_role(
            account_number,
            role.role_name,
            dynamo_table,
            config,
            hooks,
            commit=commit,
            scheduled=scheduled,
        )
        if error:
            errors.append(error)
        repoed.append(role)
        count += 1

    if errors:
        LOGGER.error(
            f"Error(s) during repo: \n{errors} (account: {account_number})")
    else:
        LOGGER.info(
            f"Successfully repoed {count} roles in account {account_number}")

    repokid.hooks.call_hooks(
        hooks,
        "AFTER_REPO_ROLES",
        {
            "account_number": account_number,
            "roles": repoed,
            "errors": errors
        },
    )