Esempio n. 1
0
        def base(*args, **kwargs):
            if 'user' in kwargs:
                if kwargs['user'].get('authenticated', False):
                    return func(*args, **kwargs)
                else:
                    abort(403, "Invalid pre-authenticated user")
                    return

            self.test_readonly("API")
            logged_in_uname = self.get_logged_in_user()
            impersonator = None

            # Impersonate
            authorization = request.environ.get("HTTP_AUTHORIZATION", None)
            if authorization:
                # noinspection PyBroadException
                try:
                    bearer_token = authorization.split(" ")[-1]
                    headers = jwt.get_unverified_header(bearer_token)
                    decoded = jwt.decode(
                        bearer_token,
                        hashlib.sha256(f"{SECRET_KEY}_{headers['token_id']}".
                                       encode()).hexdigest(),
                        algorithms=[headers.get('alg', "HS256")])
                except Exception:
                    abort(400, "Malformed bearer token")
                    return

                target_user = STORAGE.user.get(headers['user'], as_obj=False)
                if target_user:
                    target_token = target_user.get('apps', {}).get(
                        headers['token_id'], {})
                    if target_token == decoded and target_token[
                            'client_id'] == logged_in_uname:
                        impersonator = logged_in_uname
                        logged_in_uname = headers['user']
                        LOGGER.info(
                            f"{impersonator} is impersonating {logged_in_uname} for query: {request.path}"
                        )

                        if not set(self.required_priv).intersection(
                                set(SCOPES[decoded["scope"]])):
                            abort(
                                403,
                                "The method you've used to login does not give you access to this API"
                            )
                            return
                    else:
                        abort(403, "Invalid bearer token")
                        return
                else:
                    abort(404, "User not found")
                    return

            user = login(logged_in_uname)

            # Terms of Service
            if request.path not in ["/api/v4/help/tos/", "/api/v4/user/whoami/",
                                    f"/api/v4/user/tos/{logged_in_uname}/",
                                    "/api/v4/auth/logout/"] \
                    and not user.get('agrees_with_tos', False) and config.ui.tos is not None:
                abort(
                    403,
                    "Agree to Terms of Service before you can make any API calls"
                )
                return

            self.test_require_type(user, "API")

            #############################################
            # Special username api query validation
            #
            #    If an API call requests a username, the username as to match
            #    the logged in user or the user has to be ADMIN
            #
            #    API that needs this special validation need to make sure their
            #    variable name for the username is as an optional parameter
            #    inside 'username_key'. Default: 'username'
            if self.username_key in kwargs:
                if kwargs[self.username_key] != user['uname'] \
                        and not kwargs[self.username_key] == "__global__" \
                        and not kwargs[self.username_key] == "__workflow__" \
                        and not kwargs[self.username_key].lower() == "__current__" \
                        and 'admin' not in user['type']:
                    return make_api_response(
                        {}, "Your username does not match requested username",
                        403)

            self.audit_if_required(args,
                                   kwargs,
                                   logged_in_uname,
                                   user,
                                   func,
                                   impersonator=impersonator)

            # Save user credential in user kwarg for future reference
            kwargs['user'] = user

            if config.core.metrics.apm_server.server_url is not None:
                elasticapm.set_user_context(username=user.get('name', None),
                                            email=user.get('email', None),
                                            user_id=user.get('uname', None))

            # Check current user quota
            quota_user = user['uname']

            flsk_session['quota_user'] = quota_user
            flsk_session['quota_set'] = True

            quota = user.get('api_quota', 10)
            if not QUOTA_TRACKER.begin(quota_user, quota):
                if config.ui.enforce_quota:
                    LOGGER.info(
                        f"User {quota_user} was prevented from using the api due to exceeded quota."
                    )
                    return make_api_response(
                        "", f"You've exceeded your maximum quota of {quota}",
                        503)
                else:
                    LOGGER.debug(
                        f"Quota of {quota} exceeded for user {quota_user}.")
            else:
                LOGGER.debug(
                    f"{quota_user}'s quota is under or equal its limit of {quota}"
                )

            return func(*args, **kwargs)
Esempio n. 2
0
        def base(*args, **kwargs):
            if 'user' in kwargs:
                if kwargs['user'].get('authenticated', False):
                    return func(*args, **kwargs)
                else:
                    abort(403, "Invalid pre-authenticated user")
                    return

            self.test_readonly("API")
            logged_in_uname = self.get_logged_in_user()

            user = login(logged_in_uname)

            # Terms of Service
            if request.path not in ["/api/v4/help/tos/", "/api/v4/user/whoami/",
                                    f"/api/v4/user/tos/{logged_in_uname}/",
                                    "/api/v4/auth/logout/"] \
                    and not user.get('agrees_with_tos', False) and config.ui.tos is not None:
                abort(
                    403,
                    "Agree to Terms of Service before you can make any API calls"
                )
                return

            self.test_require_type(user, "API")

            #############################################
            # Special username api query validation
            #
            #    If an API call requests a username, the username as to match
            #    the logged in user or the user has to be ADMIN
            #
            #    API that needs this special validation need to make sure their
            #    variable name for the username is as an optional parameter
            #    inside 'username_key'. Default: 'username'
            if self.username_key in kwargs:
                if kwargs[self.username_key] != user['uname'] \
                        and not kwargs[self.username_key] == "__global__" \
                        and not kwargs[self.username_key] == "__workflow__" \
                        and not kwargs[self.username_key].lower() == "__current__" \
                        and 'admin' not in user['type']:
                    return make_api_response(
                        {}, "Your username does not match requested username",
                        403)

            self.audit_if_required(args, kwargs, logged_in_uname, user, func)

            # Save user credential in user kwarg for future reference
            kwargs['user'] = user

            if config.core.metrics.apm_server.server_url is not None:
                elasticapm.set_user_context(username=user.get('name', None),
                                            email=user.get('email', None),
                                            user_id=user.get('uname', None))

            # Check current user quota
            quota_user = user['uname']

            flsk_session['quota_user'] = quota_user
            flsk_session['quota_set'] = True

            quota = user.get('api_quota', 10)
            if not QUOTA_TRACKER.begin(quota_user, quota):
                if config.ui.enforce_quota:
                    LOGGER.info(
                        f"User {quota_user} was prevented from using the api due to exceeded quota."
                    )
                    return make_api_response(
                        "", f"You've exceeded your maximum quota of {quota}",
                        503)
                else:
                    LOGGER.debug(
                        f"Quota of {quota} exceeded for user {quota_user}.")
            else:
                LOGGER.debug(
                    f"{quota_user}'s quota is under or equal its limit of {quota}"
                )

            return func(*args, **kwargs)