#     Copyright 2014 Netflix, Inc.
#
#     Licensed under the Apache License, Version 2.0 (the "License");
#     you may not use this file except in compliance with the License.
#     You may obtain a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
#     Unless required by applicable law or agreed to in writing, software
#     distributed under the License is distributed on an "AS IS" BASIS,
#     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#     See the License for the specific language governing permissions and
#     limitations under the License.
"""
.. module: security_monkey.auditors.iam.iam_user
    :platform: Unix

.. version:: $$VERSION$$
.. moduleauthor::  <NAME> <<EMAIL>> @monkeysecurity

"""
import datetime

from dateutil import parser
from dateutil import tz
from security_monkey.auditor import Categories
from security_monkey.watchers.iam.iam_user import IAMUser
from security_monkey.auditors.iam.iam_policy import IAMPolicyAuditor
from security_monkey.watchers.iam.managed_policy import ManagedPolicy


class IAMUserAuditor(IAMPolicyAuditor):
    index = IAMUser.index
    i_am_singular = IAMUser.i_am_singular
    i_am_plural = IAMUser.i_am_plural
    support_auditor_indexes = [ManagedPolicy.index]

    def __init__(self, accounts=None, debug=False):
        super(IAMUserAuditor, self).__init__(accounts=accounts, debug=debug)
        self.iam_policy_keys = ['InlinePolicies$*']

    def prep_for_audit(self):
        """
        Prepare for the audit by calculating 90 days ago.
        This is used to check if access keys have been rotated.
        """
        super(IAMUserAuditor, self).prep_for_audit()
        now = datetime.datetime.now()
        then = now - datetime.timedelta(days=90)
        self.ninety_days_ago = then.replace(tzinfo=tz.gettz('UTC'))

    def check_active_access_keys(self, item):
        """
        alert when an IAM User has an active access key.
        score: 1
        """
        issue = Categories.INFORMATIONAL
        notes = Categories.INFORMATIONAL_NOTES

        akeys = item.config.get('AccessKeys', {})
        for akey in akeys:
            if 'Status' in akey:
                if akey['Status'] == 'Active':
                    note = notes.format(
                        description='Active Accesskey',
                        specific=' [{}]'.format(akey['AccessKeyId']))
                    self.add_issue(1, issue, item, notes=note)

    def check_inactive_access_keys(self, item):
        """
        alert when an IAM User has an inactive access key.
        score: 0
        """
        issue = Categories.INFORMATIONAL
        notes = Categories.INFORMATIONAL_NOTES

        akeys = item.config.get('AccessKeys', {})
        for akey in akeys:
            if 'Status' in akey:
                if akey['Status'] != 'Active':
                    description = 'Inactive Accesskey'
                    specific = ' [{}]'.format(akey['AccessKeyId'])
                    note = notes.format(description=description, specific=specific)
                    self.add_issue(0, issue, item, notes=note)

    def check_access_key_rotation(self, item):
        """
        alert when an IAM User has an active access key created more than 90 days go.
        """
        issue = Categories.ROTATION
        notes = Categories.ROTATION_NOTES
        requirement = '> 90 days ago'

        akeys = item.config.get('AccessKeys', {})
        for akey in akeys:
            if 'Status' in akey:
                if akey['Status'] == 'Active':
                    create_date = akey['CreateDate']
                    create_date = parser.parse(create_date)
                    if create_date < self.ninety_days_ago:
                        note = notes.format(
                            what='Active Accesskey [{key}]'.format(key=akey['AccessKeyId']),
                            requirement=requirement,
                            date=akey['CreateDate'])
                        self.add_issue(1, issue, item, notes=note)

    def check_access_key_last_used(self, item):
        """
        alert if an active access key hasn't been used in 90 days
        """
        issue = Categories.UNUSED
        notes = Categories.UNUSED_NOTES
        requirement = '> 90 days ago'

        akeys = item.config.get('AccessKeys', {})
        for akey in akeys:
            if 'Status' in akey:
                if akey['Status'] == 'Active':
                    last_used_str = akey.get('LastUsedDate') or akey.get('CreateDate')
                    last_used_date = parser.parse(last_used_str)
                    if last_used_date < self.ninety_days_ago:
                        note = notes.format(
                            what='Active Accesskey [{key}]'.format(key=akey['AccessKeyId']),
                            requirement=requirement,
                            date=last_used_str)
                        self.add_issue(1, issue, item, notes=note)

    def check_no_mfa(self, item):
        """
        alert when an IAM user has a login profile and no MFA devices.
        This means a human account which could be better protected with 2FA.
        """
        issue = Categories.INSECURE_CONFIGURATION
        notes = Categories.INSECURE_CONFIGURATION_NOTES
        notes = notes.format(description='User with password login and no MFA devices')

        user_mfas = item.config.get('MfaDevices', {})
        login_profile = item.config.get('LoginProfile', {})
        if login_profile and not user_mfas:
            self.add_issue(1, issue, item, notes=notes)

    def check_loginprofile_plus_akeys(self, item):
        """
        alert when an IAM user has a login profile and API access via access keys.
        An account should be used Either for API access OR for console access, but maybe not both.
        """
        if not item.config.get('LoginProfile', None):
            return

        issue = Categories.INFORMATIONAL
        notes = Categories.INFORMATIONAL_NOTES
        notes = notes.format(
            description='User with password login and API access',
            specific='')

        akeys = item.config.get('AccessKeys', {})
        for akey in akeys:
            if 'Status' in akey and akey['Status'] == 'Active':
                self.add_issue(1, issue, item, notes)
                return

    def check_attached_managed_policies(self, iamuser_item):
        """
        alert when an IAM Role is attached to a managed policy with issues
        """
        self.library_check_attached_managed_policies(iamuser_item, 'user')

#     Copyright 2014 Netflix, Inc.
#
#     Licensed under the Apache License, Version 2.0 (the "License");
#     you may not use this file except in compliance with the License.
#     You may obtain a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
#     Unless required by applicable law or agreed to in writing, software
#     distributed under the License is distributed on an "AS IS" BASIS,
#     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#     See the License for the specific language governing permissions and
#     limitations under the License.
"""
.. module: security_monkey.auditors.iam.iam_user
    :platform: Unix

.. version:: $$VERSION$$
.. moduleauthor::  <NAME> <<EMAIL>> @monkeysecurity

"""
import datetime

from dateutil import parser
from dateutil import tz
from security_monkey.auditor import Categories
from security_monkey.watchers.iam.iam_user import IAMUser
from security_monkey.auditors.iam.iam_policy import IAMPolicyAuditor
from security_monkey.watchers.iam.managed_policy import ManagedPolicy


class IAMUserAuditor(IAMPolicyAuditor):
    index = IAMUser.index
    i_am_singular = IAMUser.i_am_singular
    i_am_plural = IAMUser.i_am_plural
    support_auditor_indexes = [ManagedPolicy.index]

    def __init__(self, accounts=None, debug=False):
        super(IAMUserAuditor, self).__init__(accounts=accounts, debug=debug)
        self.iam_policy_keys = ['InlinePolicies$*']

    def prep_for_audit(self):
        """
        Prepare for the audit by calculating 90 days ago.
        This is used to check if access keys have been rotated.
        """
        super(IAMUserAuditor, self).prep_for_audit()
        now = datetime.datetime.now()
        then = now - datetime.timedelta(days=90)
        self.ninety_days_ago = then.replace(tzinfo=tz.gettz('UTC'))

    def check_active_access_keys(self, item):
        """
        alert when an IAM User has an active access key.
        score: 1
        """
        issue = Categories.INFORMATIONAL
        notes = Categories.INFORMATIONAL_NOTES

        akeys = item.config.get('AccessKeys', {})
        for akey in akeys:
            if 'Status' in akey:
                if akey['Status'] == 'Active':
                    note = notes.format(
                        description='Active Accesskey',
                        specific=' [{}]'.format(akey['AccessKeyId']))
                    self.add_issue(1, issue, item, notes=note)

    def check_inactive_access_keys(self, item):
        """
        alert when an IAM User has an inactive access key.
        score: 0
        """
        issue = Categories.INFORMATIONAL
        notes = Categories.INFORMATIONAL_NOTES

        akeys = item.config.get('AccessKeys', {})
        for akey in akeys:
            if 'Status' in akey:
                if akey['Status'] != 'Active':
                    description = 'Inactive Accesskey'
                    specific = ' [{}]'.format(akey['AccessKeyId'])
                    note = notes.format(description=description, specific=specific)
                    self.add_issue(0, issue, item, notes=note)

    def check_access_key_rotation(self, item):
        """
        alert when an IAM User has an active access key created more than 90 days go.
        """
        issue = Categories.ROTATION
        notes = Categories.ROTATION_NOTES
        requirement = '> 90 days ago'

        akeys = item.config.get('AccessKeys', {})
        for akey in akeys:
            if 'Status' in akey:
                if akey['Status'] == 'Active':
                    create_date = akey['CreateDate']
                    create_date = parser.parse(create_date)
                    if create_date < self.ninety_days_ago:
                        note = notes.format(
                            what='Active Accesskey [{key}]'.format(key=akey['AccessKeyId']),
                            requirement=requirement,
                            date=akey['CreateDate'])
                        self.add_issue(1, issue, item, notes=note)

    def check_access_key_last_used(self, item):
        """
        alert if an active access key hasn't been used in 90 days
        """
        issue = Categories.UNUSED
        notes = Categories.UNUSED_NOTES
        requirement = '> 90 days ago'

        akeys = item.config.get('AccessKeys', {})
        for akey in akeys:
            if 'Status' in akey:
                if akey['Status'] == 'Active':
                    last_used_str = akey.get('LastUsedDate') or akey.get('CreateDate')
                    last_used_date = parser.parse(last_used_str)
                    if last_used_date < self.ninety_days_ago:
                        note = notes.format(
                            what='Active Accesskey [{key}]'.format(key=akey['AccessKeyId']),
                            requirement=requirement,
                            date=last_used_str)
                        self.add_issue(1, issue, item, notes=note)

    def check_no_mfa(self, item):
        """
        alert when an IAM user has a login profile and no MFA devices.
        This means a human account which could be better protected with 2FA.
        """
        issue = Categories.INSECURE_CONFIGURATION
        notes = Categories.INSECURE_CONFIGURATION_NOTES
        notes = notes.format(description='User with password login and no MFA devices')

        user_mfas = item.config.get('MfaDevices', {})
        login_profile = item.config.get('LoginProfile', {})
        if login_profile and not user_mfas:
            self.add_issue(1, issue, item, notes=notes)

    def check_loginprofile_plus_akeys(self, item):
        """
        alert when an IAM user has a login profile and API access via access keys.
        An account should be used Either for API access OR for console access, but maybe not both.
        """
        if not item.config.get('LoginProfile', None):
            return

        issue = Categories.INFORMATIONAL
        notes = Categories.INFORMATIONAL_NOTES
        notes = notes.format(
            description='User with password login and API access',
            specific='')

        akeys = item.config.get('AccessKeys', {})
        for akey in akeys:
            if 'Status' in akey and akey['Status'] == 'Active':
                self.add_issue(1, issue, item, notes)
                return

    def check_attached_managed_policies(self, iamuser_item):
        """
        alert when an IAM Role is attached to a managed policy with issues
        """
        self.library_check_attached_managed_policies(iamuser_item, 'user')

#     Copyright 2014 Netflix, Inc. # #     Licensed under the Apache License, Version 2.0 (the "License"); #     you may not use this file except in compliance with the License. #     You may obtain a copy of the License at # #         http://www.apache.org/licenses/LICENSE-2.0 # #     Unless required by applicable law or agreed to in writing, software #     distributed under the License is distributed on an "AS IS" BASIS, #     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #     See the License for the specific language governing permissions and #     limitations under the License. .. module: security_monkey.auditors.iam.iam_user     :platform: Unix  .. version:: $$VERSION$$ .. moduleauthor::  <NAME> <<EMAIL>> @monkeysecurity Prepare for the audit by calculating 90 days ago.         This is used to check if access keys have been rotated. alert when an IAM User has an active access key.         score: 1 alert when an IAM User has an inactive access key.         score: 0 alert when an IAM User has an active access key created more than 90 days go. alert if an active access key hasn't been used in 90 days alert when an IAM user has a login profile and no MFA devices.         This means a human account which could be better protected with 2FA. alert when an IAM user has a login profile and API access via access keys.         An account should be used Either for API access OR for console access, but maybe not both. alert when an IAM Role is attached to a managed policy with issues