Ejemplo n.º 1
0
class TechMethodsGet(AuthenticatedService):
    decorators = [
        rbac.allow(["View"], ["GET"]),
    ]

    def __init__(self):
        super(TechMethodsGet, self).__init__()

    def get(self, tech_ids):
        """
            .. http:get:: /api/1/techmethods

            Get a list of technologies and associated auditor check methods

            **Example Request**:

            .. sourcecode:: http

                GET /api/1/techmethods HTTP/1.1
                Host: example.com
                Accept: application/json, text/javascript

            **Example Response**:

            .. sourcecode:: http

                HTTP/1.1 200 OK
                Vary: Accept
                Content-Type: application/json

                {
                    "technologies": [ "subnet" ]
                    "tech_methods": { "subnet": [ "check_internet_access" ] }
                    auth: {
                        authenticated: true,
                        user: "******"
                    }
                }

            :statuscode 200: no error
            :statuscode 401: Authentication failure. Please login.
        """
        tech_methods = {}

        for key in list(auditor_registry.keys()):
            methods = []

            for auditor_class in auditor_registry[key]:
                auditor = auditor_class('')
                for method_name in dir(auditor):
                    method_name = method_name + ' (' + auditor.__class__.__name__ + ')'
                    if (method_name.find("check_")) == 0:
                        methods.append(method_name)

                tech_methods[key] = methods

        marshaled_dict = {'tech_methods': tech_methods, 'auth': self.auth_dict}

        return marshaled_dict, 200
Ejemplo n.º 2
0
class ItemAuditGet(AuthenticatedService):
    decorators = [
        rbac.allow(["View"], ["GET"])
    ]

    def get(self, audit_id):
        """
            .. http:get:: /api/1/issue/1234

            Get a specific issue

            **Example Request**:

            .. sourcecode:: http

                GET /api/1/issue/1234 HTTP/1.1
                Host: example.com
                Accept: application/json

            **Example Response**:

            .. sourcecode:: http

                HTTP/1.1 200 OK
                Vary: Accept
                Content-Type: application/json

                {
                    justification: null,
                    name: "example_name",
                    issue: "Example Audit Issue",
                    notes: "Example Notes on Audit Issue",
                    auth: {
                        authenticated: true,
                        user: "******"
                    },
                    score: 0,
                    item_id: 704,
                    region: AWS_DEFAULT_REGION,
                    justified: false,
                    justified_date: null,
                    id: 704
                }

            :statuscode 200: no error
            :statuscode 401: Authentication Error. Please login.
        """

        query = ItemAudit.query.join("item").filter(ItemAudit.id == audit_id)
        result = query.first()

        issue_marshaled = marshal(result, AUDIT_FIELDS)
        item_marshaled = marshal(result.item, ITEM_FIELDS)
        issue_marshaled = dict(
            list(issue_marshaled.items()) +
            list(item_marshaled.items()) +
            list({'auth': self.auth_dict}.items())
        )
        return issue_marshaled, 200
Ejemplo n.º 3
0
class Providers(Resource):
    decorators = [rbac.allow(["anonymous"], ["GET"])]

    def __init__(self):
        super(Providers, self).__init__()

    def get(self):
        active_providers = []

        for provider in current_app.config.get("ACTIVE_PROVIDERS"):
            provider = provider.lower()

            if provider == "ping":
                active_providers.append({
                    'name':
                    current_app.config.get("PING_NAME"),
                    'url':
                    current_app.config.get('PING_REDIRECT_URI'),
                    'redirectUri':
                    current_app.config.get("PING_REDIRECT_URI"),
                    'clientId':
                    current_app.config.get("PING_CLIENT_ID"),
                    'responseType':
                    'code',
                    'scope': ['openid', 'profile', 'email'],
                    'scopeDelimiter':
                    ' ',
                    'authorizationEndpoint':
                    current_app.config.get("PING_AUTH_ENDPOINT"),
                    'requiredUrlParams': ['scope'],
                    'type':
                    '2.0'
                })
            elif provider == "google":
                google_provider = {
                    'name':
                    'google',
                    'clientId':
                    current_app.config.get("GOOGLE_CLIENT_ID"),
                    'url':
                    api.url_for(Google, _external=True, _scheme='https'),
                    'redirectUri':
                    api.url_for(Google, _external=True, _scheme='https'),
                    'authorizationEndpoint':
                    current_app.config.get("GOOGLE_AUTH_ENDPOINT"),
                    'scope': ['openid email'],
                    'responseType':
                    'code'
                }
                google_hosted_domain = current_app.config.get(
                    "GOOGLE_HOSTED_DOMAIN")
                if google_hosted_domain is not None:
                    google_provider['hd'] = google_hosted_domain
                active_providers.append(google_provider)
            else:
                raise Exception(
                    "Unknown authentication provider: {0}".format(provider))

        return active_providers
class WatcherConfigGetList(AuthenticatedService):
    decorators = [
        rbac.allow(["Admin"], ["GET"]),
    ]

    def __init__(self):
        super(WatcherConfigGetList, self).__init__()
        self.reqparse = reqparse.RequestParser()

    def get(self):
        self.reqparse.add_argument('count',
                                   type=int,
                                   default=30,
                                   location='args')
        self.reqparse.add_argument('page',
                                   type=int,
                                   default=1,
                                   location='args')

        args = self.reqparse.parse_args()
        page = args.pop('page', None)
        count = args.pop('count', None)

        configs = []
        all_keys = list(watcher_registry.keys())
        all_keys.sort()

        start_index = (page - 1) * count
        keys = all_keys[start_index:start_index + count]

        for key in keys:
            watcher_class = watcher_registry[key]
            config = WatcherConfig.query.filter(
                WatcherConfig.index == watcher_class.index).first()
            if config is None:
                config = WatcherConfig(id=0,
                                       index=watcher_class.index,
                                       interval=watcher_class.interval,
                                       active=True)

            configs.append(config)

        return_dict = {
            "page":
            page,
            "total":
            len(all_keys),
            "count":
            len(configs),
            "items": [
                marshal(item.__dict__, WATCHER_CONFIG_FIELDS)
                for item in configs
            ],
            "auth":
            self.auth_dict
        }

        return return_dict, 200
Ejemplo n.º 5
0
class RevisionCommentGet(AuthenticatedService):
    decorators = [rbac.allow(["Comment"], ["GET"])]

    def __init__(self):
        super(RevisionCommentGet, self).__init__()

    def get(self, revision_id, comment_id):
        """
            .. http:get:: /api/1/revisions/<int:revision_id>/comments/<int:comment_id>

            Get a specific Revision Comment

            **Example Request**:

            .. sourcecode:: http

                GET /api/1/revisions/1141/comments/22 HTTP/1.1
                Host: example.com
                Accept: application/json


            **Example Response**:

            .. sourcecode:: http

                HTTP/1.1 200 OK
                Vary: Accept
                Content-Type: application/json

                {
                    'id': 22,
                    'revision_id': 1141,
                    "date_created": "2013-10-04 22:01:47",
                    'text': 'This is a Revision Comment.'
                }

            :statuscode 200: no error
            :statuscode 404: Revision Comment with given ID not found.
            :statuscode 401: Authentication Error. Please Login.
        """

        auth, retval = __check_auth__(self.auth_dict)
        if auth:
            return retval

        query = ItemRevisionComment.query.filter(
            ItemRevisionComment.id == comment_id)
        query = query.filter(ItemRevisionComment.revision_id == revision_id)
        irc = query.first()

        if irc is None:
            return {"status": "Revision Comment Not Found"}, 404

        revision_marshaled = marshal(irc.__dict__, REVISION_COMMENT_FIELDS)
        revision_marshaled = dict(revision_marshaled.items() +
                                  {'user': irc.user.email}.items())

        return revision_marshaled, 200
class WatcherConfigPut(AuthenticatedService):
    decorators = [
        rbac.allow(["Admin"], ["Put"]),
    ]

    def __init__(self):
        super(WatcherConfigPut, self).__init__()

    def put(self, id):
        self.reqparse.add_argument('index',
                                   required=True,
                                   type=text_type,
                                   location='json')
        self.reqparse.add_argument('interval',
                                   required=True,
                                   type=int,
                                   location='json')
        self.reqparse.add_argument('active',
                                   required=True,
                                   type=bool,
                                   location='json')
        self.reqparse.add_argument('remove_items',
                                   required=False,
                                   type=bool,
                                   location='json')
        args = self.reqparse.parse_args()
        index = args['index']
        interval = args['interval']
        active = args['active']
        remove_items = args.get('remove_items', False)

        if id > 0:
            config = WatcherConfig.query.filter(WatcherConfig.id == id).first()
            config.interval = interval
            config.active = active
        else:
            config = WatcherConfig(index=index,
                                   interval=interval,
                                   active=active)

        db.session.add(config)
        db.session.commit()

        if active is False and remove_items is True:
            results = Item.query.join((Technology, Item.tech_id == Technology.id)) \
                                      .filter(Technology.name == index).all()

            for item in results:
                db.session.delete(item)
            db.session.commit()

        marshaled_dict = {'auth': self.auth_dict}

        return marshaled_dict, 200
Ejemplo n.º 7
0
class Roles(AuthenticatedService):
    decorators = [rbac.allow(["Admin"], ["GET"])]

    def get(self):
        """
            .. http:get:: /api/1/roles

            Get a list of roles, checking that the requester is an admin.

            **Example Request**:

            .. sourcecode:: http

                GET /api/1/roles HTTP/1.1
                Host: example.com
                Accept: application/json

            **Example Response**:

            .. sourcecode:: http

                HTTP/1.1 200 OK
                Vary: Accept
                Content-Type: application/json

                {
                    "auth": {
                        "authenticated": true,
                        "administrator": true,
                        "user": "******"
                    },
                    "roles": [
                        {
                            "id": 1,
                            "name": "admin",
                            "description": "Administrators"
                        }
                    ]
                }

            :statuscode 200: no error
            :statuscode 401: Authentication Error. Please ensure you have administrator rights.
        """

        return_dict = {"auth": self.auth_dict}

        roles = []

        for name in RBACRole.roles:
            roles.append({"name": RBACRole.roles[name].name})

        return_dict["roles"] = roles

        return return_dict, 200
Ejemplo n.º 8
0
class RevisionCommentDelete(AuthenticatedService):
    decorators = [rbac.allow(["Comment"], ["DELETE"])]

    def __init__(self):
        super(RevisionCommentDelete, self).__init__()

    def delete(self, revision_id, comment_id):
        """
            .. http:delete:: /api/1/revisions/<int:revision_id>/comments/<int:comment_id>

            Delete a specific Revision Comment

            **Example Request**:

            .. sourcecode:: http

                DELETE /api/1/revisions/1141/comments/22 HTTP/1.1
                Host: example.com
                Accept: application/json


            **Example Response**:

            .. sourcecode:: http

                HTTP/1.1 200 OK
                Vary: Accept
                Content-Type: application/json

                {
                    'status': "deleted"
                }

            :statuscode 202: Comment Deleted
            :statuscode 404: Revision Comment with given ID not found.
            :statuscode 401: Authentication Error. Please Login.
        """

        auth, retval = __check_auth__(self.auth_dict)
        if auth:
            return retval

        query = ItemRevisionComment.query.filter(
            ItemRevisionComment.id == comment_id)
        query = query.filter(ItemRevisionComment.revision_id == revision_id)
        irc = query.first()

        if irc is None:
            return {"status": "Revision Comment Not Found"}, 404

        query.delete()
        db.session.commit()

        return {"status": "deleted"}, 202
class AuditorSettingsPut(AuthenticatedService):
    decorators = [rbac.allow(["Justify"], ["PUT"])]

    def __init__(self):
        self.reqparse = reqparse.RequestParser()
        super(AuditorSettingsPut, self).__init__()

    def put(self, as_id):
        """
            .. http:put:: /api/1/auditorsettings/<int ID>

            Update an AuditorSetting

            **Example Request**:

            .. sourcecode:: http

                PUT /api/1/auditorsettings/1 HTTP/1.1
                Host: example.com
                Accept: application/json, text/javascript

                {
                    account: "aws-account-name",
                    disabled: false,
                    id: 1,
                    issue: "User with password login.",
                    technology: "iamuser"
                }


            **Example Response**:

            .. sourcecode:: http

                HTTP/1.1 200 OK
                Content-Type: application/json

            :statuscode 200: no error
            :statuscode 401: Authentication failure. Please login.
        """

        self.reqparse.add_argument('disabled',
                                   type=bool,
                                   required=True,
                                   location='json')
        args = self.reqparse.parse_args()
        disabled = args.pop('disabled', None)
        results = AuditorSettings.query.get(as_id)
        results.disabled = disabled
        db.session.add(results)
        db.session.commit()
        return 200
Ejemplo n.º 10
0
class ItemCommentGet(AuthenticatedService):
    decorators = [rbac.allow(['View'], ["GET"])]

    def get(self, item_id, comment_id):
        """
            .. http:get:: /api/1/items/<int:item_id>/comment/<int:comment_id>

            Retrieves an item comment.

            **Example Request**:

            .. sourcecode:: http

                GET /api/1/items/1234/comment/7718 HTTP/1.1
                Host: example.com
                Accept: application/json

            **Example Response**:

            .. sourcecode:: http

                HTTP/1.1 200 OK
                Vary: Accept
                Content-Type: application/json

                {
                    'id': 7719,
                    'date_created': "2013-10-04 22:01:47",
                    'text': 'This is an Item Comment.',
                    'item_id': 1111
                }

            :statuscode 200: Success
            :statuscode 404: Comment with given ID not found.
            :statuscode 401: Authentication Error. Please Login.
        """

        query = ItemComment.query.filter(ItemComment.id == comment_id)
        query = query.filter(ItemComment.item_id == item_id)
        ic = query.first()

        if ic is None:
            return {"status": "Item Comment Not Found"}, 404

        comment_marshaled = marshal(ic.__dict__, ITEM_COMMENT_FIELDS)
        comment_marshaled = dict(
            list(comment_marshaled.items()) +
            list({'user': ic.user.email}.items())
        )

        return comment_marshaled, 200
Ejemplo n.º 11
0
class ItemCommentDelete(AuthenticatedService):
    decorators = [
        rbac.allow(['Justify'], ["DELETE"])
    ]

    def delete(self, item_id, comment_id):
        """
            .. http:delete:: /api/1/items/<int:item_id>/comment/<int:comment_id>

            Deletes an item comment.

            **Example Request**:

            .. sourcecode:: http

                DELETE /api/1/items/1234/comment/7718 HTTP/1.1
                Host: example.com
                Accept: application/json

                {
                }

            **Example Response**:

            .. sourcecode:: http

                HTTP/1.1 202 OK
                Vary: Accept
                Content-Type: application/json

                {
                    'status': 'deleted'
                }

            :statuscode 202: Deleted
            :statuscode 401: Authentication Error. Please Login.
        """

        query = ItemComment.query.filter(ItemComment.id == comment_id)
        query.filter(ItemComment.user_id == current_user.id).delete()
        db.session.commit()

        return {'result': 'success'}, 202
Ejemplo n.º 12
0
class AccountListPut(AuthenticatedService):
    decorators = [rbac.allow(["Admin"], ["PUT"])]

    def __init__(self):
        super(AccountListPut, self).__init__()
        self.reqparse = reqparse.RequestParser()

    def put(self):
        values = json.loads(request.json)
        app.logger.debug("Account bulk update {}".format(values))
        for account_name in values.keys():
            account = Account.query.filter(
                Account.name == account_name).first()
            if account:
                account.active = values[account_name]
                db.session.add(account)

        db.session.commit()
        db.session.close()

        return {'status': 'updated'}, 200
Ejemplo n.º 13
0
class Distinct(AuthenticatedService):
    decorators = [rbac.allow(["View"], ["GET"])]

    def __init__(self):
        self.reqparse = reqparse.RequestParser()
        super(Distinct, self).__init__()

    def get(self, key_id):
        """
            .. http:get:: /api/1/distinct

            Get a list of distinct regions, names, accounts, accounttypes or technologies

            **Example Request**:

            .. sourcecode:: http

                GET /api/1/distinct/name HTTP/1.1
                Host: example.com
                Accept: application/json, text/javascript

            **Example Response**:

            .. sourcecode:: http

                HTTP/1.1 200 OK
                Vary: Accept
                Content-Type: application/json

            :statuscode 200: no error
        """

        self.reqparse.add_argument('count',
                                   type=int,
                                   default=30,
                                   location='args')
        self.reqparse.add_argument('page',
                                   type=int,
                                   default=1,
                                   location='args')
        self.reqparse.add_argument('select2',
                                   type=str,
                                   default="",
                                   location='args')
        self.reqparse.add_argument('searchconfig',
                                   type=str,
                                   default="",
                                   location='args')

        self.reqparse.add_argument('regions',
                                   type=str,
                                   default=None,
                                   location='args')
        self.reqparse.add_argument('accounts',
                                   type=str,
                                   default=None,
                                   location='args')
        self.reqparse.add_argument('accounttypes',
                                   type=str,
                                   default=None,
                                   location='args')
        self.reqparse.add_argument('technologies',
                                   type=str,
                                   default=None,
                                   location='args')
        self.reqparse.add_argument('names',
                                   type=str,
                                   default=None,
                                   location='args')
        self.reqparse.add_argument('active',
                                   type=str,
                                   default=None,
                                   location='args')

        args = self.reqparse.parse_args()
        page = args.pop('page', None)
        count = args.pop('count', None)
        q = args.pop('searchconfig', "").lower()
        select2 = args.pop('select2', "")
        for k, v in args.items():
            if not v:
                del args[k]

        if select2.lower() == 'true':
            select2 = True
        else:
            select2 = False

        query = Item.query
        if 'regions' in args and key_id != 'region':
            regions = args['regions'].split(',')
            query = query.filter(Item.region.in_(regions))
        if 'accounts' in args and key_id != 'account':
            query = query.join((Account, Account.id == Item.account_id))
            accounts = args['accounts'].split(',')
            query = query.filter(Account.name.in_(accounts))
        if 'accounttypes' in args and key_id != 'accounttype':
            query = query.join((Account, Account.id == Item.account_id)).join(
                (AccountType, AccountType.id == Account.account_type_id))
            accounttypes = args['accounttypes'].split(',')
            query = query.filter(AccountType.name.in_(accounttypes))
        if 'technologies' in args and key_id != 'tech':
            query = query.join((Technology, Technology.id == Item.tech_id))
            technologies = args['technologies'].split(',')
            query = query.filter(Technology.name.in_(technologies))
        if 'names' in args and key_id != 'name':
            names = args['names'].split(',')
            query = query.filter(Item.name.in_(names))
        if 'arns' in args and key_id != 'arn':
            names = args['arns'].split(',')
            query = query.filter(Item.arn.in_(names))
        if 'active' in args:
            query = query.join(
                (ItemRevision, Item.latest_revision_id == ItemRevision.id))
            active = args['active'].lower() == "true"
            query = query.filter(ItemRevision.active == active)

        if key_id == 'tech':
            query = query.join((Technology, Technology.id == Item.tech_id))
            if select2:
                query = query.distinct(Technology.name).filter(
                    func.lower(Technology.name).like('%' + q + '%'))
            else:
                query = query.distinct(Technology.name)
        elif key_id == 'accounttype':
            query = query.join((Account, Account.id == Item.account_id)).join(
                (AccountType, AccountType.id == Account.account_type_id))
            if select2:
                query = query.distinct(AccountType.name).filter(
                    func.lower(AccountType.name).like('%' + q + '%'))
            else:
                query = query.distinct(AccountType.name)
        elif key_id == 'account':
            query = query.join((Account, Account.id == Item.account_id))
            if select2:
                query = query.filter(Account.third_party == False)
                query = query.distinct(Account.name).filter(
                    func.lower(Account.name).like('%' + q + '%'))
            else:
                query = query.distinct(Account.name)

        else:
            filter_by = None
            if key_id == "region":
                filter_by = Item.region
            elif key_id == "name":
                filter_by = Item.name
            elif key_id == "arn":
                filter_by = Item.arn
            else:
                return json.loads(
                    '{ "error": "Supply key in type,region,account,name,arn" }'
                )

            if select2:
                query = query.distinct(filter_by).filter(
                    func.lower(filter_by).like('%' + q + '%'))
            else:
                query = query.distinct(filter_by)

        items = query.paginate(page, count, error_out=False)

        marshaled_dict = {}
        list_distinct = []
        for item in items.items:
            if key_id == "tech":
                text = item.technology.name
                item_id = item.id
            elif key_id == "account":
                text = item.account.name
                item_id = item.id
            elif key_id == "accounttype":
                text = item.account.account_type.name
                item_id = item.id
            elif key_id == "region":
                text = item.region
                item_id = item.id
            elif key_id == "name":
                text = item.name
                item_id = item.id
            elif key_id == "arn":
                text = item.arn
                item_id = item.id
            if (select2):
                list_distinct.append({"id": item_id, "text": text})
            else:
                list_distinct.append(text)

        marshaled_dict['auth'] = self.auth_dict
        marshaled_dict['items'] = list_distinct
        marshaled_dict['page'] = items.page
        marshaled_dict['total'] = items.total
        marshaled_dict['key_id'] = key_id
        return marshaled_dict, 200
class RevisionList(AuthenticatedService):
    decorators = [rbac.allow(["View"], ["GET"])]

    def get(self):
        """
            .. http:get:: /api/1/revisions

            Get a list of revisions

            **Example Request**:

            .. sourcecode:: http

                GET /api/1/revisions?count=1 HTTP/1.1
                Host: example.com
                Accept: application/json

            **Example Response**:

            .. sourcecode:: http

                HTTP/1.1 200 OK
                Vary: Accept
                Content-Type: application/json

                {
                    "items": [
                        {
                            "account": "example_account",
                            "name": "Example Name",
                            "region": "us-east-1",
                            "item_id": 144,
                            "active": false,
                            "date_created": "2014-06-19 20:54:12.962951",
                            "technology": "sqs",
                            "id": 223757
                        }
                    ],
                    "total": 1,
                    "page": 1,
                    "auth": {
                        "authenticated": true,
                        "user": "******"
                    }
                }

            :statuscode 200: no error
            :statuscode 401: Authentication Error. Please Login.
        """

        self.reqparse.add_argument('count',
                                   type=int,
                                   default=30,
                                   location='args')
        self.reqparse.add_argument('page',
                                   type=int,
                                   default=1,
                                   location='args')
        self.reqparse.add_argument('active',
                                   type=str,
                                   default=None,
                                   location='args')
        self.reqparse.add_argument('regions',
                                   type=str,
                                   default=None,
                                   location='args')
        self.reqparse.add_argument('accounts',
                                   type=str,
                                   default=None,
                                   location='args')
        self.reqparse.add_argument('names',
                                   type=str,
                                   default=None,
                                   location='args')
        self.reqparse.add_argument('arns',
                                   type=str,
                                   default=None,
                                   location='args')
        self.reqparse.add_argument('technologies',
                                   type=str,
                                   default=None,
                                   location='args')
        self.reqparse.add_argument('searchconfig',
                                   type=str,
                                   default=None,
                                   location='args')
        args = self.reqparse.parse_args()

        page = args.pop('page', None)
        count = args.pop('count', None)
        for k, v in args.items():
            if not v:
                del args[k]

        query = ItemRevision.query.join("item")
        if 'regions' in args:
            regions = args['regions'].split(',')
            query = query.filter(Item.region.in_(regions))
        if 'accounts' in args:
            accounts = args['accounts'].split(',')
            query = query.join((Account, Account.id == Item.account_id))
            query = query.filter(Account.name.in_(accounts))
        if 'technologies' in args:
            technologies = args['technologies'].split(',')
            query = query.join((Technology, Technology.id == Item.tech_id))
            query = query.filter(Technology.name.in_(technologies))
        if 'names' in args:
            names = args['names'].split(',')
            query = query.filter(Item.name.in_(names))
        if 'arns' in args:
            arns = args['arns'].split(',')
            query = query.filter(Item.arn.in_(arns))
        if 'active' in args:
            active = args['active'].lower() == "true"
            query = query.filter(ItemRevision.active == active)
        if 'searchconfig' in args:
            searchconfig = args['searchconfig']
            query = query.filter(
                cast(ItemRevision.config,
                     String).ilike('%{}%'.format(searchconfig)))
        query = query.order_by(ItemRevision.date_created.desc())
        revisions = query.paginate(page, count)

        marshaled_dict = {
            'page': revisions.page,
            'total': revisions.total,
            'auth': self.auth_dict
        }

        items_marshaled = []
        for revision in revisions.items:
            item_marshaled = marshal(revision.item.__dict__, ITEM_FIELDS)
            revision_marshaled = marshal(revision.__dict__, REVISION_FIELDS)
            account_marshaled = {'account': revision.item.account.name}
            technology_marshaled = {
                'technology': revision.item.technology.name
            }
            merged_marshaled = dict(item_marshaled.items() +
                                    revision_marshaled.items() +
                                    account_marshaled.items() +
                                    technology_marshaled.items())
            items_marshaled.append(merged_marshaled)

        marshaled_dict['items'] = items_marshaled
        marshaled_dict['count'] = len(items_marshaled)
        return marshaled_dict, 200
class RevisionGet(AuthenticatedService):
    decorators = [rbac.allow(["View"], ["GET"])]

    def __init__(self):
        self.reqparse = reqparse.RequestParser()
        super(RevisionGet, self).__init__()

    def get(self, revision_id):
        """
            .. http:get:: /api/1/revision/1234

            Get a specific revision.

            **Example Request**:

            .. sourcecode:: http

                GET /api/1/revision/123 HTTP/1.1
                Host: example.com
                Accept: application/json, text/javascript

            **Example Response**:

            .. sourcecode:: http

                HTTP/1.1 200 OK
                Vary: Accept
                Content-Type: application/json

                {
                    "auth": {
                        "authenticated": true,
                        "user": "******"
                    },
                    "item_id": 114,
                    "comments": [],
                    "active": false,
                    "date_created": "2013-10-04 22:01:47",
                    "config": {},
                    "id":123
                }

            :statuscode 200: no error
            :statuscode 401: Authentication failure. Please login.
        """
        query = ItemRevision.query.filter(ItemRevision.id == revision_id)
        result = query.first()

        comments = []
        for comment in result.comments:
            comment_marshaled = marshal(comment, REVISION_COMMENT_FIELDS)
            comments.append(
                dict(comment_marshaled.items() +
                     {'user': comment.user.email}.items()))

        cloudtrail_entries = []
        for entry in result.cloudtrail_entries:
            cloudtrail_entries.append(entry.full_entry)

        revision_marshaled = marshal(result, REVISION_FIELDS)
        revision_marshaled = dict(
            revision_marshaled.items() +
            {'config': OrderedDict(sorted(sub_dict(result.config).items()))
             }.items() + {'auth': self.auth_dict}.items() +
            {'comments': comments}.items() +
            {'cloudtrail': cloudtrail_entries}.items())

        self.reqparse.add_argument('compare',
                                   type=int,
                                   default=None,
                                   location='args')
        args = self.reqparse.parse_args()
        compare_id = args.pop('compare', None)
        if compare_id:
            query = ItemRevision.query.filter(ItemRevision.id == compare_id)
            compare_result = query.first()
            pdiff = PolicyDiff(
                OrderedDict(sorted(sub_dict(result.config).items())),
                OrderedDict(sorted(sub_dict(compare_result.config).items())))
            revision_marshaled = dict(
                revision_marshaled.items() +
                {'diff_html': pdiff.produceDiffHTML()}.items())

        return revision_marshaled, 200
Ejemplo n.º 16
0
class ItemCommentPost(AuthenticatedService):
    decorators = [rbac.allow(['Comment'], ["POST"])]

    def post(self, item_id):
        """
            .. http:post:: /api/1/items/<int:item_id>/comments

            Adds an item comment.

            **Example Request**:

            .. sourcecode:: http

                POST /api/1/items/1234/comments HTTP/1.1
                Host: example.com
                Accept: application/json

                {
                    "text": "This item is my favorite."
                }

            **Example Response**:

            .. sourcecode:: http

                HTTP/1.1 201 OK
                Vary: Accept
                Content-Type: application/json

                {
                    'item_id': 1234,
                    'id': 7718,
                    'comment': 'This item is my favorite.',
                    'user': '******'
                }
                {
                    "date_created": "2014-10-11 23:03:47.716698",
                    "id": 1,
                    "text": "This is an item comment."
                }

            :statuscode 201: Created
            :statuscode 401: Authentication Error. Please Login.
        """

        self.reqparse.add_argument('text',
                                   required=False,
                                   type=unicode,
                                   help='Must provide comment',
                                   location='json')
        args = self.reqparse.parse_args()

        ic = ItemComment()
        ic.user_id = current_user.id
        ic.item_id = item_id
        ic.text = args['text']
        ic.date_created = datetime.datetime.utcnow()

        db.session.add(ic)
        db.session.commit()
        db.session.refresh(ic)

        comment_marshaled = marshal(ic.__dict__, ITEM_COMMENT_FIELDS)
        comment_marshaled = dict(comment_marshaled.items() +
                                 {'user': ic.user.email}.items())

        return comment_marshaled, 201
Ejemplo n.º 17
0
class Ping(Resource):
    """
    This class serves as an example of how one might implement an SSO provider for use with Security Monkey. In
    this example we use a OpenIDConnect authentication flow, that is essentially OAuth2 underneath.
    """
    decorators = [rbac.allow(["anonymous"], ["GET", "POST"])]

    def __init__(self):
        self.reqparse = reqparse.RequestParser()
        super(Ping, self).__init__()

    def get(self):
        return self.post()

    def post(self):
        if "ping" not in current_app.config.get("ACTIVE_PROVIDERS"):
            return "Ping is not enabled in the config.  See the ACTIVE_PROVIDERS section.", 404

        default_state = 'clientId,{client_id},redirectUri,{redirectUri},return_to,{return_to}'.format(
            client_id=current_app.config.get('PING_CLIENT_ID'),
            redirectUri=current_app.config.get('PING_REDIRECT_URI'),
            return_to=current_app.config.get('WEB_PATH'))
        self.reqparse.add_argument('code', type=str, required=True)
        self.reqparse.add_argument('state',
                                   type=str,
                                   required=False,
                                   default=default_state)

        args = self.reqparse.parse_args()
        client_id = args['state'].split(',')[1]
        redirect_uri = args['state'].split(',')[3]
        return_to = args['state'].split(',')[5]

        if not validate_redirect_url(return_to):
            return_to = current_app.config.get('WEB_PATH')

        # take the information we have received from the provider to create a new request
        params = {
            'client_id': client_id,
            'grant_type': 'authorization_code',
            'scope': 'openid email profile address',
            'redirect_uri': redirect_uri,
            'code': args['code']
        }

        # you can either discover these dynamically or simply configure them
        access_token_url = current_app.config.get('PING_ACCESS_TOKEN_URL')
        user_api_url = current_app.config.get('PING_USER_API_URL')

        # the secret and cliendId will be given to you when you signup for the provider
        basic = base64.b64encode(
            bytes('{0}:{1}'.format(client_id,
                                   current_app.config.get("PING_SECRET"))))
        headers = {'Authorization': 'Basic {0}'.format(basic.decode('utf-8'))}

        # exchange authorization code for access token.
        r = requests.post(access_token_url, headers=headers, params=params)
        id_token = r.json()['id_token']
        access_token = r.json()['access_token']

        # fetch token public key
        header_data = fetch_token_header_payload(id_token)[0]
        jwks_url = current_app.config.get('PING_JWKS_URL')

        # retrieve the key material as specified by the token header
        r = requests.get(jwks_url)
        for key in r.json()['keys']:
            if key['kid'] == header_data['kid']:
                secret = get_rsa_public_key(key['n'], key['e'])
                algo = header_data['alg']
                break
        else:
            return dict(message='Key not found'), 403

        # validate your token based on the key it was signed with
        try:
            current_app.logger.debug(id_token)
            current_app.logger.debug(secret)
            current_app.logger.debug(algo)
            jwt.decode(id_token,
                       secret.decode('utf-8'),
                       algorithms=[algo],
                       audience=client_id)
        except jwt.DecodeError:
            return dict(message='Token is invalid'), 403
        except jwt.ExpiredSignatureError:
            return dict(message='Token has expired'), 403
        except jwt.InvalidTokenError:
            return dict(message='Token is invalid'), 403

        user_params = dict(access_token=access_token, schema='profile')

        # retrieve information about the current user.
        r = requests.get(user_api_url, params=user_params)
        profile = r.json()

        user = User.query.filter(User.email == profile['email']).first()

        # if we get an sso user create them an account
        if not user:
            user = User(email=profile['email'],
                        active=True,
                        role='View'
                        # profile_picture=profile.get('thumbnailPhotoUrl')
                        )
            db.session.add(user)
            db.session.commit()
            db.session.refresh(user)

        # Tell Flask-Principal the identity changed
        identity_changed.send(current_app._get_current_object(),
                              identity=Identity(user.id))
        login_user(user)

        return redirect(return_to, code=302)
Ejemplo n.º 18
0
class RevisionCommentPost(AuthenticatedService):
    decorators = [
        rbac.allow(["Comment"], ["POST"])
    ]

    def post(self, revision_id):
        """
            .. http:post:: /api/1/revisions/<int:revision_id>/comments

            Create a new Revision Comment.

            **Example Request**:

            .. sourcecode:: http

                POST /api/1/revisions/1141/comments HTTP/1.1
                Host: example.com
                Accept: application/json

                {
                    "text": "This is a Revision Comment."
                }


            **Example Response**:

            .. sourcecode:: http

                HTTP/1.1 201 OK
                Vary: Accept
                Content-Type: application/json

                {
                    'id': 22,
                    'revision_id': 1141,
                    "date_created": "2013-10-04 22:01:47",
                    'text': 'This is a Revision Comment.'
                }

            :statuscode 201: Revision Comment Created
            :statuscode 401: Authentication Error. Please Login.
        """
        self.reqparse.add_argument('text', required=False, type=unicode, help='Must provide comment', location='json')
        args = self.reqparse.parse_args()

        irc = ItemRevisionComment()
        irc.user_id = current_user.id
        irc.revision_id = revision_id
        irc.text = args['text']
        irc.date_created = datetime.datetime.utcnow()

        db.session.add(irc)
        db.session.commit()
        db.session.refresh(irc)

        revision_marshaled = marshal(irc.__dict__, REVISION_COMMENT_FIELDS)
        revision_marshaled = dict(
            revision_marshaled.items() +
            {'user': irc.user.email}.items()
        )
        return revision_marshaled, 200
Ejemplo n.º 19
0
class Google(Resource):
    decorators = [rbac.allow(["anonymous"], ["GET", "POST"])]

    def __init__(self):
        self.reqparse = reqparse.RequestParser()
        super(Google, self).__init__()

    def get(self):
        return self.post()

    def post(self):
        if "google" not in current_app.config.get("ACTIVE_PROVIDERS"):
            return "Google is not enabled in the config.  See the ACTIVE_PROVIDERS section.", 404

        default_state = 'clientId,{client_id},redirectUri,{redirectUri},return_to,{return_to}'.format(
            client_id=current_app.config.get("GOOGLE_CLIENT_ID"),
            redirectUri=api.url_for(Google),
            return_to=current_app.config.get('WEB_PATH'))
        self.reqparse.add_argument('code', type=str, required=True)
        self.reqparse.add_argument('state',
                                   type=str,
                                   required=False,
                                   default=default_state)

        args = self.reqparse.parse_args()
        client_id = args['state'].split(',')[1]
        redirect_uri = args['state'].split(',')[3]
        return_to = args['state'].split(',')[5]

        if not validate_redirect_url(return_to):
            return_to = current_app.config.get('WEB_PATH')

        access_token_url = 'https://accounts.google.com/o/oauth2/token'
        people_api_url = 'https://www.googleapis.com/plus/v1/people/me/openIdConnect'

        args = self.reqparse.parse_args()

        # Step 1. Exchange authorization code for access token
        payload = {
            'client_id': client_id,
            'grant_type': 'authorization_code',
            'redirect_uri': redirect_uri,
            'code': args['code'],
            'client_secret': current_app.config.get('GOOGLE_SECRET')
        }

        r = requests.post(access_token_url, data=payload)
        token = r.json()

        # Step 1bis. Validate (some information of) the id token (if necessary)
        google_hosted_domain = current_app.config.get("GOOGLE_HOSTED_DOMAIN")
        if google_hosted_domain is not None:
            current_app.logger.debug(
                'We need to verify that the token was issued for this hosted domain: %s '
                % (google_hosted_domain))

            # Get the JSON Web Token
            id_token = r.json()['id_token']
            current_app.logger.debug('The id_token is: %s' % (id_token))

            # Extract the payload
            (header_data, payload_data) = fetch_token_header_payload(id_token)
            current_app.logger.debug('id_token.header_data: %s' %
                                     (header_data))
            current_app.logger.debug('id_token.payload_data: %s' %
                                     (payload_data))

            token_hd = payload_data.get('hd')
            if token_hd != google_hosted_domain:
                current_app.logger.debug('Verification failed: %s != %s' %
                                         (token_hd, google_hosted_domain))
                return dict(message='Token is invalid %s' % token), 403
            current_app.logger.debug('Verification passed')

        # Step 2. Retrieve information about the current user
        headers = {'Authorization': 'Bearer {0}'.format(token['access_token'])}

        r = requests.get(people_api_url, headers=headers)
        profile = r.json()

        user = User.query.filter(User.email == profile['email']).first()

        # if we get an sso user create them an account
        if not user:
            user = User(email=profile['email'],
                        active=True,
                        role='View'
                        # profile_picture=profile.get('thumbnailPhotoUrl')
                        )
            db.session.add(user)
            db.session.commit()
            db.session.refresh(user)

        # Tell Flask-Principal the identity changed
        identity_changed.send(current_app._get_current_object(),
                              identity=Identity(user.id))
        login_user(user)

        return redirect(return_to, code=302)
Ejemplo n.º 20
0
class AccountConfigGet(AuthenticatedService):
    decorators = [
        rbac.allow(["View"], ["GET"]),
    ]

    def __init__(self):
        self.reqparse = reqparse.RequestParser()
        super(AccountConfigGet, self).__init__()

    def get(self, account_fields):
        """
            .. http:get:: /api/1/account_config/account_fields (all or custom)

            Get a list of Account types

            **Example Request**:

            .. sourcecode:: http

                GET /api/1/account_config/all HTTP/1.1
                Host: example.com
                Accept: application/json, text/javascript

            **Example Response**:

            .. sourcecode:: http

                HTTP/1.1 200 OK
                Vary: Accept
                Content-Type: application/json

                {
                    auth: {
                        authenticated: true,
                        user: "******"
                    }
                }

            :statuscode 200: no error
            :statuscode 401: Authentication failure. Please login.
        """
        load_all_account_types()
        marshaled = {}
        account_types = AccountType.query.all()
        configs_marshaled = {}

        for account_type in account_types:
            acc_manager = account_registry.get(account_type.name)
            if acc_manager is not None:
                values = {}
                values['identifier_label'] = acc_manager.identifier_label
                values['identifier_tool_tip'] = acc_manager.identifier_tool_tip
                fields = []

                if account_fields == 'all':
                    fields.append({
                        'name': 'identifier',
                        'label': '',
                        'editable': True,
                        'tool_tip': '',
                        'password': False,
                        'allowed_values': None
                    })

                    fields.append({
                        'name': 'name',
                        'label': '',
                        'editable': True,
                        'tool_tip': '',
                        'password': False,
                        'allowed_values': None
                    })

                    fields.append({
                        'name': 'notes',
                        'label': '',
                        'editable': True,
                        'tool_tip': '',
                        'password': False,
                        'allowed_values': None
                    })

                for config in acc_manager.custom_field_configs:
                    if account_fields == 'custom' or not config.password:
                        field_marshaled = {
                            'name': config.name,
                            'label': config.label,
                            'editable': config.db_item,
                            'tool_tip': config.tool_tip,
                            'password': config.password,
                            'allowed_values': config.allowed_values
                        }
                        fields.append(field_marshaled)

                    values['fields'] = fields
                configs_marshaled[account_type.name] = values

        marshaled['custom_configs'] = configs_marshaled
        marshaled['auth'] = self.auth_dict

        return marshaled, 200
class AccountPostList(AuthenticatedService):
    decorators = [
        rbac.allow(["View"], ["GET"]),
        rbac.allow(["Admin"], ["POST"])
    ]

    def __init__(self):
        super(AccountPostList, self).__init__()
        self.reqparse = reqparse.RequestParser()

    def post(self):
        """
            .. http:post:: /api/1/account/

            Create a new account.

            **Example Request**:

            .. sourcecode:: http

                POST /api/1/account/ HTTP/1.1
                Host: example.com
                Accept: application/json

                {
                    'name': 'new_account'
                    'identifier': '0123456789',
                    'notes': 'this account is for ...',
                    'active': true,
                    'third_party': false
                    'account_type': 'AWS'
                }

            **Example Response**:

            .. sourcecode:: http

                HTTP/1.1 201 Created
                Vary: Accept
                Content-Type: application/json

                {
                    'name': 'new_account'
                    'identifier': '0123456789',
                    'notes': 'this account is for ...',
                    'active': true,
                    'third_party': false
                    'account_type': 'AWS'
                    ''
                }

            :statuscode 201: created
            :statuscode 401: Authentication Error. Please Login.
        """

        args = json.loads(request.json)
        account_type = args['account_type']
        name = args['name']
        identifier = args['identifier']
        notes = args['notes']
        active = args['active']
        third_party = args['third_party']
        custom_fields = args['custom_fields']

        from security_monkey.account_manager import account_registry
        account_manager = account_registry.get(account_type)()
        account = account_manager.create(account_type,
                                         name,
                                         active,
                                         third_party,
                                         notes,
                                         identifier,
                                         custom_fields=custom_fields)

        marshaled_account = marshal(account.__dict__, ACCOUNT_FIELDS)
        marshaled_account['auth'] = self.auth_dict
        return marshaled_account, 200

    def get(self):
        """
            .. http:get:: /api/1/accounts

            Get a list of Accounts matching the given criteria

            **Example Request**:

            .. sourcecode:: http

                GET /api/1/accounts HTTP/1.1
                Host: example.com
                Accept: application/json, text/javascript

            **Example Response**:

            .. sourcecode:: http

                HTTP/1.1 200 OK
                Vary: Accept
                Content-Type: application/json

                {
                    count: 1,
                    items: [
                        {
                            third_party: false,
                            name: "example_name",
                            notes: null,
                            role_name: null,
                            identifier: "111111111111",
                            active: true,
                            id: 1,
                            s3_name: "example_name"
                        },
                    ],
                    total: 1,
                    page: 1,
                    auth: {
                        authenticated: true,
                        user: "******"
                    }
                }

            :statuscode 200: no error
            :statuscode 401: Authentication failure. Please login.
        """

        self.reqparse.add_argument('count',
                                   type=int,
                                   default=30,
                                   location='args')
        self.reqparse.add_argument('page',
                                   type=int,
                                   default=1,
                                   location='args')
        self.reqparse.add_argument('order_by',
                                   type=str,
                                   default=None,
                                   location='args')
        self.reqparse.add_argument('order_dir',
                                   type=str,
                                   default='desc',
                                   location='args')
        self.reqparse.add_argument('active',
                                   type=str,
                                   default=None,
                                   location='args')
        self.reqparse.add_argument('third_party',
                                   type=str,
                                   default=None,
                                   location='args')

        args = self.reqparse.parse_args()
        page = args.pop('page', None)
        count = args.pop('count', None)
        order_by = args.pop('order_by', None)
        order_dir = args.pop('order_dir', None)
        for k, v in list(args.items()):
            if not v:
                del args[k]

        query = Account.query
        if 'active' in args:
            active = args['active'].lower() == "true"
            query = query.filter(Account.active == active)
        if 'third_party' in args:
            third_party = args['third_party'].lower() == "true"
            query = query.filter(Account.third_party == third_party)

        if order_by and hasattr(Account, order_by):
            if order_dir.lower() == 'asc':
                if order_by == 'account_type':
                    query = query.join(Account.account_type).order_by(
                        getattr(AccountType, 'name').asc())
                else:
                    query = query.order_by(getattr(Account, order_by).asc())
            else:
                if order_by == 'account_type':
                    query = query.join(Account.account_type).order_by(
                        getattr(AccountType, 'name').desc())
                else:
                    query = query.order_by(getattr(Account, order_by).desc())
        else:
            query = query.order_by(Account.id)

        result = query.paginate(page, count, error_out=False)

        items = []
        for account in result.items:
            account_marshaled = marshal(account.__dict__, ACCOUNT_FIELDS)
            account_marshaled = dict(
                list(account_marshaled.items()) +
                list({'account_type': account.account_type.name}.items()))

            items.append(account_marshaled)

        marshaled_dict = {
            'total': result.total,
            'count': len(items),
            'page': result.page,
            'items': items,
            'auth': self.auth_dict
        }

        return marshaled_dict, 200
Ejemplo n.º 22
0
class ItemList(AuthenticatedService):
    decorators = [rbac.allow(['View'], ["GET"])]

    def get(self):
        """
            .. http:get:: /api/1/items

            Get a list of items matching the given criteria.

            **Example Request**:

            .. sourcecode:: http

                GET /api/1/items HTTP/1.1
                Host: example.com
                Accept: application/json

            **Example Response**:

            .. sourcecode:: http

                HTTP/1.1 200 OK
                Vary: Accept
                Content-Type: application/json

                {
                    "items": [
                        {
                            "account": "example_account",
                            "region": "us-east-1",
                            "technology": "sqs",
                            "id": 14414,
                            "name": "example_name",
                            "num_issues": 3,
                            "issue_score": 9,
                            "unjustified_issue_score": 3,
                            "active" true,
                            "first_seen": "2014-06-17 19:47:07.299760",
                            "last_seen": "2014-06-18 11:53:16.467709"
                        }
                    ],
                    "total": 144,
                    "page": 1,
                    "auth": {
                        "authenticated": true,
                        "user": "******"
                    }
                }

            :statuscode 200: no error
            :statuscode 401: Authenciation Error. Please Login.
        """

        self.reqparse.add_argument('count',
                                   type=int,
                                   default=30,
                                   location='args')
        self.reqparse.add_argument('page',
                                   type=int,
                                   default=1,
                                   location='args')
        self.reqparse.add_argument('regions',
                                   type=str,
                                   default=None,
                                   location='args')
        self.reqparse.add_argument('accounts',
                                   type=str,
                                   default=None,
                                   location='args')
        self.reqparse.add_argument('active',
                                   type=str,
                                   default=None,
                                   location='args')
        self.reqparse.add_argument('names',
                                   type=str,
                                   default=None,
                                   location='args')
        self.reqparse.add_argument('arns',
                                   type=str,
                                   default=None,
                                   location='args')
        self.reqparse.add_argument('technologies',
                                   type=str,
                                   default=None,
                                   location='args')
        self.reqparse.add_argument('searchconfig',
                                   type=str,
                                   default=None,
                                   location='args')
        self.reqparse.add_argument('ids',
                                   type=int,
                                   default=None,
                                   location='args')
        self.reqparse.add_argument('summary',
                                   type=bool,
                                   default=False,
                                   location='args')
        self.reqparse.add_argument('min_score',
                                   type=int,
                                   default=False,
                                   location='args')
        self.reqparse.add_argument('min_unjustified_score',
                                   type=int,
                                   default=False,
                                   location='args')
        args = self.reqparse.parse_args()

        page = args.pop('page', None)
        count = args.pop('count', None)
        for k, v in args.items():
            if not v:
                del args[k]

        # Read more about filtering:
        # http://docs.sqlalchemy.org/en/rel_0_7/orm/query.html
        query = Item.query.join(
            (ItemRevision, Item.latest_revision_id == ItemRevision.id))
        if 'regions' in args:
            regions = args['regions'].split(',')
            query = query.filter(Item.region.in_(regions))
        if 'accounts' in args:
            accounts = args['accounts'].split(',')
            query = query.join((Account, Account.id == Item.account_id))
            query = query.filter(Account.name.in_(accounts))
        if 'technologies' in args:
            technologies = args['technologies'].split(',')
            query = query.join((Technology, Technology.id == Item.tech_id))
            query = query.filter(Technology.name.in_(technologies))
        if 'names' in args:
            names = args['names'].split(',')
            query = query.filter(Item.name.in_(names))
        if 'arns' in args:
            arns = args['arns'].split(',')
            query = query.filter(Item.arn.in_(arns))
        if 'ids' in args:
            ids = args['ids'].split(',')
            query = query.filter(Item.id.in_(ids))
        if 'active' in args:
            active = args['active'].lower() == "true"
            query = query.filter(ItemRevision.active == active)
        if 'searchconfig' in args:
            searchconfig = args['searchconfig']
            query = query.filter(
                cast(ItemRevision.config,
                     String).ilike('%{}%'.format(searchconfig)))
        if 'min_score' in args:
            min_score = args['min_score']
            query = query.filter(Item.score >= min_score)
        if 'min_unjustified_score' in args:
            min_unjustified_score = args['min_unjustified_score']
            query = query.filter(
                Item.unjustified_score >= min_unjustified_score)

        # Eager load the joins except for the revisions because of the dynamic lazy relationship
        query = query.options(joinedload('issues'))
        query = query.options(joinedload('account'))
        query = query.options(joinedload('technology'))

        query = query.order_by(ItemRevision.date_created.desc())

        items = query.paginate(page, count)

        marshaled_dict = {
            'page': items.page,
            'total': items.total,
            'auth': self.auth_dict
        }

        marshaled_items = []
        for item in items.items:
            item_marshaled = marshal(item.__dict__, ITEM_FIELDS)

            if 'summary' in args and args['summary']:
                item_marshaled = dict(
                    item_marshaled.items() + {
                        'account': item.account.name,
                        'technology': item.technology.name,
                        'num_issues': item.issue_count,
                        'issue_score': item.score,
                        'unjustified_issue_score': item.unjustified_score,
                        'active': active,
                        #'last_rev': item.revisions[0].config,
                    }.items())
            else:
                first_seen_query = ItemRevision.query.filter(
                    ItemRevision.item_id == item.id).order_by(
                        ItemRevision.date_created.asc())
                first_seen = str(first_seen_query.first().date_created)
                last_seen = str(item.revisions.first().date_created)
                active = item.revisions.first().active
                item_marshaled = dict(
                    item_marshaled.items() + {
                        'account': item.account.name,
                        'technology': item.technology.name,
                        'num_issues': item.issue_count,
                        'issue_score': item.score,
                        'unjustified_issue_score': item.unjustified_score,
                        'active': active,
                        'first_seen': first_seen,
                        'last_seen': last_seen
                        # 'last_rev': item.revisions[0].config,
                    }.items())

            marshaled_items.append(item_marshaled)

        marshaled_dict['items'] = marshaled_items
        marshaled_dict['count'] = len(marshaled_items)

        return marshaled_dict, 200
class AccountGetPutDelete(AuthenticatedService):
    decorators = [
        rbac.allow(["View"], ["GET"]),
        rbac.allow(["Admin"], ["PUT", "DELETE"])
    ]

    def __init__(self):
        self.reqparse = reqparse.RequestParser()
        super(AccountGetPutDelete, self).__init__()

    def get(self, account_id):
        """
            .. http:get:: /api/1/account/<int:id>

            Get a list of Accounts matching the given criteria

            **Example Request**:

            .. sourcecode:: http

                GET /api/1/account/1 HTTP/1.1
                Host: example.com
                Accept: application/json, text/javascript

            **Example Response**:

            .. sourcecode:: http

                HTTP/1.1 200 OK
                Vary: Accept
                Content-Type: application/json

                {
                    third_party: false,
                    name: "example_name",
                    notes: null,
                    identifier: "111111111111",
                    active: true,
                    id: 1,
                    account_type: "AWS",
                    auth: {
                        authenticated: true,
                        user: "******"
                    }
                }

            :statuscode 200: no error
            :statuscode 401: Authentication failure. Please login.
        """

        result = get_account_by_id(account_id)

        account_marshaled = marshal(result.__dict__, ACCOUNT_FIELDS)
        account_marshaled = dict(
            list(account_marshaled.items()) +
            list({'account_type': result.account_type.name}.items()))

        custom_fields_marshaled = []
        for field in result.custom_fields:
            field_marshaled = {
                'name': field.name,
                'value': field.value,
            }
            custom_fields_marshaled.append(field_marshaled)
        account_marshaled['custom_fields'] = custom_fields_marshaled

        account_marshaled['auth'] = self.auth_dict
        return account_marshaled, 200

    def put(self, account_id):
        """
            .. http:put:: /api/1/account/1

            Edit an existing account.

            **Example Request**:

            .. sourcecode:: http

                PUT /api/1/account/1 HTTP/1.1
                Host: example.com
                Accept: application/json

                {
                    'name': 'edited_account'
                    'identifier': '0123456789',
                    'notes': 'this account is for ...',
                    'active': true,
                    'third_party': false
                }

            **Example Response**:

            .. sourcecode:: http

                HTTP/1.1 200 OK
                Vary: Accept
                Content-Type: application/json

                {
                    'name': 'edited_account'
                    'identifier': '0123456789',
                    'notes': 'this account is for ...',
                    'active': true,
                    'third_party': false
                    'account_type': 'AWS'
                }

            :statuscode 200: no error
            :statuscode 401: Authentication Error. Please Login.
        """

        args = json.loads(request.json)
        account_type = args['account_type']
        name = args['name']
        identifier = args['identifier']
        notes = args['notes']
        active = args['active']
        third_party = args['third_party']
        custom_fields = args['custom_fields']

        from security_monkey.account_manager import account_registry
        account_manager = account_registry.get(account_type)()

        try:
            account = account_manager.update(account_id,
                                             account_type,
                                             name,
                                             active,
                                             third_party,
                                             notes,
                                             identifier,
                                             custom_fields=custom_fields)
        except AccountNameExists as _:
            return {'status': 'error. Account name exists.'}, 409

        if not account:
            return {'status': 'error. Account ID not found.'}, 404

        from security_monkey.common.audit_issue_cleanup import clean_account_issues
        clean_account_issues(account)

        marshaled_account = marshal(account.__dict__, ACCOUNT_FIELDS)
        marshaled_account['auth'] = self.auth_dict

        return marshaled_account, 200

    def delete(self, account_id):
        """
            .. http:delete:: /api/1/account/1

            Delete an account.

            **Example Request**:

            .. sourcecode:: http

                DELETE /api/1/account/1 HTTP/1.1
                Host: example.com
                Accept: application/json

            **Example Response**:

            .. sourcecode:: http

                HTTP/1.1 202 Accepted
                Vary: Accept
                Content-Type: application/json

                {
                    'status': 'deleted'
                }

            :statuscode 202: accepted
            :statuscode 401: Authentication Error. Please Login.
        """
        delete_account_by_id(account_id)
        return {'status': 'deleted'}, 202
Ejemplo n.º 24
0
class WhitelistGetPutDelete(AuthenticatedService):
    decorators = [
        rbac.allow(["Admin"], ["GET", "PUT", "DELETE"])
    ]

    def __init__(self):
        self.reqparse = reqparse.RequestParser()
        super(WhitelistGetPutDelete, self).__init__()

    def get(self, item_id):
        """
            .. http:get:: /api/1/whitelistcidrs/<int:id>

            Get the whitelist entry with the given ID.

            **Example Request**:

            .. sourcecode:: http

                GET /api/1/whitelistcidrs/123 HTTP/1.1
                Host: example.com
                Accept: application/json, text/javascript

            **Example Response**:

            .. sourcecode:: http

                HTTP/1.1 200 OK
                Vary: Accept
                Content-Type: application/json

                {
                    "id": 123,
                    "name": "Corp",
                    "notes": "Corporate Network",
                    "cidr": "1.2.3.4/22",
                    auth: {
                        authenticated: true,
                        user: "******"
                    }
                }

            :statuscode 200: no error
            :statuscode 404: item with given ID not found
            :statuscode 401: Authentication failure. Please login.
        """

        result = NetworkWhitelistEntry.query.filter(NetworkWhitelistEntry.id == item_id).first()

        if not result:
            return {"status": "Whitelist entry with the given ID not found."}, 404

        whitelistentry_marshaled = marshal(result.__dict__, WHITELIST_FIELDS)
        whitelistentry_marshaled['auth'] = self.auth_dict

        return whitelistentry_marshaled, 200

    def put(self, item_id):
        """
            .. http:get:: /api/1/whitelistcidrs/<int:id>

            Update the whitelist entry with the given ID.

            **Example Request**:

            .. sourcecode:: http

                PUT /api/1/whitelistcidrs/123 HTTP/1.1
                Host: example.com
                Accept: application/json, text/javascript

                {
                    "id": 123,
                    "name": "Corp",
                    "notes": "Corporate Network - New",
                    "cidr": "2.2.0.0/16"
                }

            **Example Response**:

            .. sourcecode:: http

                HTTP/1.1 200 OK
                Vary: Accept
                Content-Type: application/json

                {
                    "id": 123,
                    "name": "Corp",
                    "notes": "Corporate Network - New",
                    "cidr": "2.2.0.0/16",
                    auth: {
                        authenticated: true,
                        user: "******"
                    }
                }

            :statuscode 200: no error
            :statuscode 404: item with given ID not found
            :statuscode 401: Authentication failure. Please login.
        """

        self.reqparse.add_argument('name', required=True, type=text_type, help='Must provide account name', location='json')
        self.reqparse.add_argument('cidr', required=True, type=text_type, help='Network CIDR required.', location='json')
        self.reqparse.add_argument('notes', required=False, type=text_type, help='Add context.', location='json')
        args = self.reqparse.parse_args()

        name = args['name']
        cidr = args.get('cidr', True)
        notes = args.get('notes', None)

        result = NetworkWhitelistEntry.query.filter(NetworkWhitelistEntry.id == item_id).first()

        if not result:
            return {"status": "Whitelist entry with the given ID not found."}, 404

        result.name = name
        result.cidr = cidr
        result.notes = notes

        db.session.add(result)
        db.session.commit()
        db.session.refresh(result)

        whitelistentry_marshaled = marshal(result.__dict__, WHITELIST_FIELDS)
        whitelistentry_marshaled['auth'] = self.auth_dict

        return whitelistentry_marshaled, 200

    def delete(self, item_id):
        """
            .. http:delete:: /api/1/whitelistcidrs/123

            Delete a network whitelist entry.

            **Example Request**:

            .. sourcecode:: http

                DELETE /api/1/whitelistcidrs/123 HTTP/1.1
                Host: example.com
                Accept: application/json

            **Example Response**:

            .. sourcecode:: http

                HTTP/1.1 202 Accepted
                Vary: Accept
                Content-Type: application/json

                {
                    'status': 'deleted'
                }

            :statuscode 202: accepted
            :statuscode 401: Authentication Error. Please Login.
        """
        NetworkWhitelistEntry.query.filter(NetworkWhitelistEntry.id == item_id).delete()
        db.session.commit()

        return {'status': 'deleted'}, 202
Ejemplo n.º 25
0
class WhitelistListPost(AuthenticatedService):
    decorators = [
        rbac.allow(["Admin"], ["GET", "POST"]),
        rbac.allow(["View"], ["GET"])
    ]

    def get(self):
        """
            .. http:get:: /api/1/whitelistcidrs

            Get a list of CIDR's whitelisted to be used in security groups.

            **Example Request**:

            .. sourcecode:: http

                GET /api/1/whitelistcidrs HTTP/1.1
                Host: example.com
                Accept: application/json, text/javascript

            **Example Response**:

            .. sourcecode:: http

                HTTP/1.1 200 OK
                Vary: Accept
                Content-Type: application/json

                {
                    count: 1,
                    items: [
                        {
                            "id": 123,
                            "name": "Corp",
                            "notes": "Corporate Network",
                            "cidr": "1.2.3.4/22"
                        },
                    ],
                    total: 1,
                    page: 1,
                    auth: {
                        authenticated: true,
                        user: "******"
                    }
                }

            :statuscode 200: no error
            :statuscode 401: Authentication failure. Please login.
        """

        self.reqparse.add_argument('count', type=int, default=30, location='args')
        self.reqparse.add_argument('page', type=int, default=1, location='args')

        args = self.reqparse.parse_args()
        page = args.pop('page', None)
        count = args.pop('count', None)

        result = NetworkWhitelistEntry.query.order_by(NetworkWhitelistEntry.id).paginate(page, count, error_out=False)

        items = []
        for entry in result.items:
            whitelistentry_marshaled = marshal(entry.__dict__, WHITELIST_FIELDS)
            items.append(whitelistentry_marshaled)

        marshaled_dict = {
            'total': result.total,
            'count': len(items),
            'page': result.page,
            'items': items,
            'auth': self.auth_dict
        }

        return marshaled_dict, 200

    def post(self):
        """
            .. http:post:: /api/1/whitelistcidrs

            Create a new CIDR whitelist entry.

            **Example Request**:

            .. sourcecode:: http

                POST /api/1/whitelistcidrs HTTP/1.1
                Host: example.com
                Accept: application/json

                {
                    "name": "Corp",
                    "notes": "Corporate Network",
                    "cidr": "1.2.3.4/22"
                }

            **Example Response**:

            .. sourcecode:: http

                HTTP/1.1 201 Created
                Vary: Accept
                Content-Type: application/json

                {
                    "id": 123,
                    "name": "Corp",
                    "notes": "Corporate Network",
                    "cidr": "1.2.3.4/22"
                }

            :statuscode 201: created
            :statuscode 401: Authentication Error. Please Login.
        """

        self.reqparse.add_argument('name', required=True, type=text_type, help='Must provide account name', location='json')
        self.reqparse.add_argument('cidr', required=True, type=text_type, help='Network CIDR required.', location='json')
        self.reqparse.add_argument('notes', required=False, type=text_type, help='Add context.', location='json')
        args = self.reqparse.parse_args()

        name = args['name']
        cidr = args.get('cidr', True)
        notes = args.get('notes', None)

        whitelist_entry = NetworkWhitelistEntry()
        whitelist_entry.name = name
        whitelist_entry.cidr = cidr
        if notes:
            whitelist_entry.notes = notes

        db.session.add(whitelist_entry)
        db.session.commit()
        db.session.refresh(whitelist_entry)

        whitelistentry_marshaled = marshal(whitelist_entry.__dict__, WHITELIST_FIELDS)
        whitelistentry_marshaled['auth'] = self.auth_dict
        return whitelistentry_marshaled, 201
Ejemplo n.º 26
0
class ItemAuditList(AuthenticatedService):
    decorators = [rbac.allow(["View"], ["GET"])]

    def get(self):
        """
             .. http:get:: /api/1/issues

             Get a list of Audit Issues matching the given criteria

             **Example Request**:

             .. sourcecode:: http

                 GET /api/1/issues HTTP/1.1
                 Host: example.com
                 Accept: application/json, text/javascript

             **Example Response**:

             .. sourcecode:: http

                 HTTP/1.1 200 OK
                 Vary: Accept
                 Content-Type: application/json

                 {
                    items: [
                        {
                            account: "example_account",
                            justification: null,
                            name: "example_name",
                            technology: "s3",
                            issue: "Example Issue",
                            region: "us-east-1",
                            score: 10,
                            notes: "Example Notes",
                            item_id: 11,
                            justified: false,
                            justified_date: null,
                            id: 595
                        }
                    ],
                    total: 1,
                    page: 1,
                    auth: {
                        authenticated: true,
                        user: "******"
                    }
                }

             :statuscode 200: no error
             :statuscode 401: Authentication failure. Please login.
        """

        self.reqparse.add_argument('count',
                                   type=int,
                                   default=30,
                                   location='args')
        self.reqparse.add_argument('page',
                                   type=int,
                                   default=1,
                                   location='args')
        self.reqparse.add_argument('regions',
                                   type=str,
                                   default=None,
                                   location='args')
        self.reqparse.add_argument('accounts',
                                   type=str,
                                   default=None,
                                   location='args')
        self.reqparse.add_argument('technologies',
                                   type=str,
                                   default=None,
                                   location='args')
        self.reqparse.add_argument('names',
                                   type=str,
                                   default=None,
                                   location='args')
        self.reqparse.add_argument('arns',
                                   type=str,
                                   default=None,
                                   location='args')
        self.reqparse.add_argument('active',
                                   type=str,
                                   default=None,
                                   location='args')
        self.reqparse.add_argument('searchconfig',
                                   type=str,
                                   default=None,
                                   location='args')
        self.reqparse.add_argument('enabledonly',
                                   type=bool,
                                   default=None,
                                   location='args')
        args = self.reqparse.parse_args()

        page = args.pop('page', None)
        count = args.pop('count', None)
        for k, v in args.items():
            if not v:
                del args[k]

        query = ItemAudit.query.join("item")
        if 'regions' in args:
            regions = args['regions'].split(',')
            query = query.filter(Item.region.in_(regions))
        if 'accounts' in args:
            accounts = args['accounts'].split(',')
            query = query.join((Account, Account.id == Item.account_id))
            query = query.filter(Account.name.in_(accounts))
        if 'technologies' in args:
            technologies = args['technologies'].split(',')
            query = query.join((Technology, Technology.id == Item.tech_id))
            query = query.filter(Technology.name.in_(technologies))
        if 'names' in args:
            names = args['names'].split(',')
            query = query.filter(Item.name.in_(names))
        if 'arns' in args:
            arns = args['arns'].split(',')
            query = query.filter(Item.arn.in_(arns))
        if 'active' in args:
            active = args['active'].lower() == "true"
            query = query.join(
                (ItemRevision, Item.latest_revision_id == ItemRevision.id))
            query = query.filter(ItemRevision.active == active)
        if 'searchconfig' in args:
            search = args['searchconfig']
            query = query.filter(
                (ItemAudit.issue.ilike('%{}%'.format(search)))
                | (ItemAudit.notes.ilike('%{}%'.format(search)))
                | (ItemAudit.justification.ilike('%{}%'.format(search)))
                | (Item.name.ilike('%{}%'.format(search))))
        if 'enabledonly' in args:
            query = query.join(
                (AuditorSettings,
                 AuditorSettings.id == ItemAudit.auditor_setting_id))
            query = query.filter(AuditorSettings.disabled == False)

        query = query.order_by(ItemAudit.justified, ItemAudit.score.desc())
        issues = query.paginate(page, count)

        marshaled_dict = {
            'page': issues.page,
            'total': issues.total,
            'auth': self.auth_dict
        }

        items_marshaled = []
        for issue in issues.items:
            item_marshaled = marshal(issue.item.__dict__, ITEM_FIELDS)
            issue_marshaled = marshal(issue.__dict__, AUDIT_FIELDS)
            account_marshaled = {'account': issue.item.account.name}
            technology_marshaled = {'technology': issue.item.technology.name}
            if issue.justified:
                issue_marshaled = dict(
                    issue_marshaled.items() +
                    {'justified_user': issue.user.email}.items())
            merged_marshaled = dict(item_marshaled.items() +
                                    issue_marshaled.items() +
                                    account_marshaled.items() +
                                    technology_marshaled.items())
            items_marshaled.append(merged_marshaled)

        marshaled_dict['items'] = items_marshaled
        marshaled_dict['count'] = len(items_marshaled)
        return marshaled_dict, 200
Ejemplo n.º 27
0
class ItemAuditList(AuthenticatedService):
    decorators = [
        rbac.allow(["View"], ["GET"])
    ]

    def get(self):
        """
             .. http:get:: /api/1/issues

             Get a list of Audit Issues matching the given criteria

             **Example Request**:

             .. sourcecode:: http

                 GET /api/1/issues HTTP/1.1
                 Host: example.com
                 Accept: application/json, text/javascript

             **Example Response**:

             .. sourcecode:: http

                 HTTP/1.1 200 OK
                 Vary: Accept
                 Content-Type: application/json

                 {
                    items: [
                        {
                            account: "example_account",
                            account_type: "AWS",
                            justification: null,
                            name: "example_name",
                            technology: "s3",
                            issue: "Example Issue",
                            region: AWS_DEFAULT_REGION,
                            score: 10,
                            notes: "Example Notes",
                            item_id: 11,
                            justified: false,
                            justified_date: null,
                            id: 595
                        }
                    ],
                    total: 1,
                    page: 1,
                    auth: {
                        authenticated: true,
                        user: "******"
                    }
                }

             :statuscode 200: no error
             :statuscode 401: Authentication failure. Please login.
        """

        self.reqparse.add_argument('count', type=int, default=30, location='args')
        self.reqparse.add_argument('page', type=int, default=1, location='args')
        self.reqparse.add_argument('regions', type=str, default=None, location='args')
        self.reqparse.add_argument('accounts', type=str, default=None, location='args')
        self.reqparse.add_argument('accounttypes', type=str, default=None, location='args')
        self.reqparse.add_argument('technologies', type=str, default=None, location='args')
        self.reqparse.add_argument('names', type=str, default=None, location='args')
        self.reqparse.add_argument('arns', type=str, default=None, location='args')
        self.reqparse.add_argument('active', type=str, default=None, location='args')
        self.reqparse.add_argument('searchconfig', type=str, default=None, location='args')
        self.reqparse.add_argument('enabledonly', type=bool, default=None, location='args')
        self.reqparse.add_argument('justified', type=str, default=None, location='args')
        self.reqparse.add_argument('summary', type=str, default=None, location='args')
        args = self.reqparse.parse_args()

        page = args.pop('page', None)
        count = args.pop('count', None)
        for k, v in list(args.items()):
            if not v:
                del args[k]

        query = ItemAudit.query.join("item")
        query = query.filter(ItemAudit.fixed == False)
        if 'regions' in args:
            regions = args['regions'].split(',')
            query = query.filter(Item.region.in_(regions))
        if 'accounts' in args:
            accounts = args['accounts'].split(',')
            query = query.join((Account, Account.id == Item.account_id))
            query = query.filter(Account.name.in_(accounts))
        if 'accounttypes' in args:
            accounttypes = args['accounttypes'].split(',')
            query = query.join((Account, Account.id == Item.account_id))
            query = query.join((AccountType, AccountType.id == Account.account_type_id))
            query = query.filter(AccountType.name.in_(accounttypes))
        if 'technologies' in args:
            technologies = args['technologies'].split(',')
            query = query.join((Technology, Technology.id == Item.tech_id))
            query = query.filter(Technology.name.in_(technologies))
        if 'names' in args:
            names = args['names'].split(',')
            query = query.filter(Item.name.in_(names))
        if 'arns' in args:
            arns = args['arns'].split(',')
            query = query.filter(Item.arn.in_(arns))
        if 'active' in args:
            active = args['active'].lower() == "true"
            query = query.join((ItemRevision, Item.latest_revision_id == ItemRevision.id))
            query = query.filter(ItemRevision.active == active)
        if 'searchconfig' in args:
            search = args['searchconfig'].split(',')
            conditions = []
            for searchterm in search:
                conditions.append(ItemAudit.issue.ilike('%{}%'.format(searchterm)))
                conditions.append(ItemAudit.notes.ilike('%{}%'.format(searchterm)))
                conditions.append(ItemAudit.justification.ilike('%{}%'.format(searchterm)))
                conditions.append(Item.name.ilike('%{}%'.format(searchterm))) 
            query = query.filter(or_(*conditions))
        if 'enabledonly' in args:
            query = query.join((AuditorSettings, AuditorSettings.id == ItemAudit.auditor_setting_id))
            query = query.filter(AuditorSettings.disabled == False)
        if 'justified' in args:
            justified = args['justified'].lower() == "true"
            query = query.filter(ItemAudit.justified == justified)
        if 'summary' in args:
            # Summary wants to order by oldest issues
            # TODO: Add date_created column to ItemAudit, and have summary order by date_created
            # Order by justified_date until date_created exists
            query = query.order_by(ItemAudit.justified_date.asc())
        else:
            query = query.order_by(ItemAudit.justified, ItemAudit.score.desc())

        issues = query.paginate(page, count)

        marshaled_dict = {
            'page': issues.page,
            'total': issues.total,
            'auth': self.auth_dict
        }

        items_marshaled = []
        for issue in issues.items:
            # TODO: This MUST be modified when switching to new issue logic in future:
            #       Currently there should be exactly 1 item in the list of sub_items:
            item_marshaled = marshal(issue.item.__dict__, ITEM_FIELDS)
            issue_marshaled = marshal(issue.__dict__, AUDIT_FIELDS)
            account_marshaled = {'account': issue.item.account.name}
            accounttype_marshaled = {'account_type': issue.item.account.account_type.name}
            technology_marshaled = {'technology': issue.item.technology.name}

            links = []
            for link in issue.sub_items:
                item_link_marshaled = marshal(link.__dict__, ITEM_LINK_FIELDS)
                links.append(item_link_marshaled)

            issue_marshaled['item_links'] = links

            if issue.justified:
                if issue.user is not None:
                    issue_marshaled = dict(
                        list(issue_marshaled.items()) +
                        list({'justified_user': issue.user.email}.items()))
            merged_marshaled = dict(
                list(item_marshaled.items()) +
                list(issue_marshaled.items()) +
                list(account_marshaled.items()) +
                list(accounttype_marshaled.items()) +
                list(technology_marshaled.items()))
            items_marshaled.append(merged_marshaled)

        marshaled_dict['items'] = items_marshaled
        marshaled_dict['count'] = len(items_marshaled)
        return marshaled_dict, 200
Ejemplo n.º 28
0
class AzureAD(Resource):
    """
    This class serves as an example of how one might implement an SSO provider for use with Security Monkey. In
    this example we use a OpenIDConnect authentication flow, that is essentially OAuth2 underneath.
    """
    decorators = [rbac.allow(["anonymous"], ["GET", "POST"])]

    def __init__(self):
        self.reqparse = reqparse.RequestParser()
        super(AzureAD, self).__init__()

    def get(self):
        return self.post()

    def get_idp_cert(self, id_token, jwks_url):
        header_data = fetch_token_header_payload(id_token)[0]
        # retrieve the key material as specified by the token header
        r = requests.get(jwks_url)
        for key in r.json()['keys']:
            if key['kid'] == header_data['kid']:
                secret = get_rsa_public_key(key['n'], key['e'])
                algo = header_data['alg']
                return secret, algo
        else:
            return dict(message='Key not found'), 403

    def validate_id_token(self, id_token, client_id, jwks_url):
        # validate your token based on the key it was signed with
        try:
            (secret, algo) = self.get_idp_cert(id_token, jwks_url)
            token = jwt.decode(id_token,
                               secret.decode('utf-8'),
                               algorithms=[algo],
                               audience=client_id)
            if 'upn' in token:
                return token['upn']
            elif 'email' in token:
                return token['email']
            else:
                return dict(
                    message="Unable to obtain user information from token")
        except jwt.DecodeError:
            return dict(message='Token is invalid'), 403
        except jwt.ExpiredSignatureError:
            return dict(message='Token has expired'), 403
        except jwt.InvalidTokenError:
            return dict(message='Token is invalid'), 403

    def post(self):
        if "aad" not in current_app.config.get("ACTIVE_PROVIDERS"):
            return "AzureAD is not enabled in the config.  See the ACTIVE_PROVIDERS section.", 404

        default_state = 'clientId,{client_id},redirectUri,{redirectUri},return_to,{return_to}'.format(
            client_id=current_app.config.get('AAD_CLIENT_ID'),
            redirectUri=current_app.config.get('AAD_REDIRECT_URI'),
            return_to=current_app.config.get('WEB_PATH'))
        self.reqparse.add_argument('code', type=str, required=True)
        self.reqparse.add_argument('id_token', type=str, required=True)
        self.reqparse.add_argument('state',
                                   type=str,
                                   required=False,
                                   default=default_state)

        args = self.reqparse.parse_args()
        client_id = args['state'].split(',')[1]
        redirect_uri = args['state'].split(',')[3]
        return_to = args['state'].split(',')[5]
        id_token = args['id_token']

        if not validate_redirect_url(return_to):
            return_to = current_app.config.get('WEB_PATH')

        # fetch token public key
        jwks_url = current_app.config.get('AAD_JWKS_URL')

        # Validate id_token and extract username (email)
        username = self.validate_id_token(id_token, client_id, jwks_url)

        user = setup_user(username, '',
                          current_app.config.get('AAD_DEFAULT_ROLE', 'View'))

        # Tell Flask-Principal the identity changed
        identity_changed.send(current_app._get_current_object(),
                              identity=Identity(user.id))
        login_user(user)
        db.session.commit()
        db.session.refresh(user)

        return redirect(return_to, code=302)
Ejemplo n.º 29
0
class ItemGet(AuthenticatedService):
    decorators = [rbac.allow(['View'], ["GET"])]

    def get(self, item_id):
        """
            .. http:get:: /api/1/item/1234

            Get a specific item

            **Example Request**:

            .. sourcecode:: http

                GET /api/1/item/1234 HTTP/1.1
                Host: example.com
                Accept: application/json

            **Example Response**:

            .. sourcecode:: http

                HTTP/1.1 200 OK
                Vary: Accept
                Content-Type: application/json

                {
                    "item": {
                        "account": "example_account",
                        "region": "us-east-1",
                        "technology": "elb",
                        "id": 1234,
                        "name": "example_name"
                    },
                    "revisions": [
                        {
                            "active": false,
                            "date_created": "2014-04-11 17:05:06.701936",
                            "config": {},
                            "item_id": 1234,
                            "id": 213784
                        }                    ],
                    "auth": {
                        "authenticated": true,
                        "user": "******"
                    },
                    "issues": [],
                    "comments": []
                }

            :statuscode 200: no error
            :statuscode 401: Authenticaiton Error Please login.
        """

        query = Item.query.filter(Item.id == item_id)
        result = query.first()

        # result should be an Item with a list of audit thingers and a list of
        # revisions
        retval = {}

        item_marshaled = marshal(result.__dict__, ITEM_FIELDS)
        item_marshaled = dict(item_marshaled.items() +
                              {'account': result.account.name}.items() +
                              {'technology': result.technology.name}.items())
        retval['item'] = item_marshaled
        retval['issues'] = []
        retval['auth'] = self.auth_dict

        comments_marshaled = []
        for comment in result.comments:
            comment_marshaled = marshal(comment, ITEM_COMMENT_FIELDS)
            comment_marshaled = dict(comment_marshaled.items() +
                                     {'user': comment.user.email}.items())
            comments_marshaled.append(comment_marshaled)
        retval['comments'] = comments_marshaled

        for issue in result.issues:
            if issue.auditor_setting.disabled:
                continue
            issue_marshaled = marshal(issue.__dict__, AUDIT_FIELDS)
            if issue.user is not None:
                issue_marshaled = dict(
                    issue_marshaled.items() +
                    {'justified_user': issue.user.email}.items())

            links = []
            for link in issue.sub_items:
                item_link_marshaled = marshal(link.__dict__, ITEM_LINK_FIELDS)
                links.append(item_link_marshaled)

            issue_marshaled['item_links'] = links

            retval['issues'].append(issue_marshaled)

        retval['revisions'] = []
        for revision in result.revisions.all():
            revision_marshaled = marshal(revision.__dict__, REVISION_FIELDS)
            retval['revisions'].append(revision_marshaled)

        return retval, 200
Ejemplo n.º 30
0
class OneLogin(Resource):
    decorators = [rbac.allow(["anonymous"], ["GET", "POST"])]

    def __init__(self):
        self.reqparse = reqparse.RequestParser()
        self.req = OneLogin.prepare_from_flask_request(request)
        super(OneLogin, self).__init__()

    @staticmethod
    def prepare_from_flask_request(req):
        url_data = urlparse(req.url)
        return {
            'http_host': req.host,
            'server_port': url_data.port,
            'script_name': req.path,
            'get_data': req.args.copy(),
            'post_data': req.form.copy(),
            'https':
            ("on" if current_app.config.get("ONELOGIN_HTTPS") else "off")
        }

    def get(self):
        return self.post()

    def _consumer(self, auth):
        auth.process_response()
        errors = auth.get_errors()
        if not errors:
            if auth.is_authenticated:
                return True
            else:
                return False
        else:
            current_app.logger.error('Error processing %s' %
                                     (', '.join(errors)))
            return False

    def post(self):
        if "onelogin" not in current_app.config.get("ACTIVE_PROVIDERS"):
            return "Onelogin is not enabled in the config.  See the ACTIVE_PROVIDERS section.", 404
        auth = OneLogin_Saml2_Auth(self.req,
                                   current_app.config.get("ONELOGIN_SETTINGS"))

        self.reqparse.add_argument('return_to',
                                   required=False,
                                   default=current_app.config.get('WEB_PATH'))
        self.reqparse.add_argument('acs', required=False)
        self.reqparse.add_argument('sls', required=False)

        args = self.reqparse.parse_args()

        return_to = args['return_to']

        if args['acs'] != None:
            # valids the SAML response and checks if successfully authenticated
            if self._consumer(auth):
                email = auth.get_attribute(
                    current_app.config.get("ONELOGIN_EMAIL_FIELD"))[0]
                user = User.query.filter(User.email == email).first()

                # if we get an sso user create them an account
                if not user:
                    user = User(
                        email=email,
                        active=True,
                        role=current_app.config.get('ONELOGIN_DEFAULT_ROLE')
                        # profile_picture=profile.get('thumbnailPhotoUrl')
                    )
                    db.session.add(user)
                    db.session.commit()
                    db.session.refresh(user)

                # Tell Flask-Principal the identity changed
                identity_changed.send(current_app._get_current_object(),
                                      identity=Identity(user.id))
                login_user(user)
                db.session.commit()
                db.session.refresh(user)

                self_url = OneLogin_Saml2_Utils.get_self_url(self.req)
                if 'RelayState' in request.form and self_url != request.form[
                        'RelayState']:
                    return redirect(auth.redirect_to(
                        request.form['RelayState']),
                                    code=302)
                else:
                    return redirect(current_app.config.get('BASE_URL'),
                                    code=302)
            else:
                return dict(message='OneLogin authentication failed.'), 403
        elif args['sls'] != None:
            return dict(message='OneLogin SLS not implemented yet.'), 405
        else:
            return redirect(auth.login(return_to=return_to))