def create(cls, *, account_name, contacts, enabled, required_roles=list(), properties=None, auto_commit=True): if cls.get(account_name): raise AccountException('Account {} already exists'.format(account_name)) for prop in cls.class_properties: if prop['required'] and (prop['key'] not in properties or not properties[prop['key']]): raise InquisitorError('Missing required property {}'.format(prop['name'], cls.__name__)) res = Account() res.account_name = account_name res.contacts = contacts res.enabled = enabled res.required_roles = required_roles res.account_type_id = AccountType.get(cls.account_type).account_type_id if properties: for name, value in properties.items(): prop = AccountProperty() prop.account_id = res.account_id prop.name = name prop.value = value.isoformat() if type(value) == datetime else value res.properties.append(prop) db.session.add(prop) db.session.add(res) if auto_commit: db.session.commit() return res
def post(self): """Create a new account""" self.reqparse.add_argument('accountName', type=str, required=True) self.reqparse.add_argument('accountType', type=str, required=True) self.reqparse.add_argument('contacts', type=dict, required=True, action='append') self.reqparse.add_argument('enabled', type=int, required=True, choices=(0, 1)) self.reqparse.add_argument('requiredGroups', type=str, action='append', default=()) self.reqparse.add_argument('properties', type=dict, required=True) args = self.reqparse.parse_args() account_class = get_plugin_by_name(PLUGIN_NAMESPACES['accounts'], args['accountType']) if not account_class: raise InquisitorError('Invalid account type: {}'.format(args['accountType'])) validate_contacts(args['contacts']) for key in ('accountName', 'accountType', 'contacts', 'enabled'): value = args[key] if type(value) == str: value = value.strip() if type(value) in (int, tuple, list, str): if not value: raise Exception('{} cannot be empty'.format(key.replace('_', ' ').title())) else: raise ValueError('Invalid type: {} for value {} for argument {}'.format(type(value), value, key)) class_properties = {from_camelcase(key): value for key, value in args['properties'].items()} for prop in account_class.class_properties: if prop['key'] not in class_properties: raise InquisitorError('Missing required property {}'.format(prop)) acct = account_class.create( account_name=args['accountName'], contacts=args['contacts'], enabled=args['enabled'], required_roles=args['requiredGroups'], properties=class_properties, auto_commit=False ) db.session.commit() # Add the newly created account to the session so we can see it right away session['accounts'].append(acct.account_id) auditlog(event='account.create', actor=session['user'].username, data=args) return self.make_response({'message': 'Account created', 'accountId': acct.account_id}, HTTP.CREATED)
def put(self, accountId): """Update an account""" self.reqparse.add_argument('accountName', type=str, required=True) self.reqparse.add_argument('accountType', type=str, required=True) self.reqparse.add_argument('contacts', type=dict, required=True, action='append') self.reqparse.add_argument('enabled', type=int, required=True, choices=(0, 1)) self.reqparse.add_argument('requiredRoles', type=str, action='append', default=()) self.reqparse.add_argument('properties', type=dict, required=True) args = self.reqparse.parse_args() account_class = get_plugin_by_name(PLUGIN_NAMESPACES['accounts'], args['accountType']) if not account_class: raise InquisitorError('Invalid account type: {}'.format(args['accountType'])) validate_contacts(args['contacts']) if not args['accountName'].strip(): raise Exception('You must provide an account name') if not args['contacts']: raise Exception('You must provide at least one contact') class_properties = {from_camelcase(key): value for key, value in args['properties'].items()} for prop in account_class.class_properties: if prop['key'] not in class_properties: raise InquisitorError('Missing required property {}'.format(prop)) account = account_class.get(accountId) if account.account_type != args['accountType']: raise InquisitorError('You cannot change the type of an account') account.account_name = args['accountName'] account.contacts = args['contacts'] account.enabled = args['enabled'] account.required_roles = args['requiredRoles'] account.update(**args['properties']) account.save() auditlog(event='account.update', actor=session['user'].username, data=args) return self.make_response({'message': 'Object updated', 'account': account.to_dict(is_admin=True)})
def __init__(self, account): super().__init__() if type(account) == str: account = AWSAccount.get(account) if not isinstance(account, AWSAccount): raise InquisitorError('The AWS Collector only supports AWS Accounts, got {}'.format( account.__class__.__name__ )) self.account = account self.session = get_aws_session(self.account)
def get_aws_session(account): """Function to return a boto3 Session based on the account passed in the first argument. Args: account (:obj:`Account`): Account to create the session object for Returns: :obj:`boto3:boto3.session.Session` """ from cloud_inquisitor.config import dbconfig from cloud_inquisitor.plugins.types.accounts import AWSAccount if not isinstance(account, AWSAccount): raise InquisitorError('Non AWSAccount passed to get_aws_session, got {}'.format(account.__class__.__name__)) # If no keys are on supplied for the account, use sts.assume_role instead session = get_local_aws_session() if session.get_credentials().method == 'iam-role': sts = session.client('sts') else: # If we are not running on an EC2 instance, assume the instance role # first, then assume the remote role temp_sts = session.client('sts') audit_sts_role = temp_sts.assume_role( RoleArn=app_config.aws_api.instance_role_arn, RoleSessionName='inquisitor' ) sts = boto3.session.Session( audit_sts_role['Credentials']['AccessKeyId'], audit_sts_role['Credentials']['SecretAccessKey'], audit_sts_role['Credentials']['SessionToken'] ).client('sts') role = sts.assume_role( RoleArn='arn:aws:iam::{}:role/{}'.format( account.account_number, dbconfig.get('role_name', default='cinq_role') ), RoleSessionName='inquisitor' ) sess = boto3.session.Session( role['Credentials']['AccessKeyId'], role['Credentials']['SecretAccessKey'], role['Credentials']['SessionToken'] ) return sess
def get_template(template): """Return a Jinja2 template by filename Args: template (str): Name of the template to return Returns: A Jinja2 Template object """ from cloud_inquisitor.database import db tmpl = db.Template.find_one(template_name=template) if not tmpl: raise InquisitorError('No such template found: {}'.format(template)) tmplenv = Environment(loader=BaseLoader, autoescape=True) tmplenv.filters['json_loads'] = json.loads tmplenv.filters['slack_quote_join'] = lambda data: ', '.join('`{}`'.format(x) for x in data) return tmplenv.from_string(tmpl.template)
def get_all(cls, include_disabled=True): """Returns a list of all accounts of a given type Args: include_disabled (`bool`): Include disabled accounts. Default: `True` Returns: list of account objects """ if cls == BaseAccount: raise InquisitorError('get_all on BaseAccount is not supported') account_type_id = db.AccountType.find_one(account_type=cls.account_type).account_type_id qry = db.Account.order_by(desc(Account.enabled), Account.account_type_id, Account.account_name) if not include_disabled: qry = qry.filter(Account.enabled == 1) accounts = qry.find(Account.account_type_id == account_type_id) return {res.account_id: cls(res) for res in accounts}
def update(self, **kwargs): """Updates the object information based on live data, if there were any changes made. Any changes will be automatically applied to the object, but will not be automatically persisted. You must manually call `db.session.add(object)` on the object. Args: **kwargs (:obj:): AWS API Resource object fetched from AWS API Returns: `bool` """ updated = False for prop in self.class_properties: key = prop['key'] kwarg_key = to_camelcase(key) if kwarg_key in kwargs: if prop['required'] and not kwargs[kwarg_key]: raise InquisitorError('Missing required property {}'.format(prop['name'])) updated |= self.set_property(key, kwargs[kwarg_key]) return updated