コード例 #1
0
    async def get(self, account_id, role_name):
        if config.get("policy_editor.disallow_contractors",
                      True) and self.contractor:
            if self.user not in config.get(
                    "groups.can_bypass_contractor_restrictions", []):
                raise MustBeFte("Only FTEs are authorized to view this page.")

        log_data = {
            "function": "ManagedPoliciesOnRoleHandler.get",
            "user": self.user,
            "ip": self.ip,
            "message": "Retrieving managed policies for role",
            "user-agent": self.request.headers.get("User-Agent"),
            "request_id": self.request_uuid,
            "account_id": account_id,
            "role_name": role_name,
        }

        log.debug(log_data)

        managed_policy_details = await sync_to_async(
            get_role_managed_policy_documents)(
                {
                    "RoleName": role_name
                },
                account_number=account_id,
                assume_role=config.get("policies.role_name"),
                region=config.region,
            )
        res = WebResponse(
            status="success",
            status_code=200,
            data=managed_policy_details,
        )
        self.write(res.json())
コード例 #2
0
ファイル: web.py プロジェクト: jaydhulia/consoleme
async def handle_generic_error_response(
    request,
    message: str,
    errors: List[str],
    status_code: int,
    reason: str,
    log_data: Dict[str, Any],
) -> bool:
    """

    Args:
        request: Tornado web request
        message: Message to be logged
        reason: One line reason for the response (easier for frontend to parse)
        errors: List of errors to be logged, and to be returned to user
        status_code: Status code to return to end-user
        log_data: Dictionary of data to log, typically containing function and information about the user.

    Returns:
        boolean

    """
    log.error({**log_data, "message": message, "errors": errors})
    res = WebResponse(status="error",
                      status_code=status_code,
                      errors=errors,
                      reason=reason)
    request.set_status(status_code)
    request.write(res.json(exclude_unset=True))
    await request.finish()
    return True
コード例 #3
0
ファイル: logout.py プロジェクト: ywyt738/consoleme
    async def get(self):
        log_data = {
            "function":
            f"{__name__}.{self.__class__.__name__}.{sys._getframe().f_code.co_name}",
            "message": "Attempting to log out user",
            "user-agent": self.request.headers.get("User-Agent"),
            "ip": self.ip,
        }
        if not config.get("auth.set_auth_cookie"):
            await handle_generic_error_response(
                self,
                "Unable to log out",
                [("Configuration value `auth.set_auth_cookie` is not enabled. "
                  "ConsoleMe isn't able to delete an auth cookie if setting auth "
                  "cookies is not enabled.")],
                400,
                "logout_failure",
                log_data,
            )
            return
        cookie_name: str = config.get("auth_cookie_name", "consoleme_auth")
        if not cookie_name:
            await handle_generic_error_response(
                self,
                "Unable to log out",
                [("Configuration value `auth_cookie_name` is not set. "
                  "ConsoleMe isn't able to delete an auth cookie if the auth cookie name "
                  "is not known.")],
                400,
                "logout_failure",
                log_data,
            )
            return
        self.clear_cookie(cookie_name)

        extra_auth_cookies: list = config.get("auth.extra_auth_cookies", [])
        for cookie in extra_auth_cookies:
            self.clear_cookie(cookie)

        redirect_url: str = config.get("auth.logout_redirect_url", "/")
        res = WebResponse(
            status="redirect",
            redirect_url=redirect_url,
            status_code=200,
            reason="logout_redirect",
            message=
            "User has successfully logged out. Redirecting to landing page",
        )
        log.debug({**log_data, "message": "Successfully logged out user."})
        self.write(res.json())
コード例 #4
0
    async def get(self):
        """
        GET /api/v2/audit/roles
        """
        log_data = {
            "function": f"{__name__}.{self.__class__.__name__}.{sys._getframe().f_code.co_name}",
            "user-agent": self.request.headers.get("User-Agent"),
            "request_id": self.request_uuid,
        }

        page = self.get_argument("page", "0")
        try:
            page = int(page)
        except ValueError:
            log_data["message"] = f"invalid value for page: {page}"
            log.warning(log_data)
            page = 0

        count = self.get_argument("count", "1000")
        try:
            count = int(count)
        except ValueError:
            log_data["message"] = f"invalid value for count: {count}"
            log.warning(log_data)
            count = 1000

        if page < 0:
            page = 0
        if count <= 0:
            count = 1000

        app_name = self.requester.get("name") or self.requester.get("username")
        stats.count(
            "AuditRoleHandler.get",
            tags={
                "requester": app_name,
            },
        )

        roles = await credential_mapping.all_roles(
            paginate=True, page=page, count=count
        )
        total_roles = await credential_mapping.number_roles()
        start = page * count
        end = start + count
        if end >= total_roles:
            end = total_roles - 1
        roles = roles[start:end]

        self.write(
            WebResponse(
                status=Status2.success,
                status_code=200,
                data=roles,
                page=page,
                total=total_roles,
                count=len(roles),
                last_page=_get_last_page(total_roles, count),
            ).json(exclude_unset=True)
        )
コード例 #5
0
    async def get(self, account_id, role_name):
        """
        GET /api/v2/audit/roles/{accountNumber}/{roleName}/access
        """
        log_data = {
            "function": f"{__name__}.{self.__class__.__name__}.{sys._getframe().f_code.co_name}",
            "user-agent": self.request.headers.get("User-Agent"),
            "request_id": self.request_uuid,
            "account_id": account_id,
            "role_name": role_name,
        }
        app_name = self.requester.get("name") or self.requester.get("username")
        stats.count(
            "RoleAccessHandler.get",
            tags={
                "requester": app_name,
                "account_id": account_id,
                "role_name": role_name,
            },
        )

        groups = await credential_mapping.determine_role_authorized_groups(
            account_id, role_name
        )
        if not groups:
            log_data[
                "message"
            ] = f"No authorized groups found for {role_name} in {account_id}"
            log.warning(log_data)
            self.set_status(404)
            self.write(
                WebResponse(
                    status=Status2.error,
                    status_code=404,
                    message="No groups found for requested role",
                ).json(exclude_unset=True)
            )
            return

        self.write(
            WebResponse(status=Status2.success, status_code=200, data=groups).json(
                exclude_unset=True
            )
        )
コード例 #6
0
    async def get(self, identifier):
        if config.get("policy_editor.disallow_contractors",
                      True) and self.contractor:
            if self.user not in config.get(
                    "groups.can_bypass_contractor_restrictions", []):
                raise MustBeFte("Only FTEs are authorized to view this page.")

        log_data = {
            "function": "ServiceControlPolicyHandler.get",
            "user": self.user,
            "message": "Retrieving service control policies for identifier",
            "identifier": identifier,
            "user-agent": self.request.headers.get("User-Agent"),
            "request_id": self.request_uuid,
        }

        log.debug(log_data)
        try:
            scps = await get_scps_for_account_or_ou(identifier)
        except Exception as e:
            sentry_sdk.capture_exception()
            response = WebResponse(status=Status2.error,
                                   status_code=403,
                                   errors=[str(e)],
                                   data=[])
            self.write(response.json())
            return
        response = WebResponse(status=Status2.success,
                               status_code=200,
                               data=scps.__root__)
        self.write(response.json())
コード例 #7
0
 async def get(self):
     try:
         notification_response: GetNotificationsForUserResponse = (
             await get_notifications_for_user(self.user, self.groups))
         notifications: List[
             ConsoleMeUserNotification] = notification_response.notifications
         response = WebResponse(
             status="success",
             status_code=200,
             data={
                 "unreadNotificationCount":
                 notification_response.unread_count,
                 "notifications": notifications,
             },
         )
         self.write(response.json())
     except Exception as e:
         sentry_sdk.capture_exception()
         self.set_status(500)
         response = WebResponse(status=Status2.error,
                                status_code=500,
                                errors=[str(e)],
                                data=[])
         self.write(response.json())
         return
コード例 #8
0
    async def get(self, policy_arn: str):
        if config.get("policy_editor.disallow_contractors",
                      True) and self.contractor:
            if self.user not in config.get(
                    "groups.can_bypass_contractor_restrictions", []):
                raise MustBeFte("Only FTEs are authorized to view this page.")

        account_id = policy_arn.split(":")[4]
        policy_name = policy_arn.split("/")[-1]
        log_data = {
            "function": "ManagedPoliciesHandler.get",
            "user": self.user,
            "ip": self.ip,
            "message": "Retrieving managed policy",
            "user-agent": self.request.headers.get("User-Agent"),
            "request_id": self.request_uuid,
            "account_id": account_id,
            "policy_name": policy_name,
            "policy_arn": policy_arn,
        }

        log.debug(log_data)

        managed_policy_details = await sync_to_async(
            get_managed_policy_document)(
                policy_arn=policy_arn,
                account_number=account_id,
                assume_role=config.get("policies.role_name"),
                region=config.region,
                retry_max_attempts=2,
                client_kwargs=config.get("boto3.client_kwargs", {}),
            )
        res = WebResponse(
            status=Status2.success,
            status_code=200,
            data=managed_policy_details,
        )
        self.write(res.json())
コード例 #9
0
    async def get(self):
        """
        GET /api/v2/audit/roles
        """
        app_name = self.requester.get("name") or self.requester.get("username")
        stats.count(
            "AuditRoleHandler.get",
            tags={
                "requester": app_name,
            },
        )

        roles = await credential_mapping.all_roles()

        self.write(
            WebResponse(status=Status2.success, status_code=200,
                        data=roles).json(exclude_unset=True))
コード例 #10
0
ファイル: user.py プロジェクト: ssahgal/consoleme
    async def post(self):
        # TODO: Send verification e-mail to proposed user

        log_data = {
            "function":
            f"{__name__}.{self.__class__.__name__}.{sys._getframe().f_code.co_name}",
            "message": "Attempting to register user",
            "user-agent": self.request.headers.get("User-Agent"),
        }

        generic_error_message: str = "User registration failed"
        # Fail if getting users by password is not enabled
        if not config.get("auth.get_user_by_password"):
            errors = [
                "Expected configuration `auth.get_user_by_password`, but it is not enabled."
            ]
            await handle_generic_error_response(self, generic_error_message,
                                                errors, 403, "not_configured",
                                                log_data)
            return
        # Fail if user registration not allowed
        if not config.get("auth.allow_user_registration"):
            errors = [
                "Expected configuration `auth.allow_user_registration`, but it is not enabled."
            ]
            await handle_generic_error_response(self, generic_error_message,
                                                errors, 403, "not_configured",
                                                log_data)
            return

        registration_attempt = RegistrationAttemptModel.parse_raw(
            self.request.body)
        log_data["username"] = registration_attempt.username
        # Fail if username not valid email address
        try:
            if not validate_email(registration_attempt.username):
                errors = ["Username must be a valid e-mail address."]
                await handle_generic_error_response(
                    self,
                    generic_error_message,
                    errors,
                    403,
                    "invalid_request",
                    log_data,
                )
                return
        except Exception as e:
            sentry_sdk.capture_exception()
            await handle_generic_error_response(self, generic_error_message,
                                                [str(e)], 403,
                                                "invalid_request", log_data)
            return
        # Fail if user already exists
        if await self.ddb.get_user(registration_attempt.username):
            errors = ["User already exists"]
            await handle_generic_error_response(self, generic_error_message,
                                                errors, 403, "invalid_request",
                                                log_data)
            return

        # Fails if password is not strong enough.
        password_strength_errors = await check_password_strength(
            registration_attempt.password)
        if password_strength_errors:
            await handle_generic_error_response(
                self,
                password_strength_errors["message"],
                password_strength_errors["errors"],
                403,
                "weak_password",
                log_data,
            )
            return

        self.ddb.create_user(registration_attempt.username,
                             registration_attempt.password)

        res = WebResponse(
            status="success",
            status_code=200,
            message=
            f"Successfully created user {registration_attempt.username}.",
        )
        self.write(res.json())
コード例 #11
0
ファイル: user.py プロジェクト: ssahgal/consoleme
    async def post(self):
        log_data = {
            "function":
            f"{__name__}.{self.__class__.__name__}.{sys._getframe().f_code.co_name}",
            "user": self.user,
            "message": "Create/Update User",
            "user-agent": self.request.headers.get("User-Agent"),
            "request_id": self.request_uuid,
            "ip": self.ip,
        }
        generic_error_message = "Unable to create/update user"
        log.debug(log_data)
        # Checks authz levels of current user
        if not can_admin_all(self.user, self.groups):
            errors = ["User is not authorized to access this endpoint."]
            await handle_generic_error_response(self, generic_error_message,
                                                errors, 403, "unauthorized",
                                                log_data)
            return
        request = UserManagementModel.parse_raw(self.request.body)
        log_data["requested_user"] = request.username
        if request.user_management_action.value == "create":
            log.debug({
                **log_data,
                "message": "Creating user",
                "requested_user": request.username,
                "requested_groups": request.groups,
            })

            # Fails if password is not strong enough.
            password_strength_errors = await check_password_strength(
                request.password)
            if password_strength_errors:
                await handle_generic_error_response(
                    self,
                    password_strength_errors["message"],
                    password_strength_errors["errors"],
                    403,
                    "weak_password",
                    log_data,
                )
                return

            self.ddb.create_user(
                request.username,
                request.password,
                request.groups,
            )
            res = WebResponse(
                status="success",
                status_code=200,
                message=f"Successfully created user {request.username}.",
            )
            self.write(res.json())
            return
        elif request.user_management_action.value == "update":
            log.debug({
                **log_data,
                "message": "Updating user",
                "requested_user": request.username,
                "requested_groups": request.groups,
            })

            if request.password:
                # Fails if password is not strong enough.
                password_strength_errors = await check_password_strength(
                    request.password)
                if password_strength_errors:
                    await handle_generic_error_response(
                        self,
                        password_strength_errors["message"],
                        password_strength_errors["errors"],
                        403,
                        "weak_password",
                        log_data,
                    )
                    return
            self.ddb.update_user(
                request.username,
                request.password,
                request.groups,
            )
            res = WebResponse(
                status="success",
                status_code=200,
                message=f"Successfully updated user {request.username}.",
            )
            self.write(res.json())
            return
        elif request.user_management_action.value == "delete":
            log.debug({
                **log_data,
                "message": "Deleting user",
                "requested_user": request.username,
            })
            self.ddb.delete_user(request.username, )
            res = WebResponse(
                status="success",
                status_code=200,
                message=f"Successfully deleted user {request.username}.",
            )
            self.write(res.json())
            return
        else:
            errors = ["Change type is not supported by this endpoint."]
            await handle_generic_error_response(self, generic_error_message,
                                                errors, 403, "invalid_request",
                                                log_data)
            return
コード例 #12
0
ファイル: user.py プロジェクト: ssahgal/consoleme
    async def post(self):
        log_data = {
            "function":
            f"{__name__}.{self.__class__.__name__}.{sys._getframe().f_code.co_name}",
            "message": "Attempting to authenticate User",
            "user-agent": self.request.headers.get("User-Agent"),
        }
        generic_error_message = "Authentication failed"
        if not config.get("auth.get_user_by_password"):
            errors = [
                "Expected configuration `auth.get_user_by_password`, but it is not enabled."
            ]
            await handle_generic_error_response(self, generic_error_message,
                                                errors, 403, "not_configured",
                                                log_data)
            return
        # Auth cookie must be set to use password authentication.
        if not config.get("auth.set_auth_cookie"):
            errors = [
                "Expected configuration `auth.set_auth_cookie`, but it is not enabled."
            ]
            await handle_generic_error_response(self, generic_error_message,
                                                errors, 403, "not_configured",
                                                log_data)
            return

        login_attempt = LoginAttemptModel.parse_raw(self.request.body)
        log_data["username"] = login_attempt.username
        log_data["after_redirect_uri"] = login_attempt.after_redirect_uri
        authenticated_response: AuthenticationResponse = (
            await self.ddb.authenticate_user(login_attempt))
        if not authenticated_response.authenticated:
            # Wait 1 second to protect from single-host brute-force
            await asyncio.sleep(1)
            await handle_generic_error_response(
                self,
                generic_error_message,
                authenticated_response.errors,
                403,
                "authentication_failure",
                log_data,
            )
            return
        # Make and set jwt for user
        expiration = datetime.utcnow().replace(tzinfo=pytz.UTC) + timedelta(
            minutes=config.get("jwt.expiration_minutes", 60))
        encoded_cookie = await generate_jwt_token(
            authenticated_response.username,
            authenticated_response.groups,
            exp=expiration,
        )
        self.set_cookie(
            config.get("auth_cookie_name", "consoleme_auth"),
            encoded_cookie,
            expires=expiration,
            secure=config.get(
                "auth.cookie.secure",
                True if "https://" in config.get("url") else False,
            ),
            httponly=config.get("auth.cookie.httponly", True),
            samesite=config.get("auth.cookie.samesite", True),
        )
        res = WebResponse(
            status="redirect",
            redirect_url=login_attempt.after_redirect_uri,
            status_code=200,
            reason="authenticated_redirect",
            message=
            "User has successfully authenticated. Redirecting to their intended destination.",
        )
        self.write(res.json())
コード例 #13
0
    async def put(self):
        """
        Allows an "authorized user" (Any user the notification is intended for) to mark the notification as read/unread
        or hidden/unhidden for themselves or all other notification recipients

        :return:
        """
        change = ConsoleMeNotificationUpdateRequest.parse_raw(
            self.request.body)
        errors = []

        for untrusted_notification in change.notifications:
            notification = await fetch_notification(
                untrusted_notification.predictable_id)
            if not notification:
                errors.append("Unable to find matching notification")
                continue
            authorized = is_in_group(self.user, self.groups,
                                     notification.users_or_groups)
            if not authorized:
                errors.append(
                    f"Unauthorized because user is not associated with notification: {notification.predictable_id}"
                )
                continue
            if (change.action == ConsoleMeNotificationUpdateAction.
                    toggle_read_for_current_user):
                if self.user in notification.read_by_users:
                    # Mark as unread
                    notification.read_by_users.remove(self.user)
                else:
                    # Mark as read
                    notification.read_by_users.append(self.user)
            elif (change.action ==
                  ConsoleMeNotificationUpdateAction.toggle_read_for_all_users):
                # Mark or unmark notification as `read_by_all`.  If unmarked,
                # ConsoleMe will fall back to `notification.read_by_user` to determine if
                # a given user has read the notification
                notification.read_by_all = not notification.read_by_all
            elif (change.action == ConsoleMeNotificationUpdateAction.
                  toggle_hidden_for_current_user):
                if self.user in notification.hidden_for_users:
                    # Unmark as hidden
                    notification.hidden_for_users.remove(self.user)
                else:
                    # Mark as hidden
                    notification.hidden_for_users.append(self.user)
            elif (change.action == ConsoleMeNotificationUpdateAction.
                  toggle_hidden_for_all_users):
                # Mark or unmark as "Hidden for all users". If unmarked, falls back to `hidden_for_users.read_by_user`
                # to determine whether to show the notification to a given user
                notification.hidden_for_all = not notification.hidden_for_all
            else:
                raise Exception("Unknown or unsupported change action.")
            await write_notification(notification)
        try:
            # Retrieve and return updated notifications for user
            notification_response: GetNotificationsForUserResponse = (
                await get_notifications_for_user(self.user,
                                                 self.groups,
                                                 force_refresh=True))
            notifications: List[
                ConsoleMeUserNotification] = notification_response.notifications
            response = WebResponse(
                status="success",
                status_code=200,
                data={
                    "unreadNotificationCount":
                    notification_response.unread_count,
                    "notifications": notifications,
                },
            )
            self.write(response.json())
        except Exception as e:
            sentry_sdk.capture_exception()
            self.set_status(500)
            response = WebResponse(status=Status2.error,
                                   status_code=500,
                                   errors=[str(e)],
                                   data=[])
            self.write(response.json())
            return
コード例 #14
0
ファイル: resources.py プロジェクト: rhnasc/consoleme
    async def get(self):
        """
        /api/v2/get_resource_url - Endpoint used to get an URL from an ARN
        ---
        get:
            description: Get the resource URL for ConsoleMe, given an ARN
            responses:
                200:
                    description: Returns a URL generated from the ARN in JSON form
                400:
                    description: Malformed Request
                403:
                    description: Forbidden
        """
        self.user: str = self.requester["email"]
        arn: str = self.get_argument("arn", None)
        log_data = {
            "function":
            f"{__name__}.{self.__class__.__name__}.{sys._getframe().f_code.co_name}",
            "user": self.user,
            "arn": arn,
            "message": "Generating URL for resource",
            "user-agent": self.request.headers.get("User-Agent"),
            "request_id": self.request_uuid,
        }
        log.debug(log_data)
        stats.count("GetResourceURL.get", tags={"user": self.user})
        if not arn:
            generic_error_message: str = "Missing required parameter"
            errors = ["arn is a required parameter"]
            await handle_generic_error_response(self, generic_error_message,
                                                errors, 404, "missing_data",
                                                log_data)
            return

        try:
            # parse_arn will raise an exception on invalid arns
            parse_arn(arn)
            resource_url = await get_url_for_resource(arn)
            if not resource_url:
                raise ValueError(
                    "This resource type is currently not supported")
        except (ResourceNotFound, ValueError) as e:
            generic_error_message: str = "Unsupported data"
            errors = [str(e)]
            await handle_generic_error_response(self, generic_error_message,
                                                errors, 404, "invalid_data",
                                                log_data)
            return
        except Exception as e:
            generic_error_message: str = "Malformed data"
            errors = [str(e)]
            await handle_generic_error_response(self, generic_error_message,
                                                errors, 404, "malformed_data",
                                                log_data)
            return

        res = WebResponse(
            status="success",
            status_code=200,
            message="Successfully generated URL for ARN",
            data={"url": resource_url},
        )

        self.write(res.json())
        await self.finish()
コード例 #15
0
ファイル: resources.py プロジェクト: jaydhulia/consoleme
    async def get(self):
        """
        /api/v2/get_resource_url - Endpoint used to get an URL from an ARN
        ---
        get:
            description: Get the resource URL for ConsoleMe, given an ARN
            responses:
                200:
                    description: Returns a URL generated from the ARN in JSON form
                400:
                    description: Malformed Request
                403:
                    description: Forbidden
        """
        self.user: str = self.requester["email"]
        arn: str = self.get_argument("arn", None)
        log_data = {
            "function":
            f"{__name__}.{self.__class__.__name__}.{sys._getframe().f_code.co_name}",
            "user": self.user,
            "arn": arn,
            "message": "Generating URL for resource",
            "user-agent": self.request.headers.get("User-Agent"),
            "request_id": self.request_uuid,
        }
        log.debug(log_data)
        stats.count("GetResourceURL.get", tags={"user": self.user})
        if not arn:
            generic_error_message: str = "Missing required parameter"
            errors = ["arn is a required parameter"]
            await handle_generic_error_response(self, generic_error_message,
                                                errors, 404, "missing_data",
                                                log_data)
            return

        try:
            # parse_arn will raise an exception on invalid arns
            parse_arn(arn)

            resources_from_aws_config_redis_key = config.get(
                "aws_config_cache.redis_key", "AWSCONFIG_RESOURCE_CACHE")
            if not red.exists(resources_from_aws_config_redis_key):
                # This will force a refresh of our redis cache if the data exists in S3
                await retrieve_json_data_from_redis_or_s3(
                    redis_key=resources_from_aws_config_redis_key,
                    s3_bucket=config.get(
                        "aws_config_cache_combined.s3.bucket"),
                    s3_key=config.get(
                        "aws_config_cache_combined.s3.file",
                        "aws_config_cache_combined/aws_config_resource_cache_combined_v1.json.gz",
                    ),
                    redis_data_type="hash",
                )
            resource_info = await redis_hget(
                resources_from_aws_config_redis_key, arn)
            if not resource_info:
                raise ValueError("Resource not found in organization cache")
            resource_url = await get_url_for_resource(arn)
            if not resource_url:
                raise ValueError(
                    "This resource type is currently not supported")
        except (ResourceNotFound, ValueError) as e:
            generic_error_message: str = "Unsupported data"
            errors = [str(e)]
            await handle_generic_error_response(self, generic_error_message,
                                                errors, 404, "invalid_data",
                                                log_data)
            return
        except Exception as e:
            generic_error_message: str = "Malformed data"
            errors = [str(e)]
            await handle_generic_error_response(self, generic_error_message,
                                                errors, 404, "malformed_data",
                                                log_data)
            return

        res = WebResponse(
            status="success",
            status_code=200,
            message="Successfully generated URL for ARN",
            data={"url": resource_url},
        )

        self.write(res.json())
        await self.finish()
コード例 #16
0
ファイル: roles.py プロジェクト: castrapel/consoleme
    async def get(self):
        """
        GET /api/v2/get_roles - Endpoint used to get details of eligible roles. Used by weep and newt.
        ---
        get:
            description: Returns a json-encoded list of objects of eligible roles for the user.
            response format: WebResponse. The "data" field within WebResponse is of format EligibleRolesModelArray
            Example response:
                {
                    "status": "success",
                    "status_code": 200,
                    "data": {
                        "roles": [
                                    {
                                        "arn": "arn:aws:iam::123456789012:role/role_name",
                                        "account_id": "123456789012",
                                        "account_friendly_name": "prod",
                                        "role_name": "role_name",
                                        "apps": {
                                            "app_details": [
                                                {
                                                    "name": "consoleme",
                                                    "owner": "*****@*****.**",
                                                    "owner_url": null,
                                                    "app_url": "https://example.com"
                                                }
                                            ]
                                        }
                                    },
                                    ...
                                ]
                    }
                }
        """
        self.user: str = self.requester["email"]

        include_all_roles = self.get_arguments("all")
        console_only = True
        if include_all_roles == ["true"]:
            console_only = False

        log_data = {
            "function": f"{__name__}.{self.__class__.__name__}.{sys._getframe().f_code.co_name}",
            "user": self.user,
            "console_only": console_only,
            "message": "Getting all eligible user roles",
            "user-agent": self.request.headers.get("User-Agent"),
            "request_id": self.request_uuid,
        }
        log.debug(log_data)
        stats.count("GetRolesMTLSHandler.get", tags={"user": self.user})

        await self.authorization_flow(user=self.user, console_only=console_only)
        eligible_roles_details_array = await get_eligible_role_details(
            sorted(self.eligible_roles)
        )

        res = WebResponse(
            status=Status2.success,
            status_code=200,
            data=eligible_roles_details_array.dict(),
        )
        self.write(res.json(exclude_unset=True))
        await self.finish()
コード例 #17
0
    async def get(self, arn):
        if config.get("policy_editor.disallow_contractors",
                      True) and self.contractor:
            if self.user not in config.get(
                    "groups.can_bypass_contractor_restrictions", []):
                raise MustBeFte("Only FTEs are authorized to view this page.")

        errors = []
        if not arn.startswith("arn:aws:iam::"):
            errors.append("ARN must start with 'arn:aws:iam::'")

        principal_name = tornado.escape.xhtml_escape(arn.split("/")[-1])
        principal_type = tornado.escape.xhtml_escape(
            arn.split(":")[5].split("/")[0])
        account_id = tornado.escape.xhtml_escape(arn.split(":")[4])
        if principal_type not in ["role", "user"]:
            errors.append(
                f"Principal type must be role or user. not {principal_type}")

        log_data = {
            "function": "ManagedPoliciesOnRoleHandler.get",
            "user": self.user,
            "ip": self.ip,
            "message": "Retrieving managed policies for role",
            "user-agent": self.request.headers.get("User-Agent"),
            "request_id": self.request_uuid,
            "account_id": account_id,
            "principal_name": principal_name,
            "principal_type": principal_type,
        }

        log.debug(log_data)
        if errors:
            log.error({
                **log_data, "errors": errors,
                "message": "Unable to process request"
            })
            res = WebResponse(
                status=Status2.error,
                reason="bad_request",
                status_code=400,
                errors=errors,
            )
            self.write(res.json())
            return

        if principal_type == "role":
            managed_policy_details = await sync_to_async(
                get_role_managed_policy_documents)(
                    {
                        "RoleName": principal_name
                    },
                    account_number=account_id,
                    assume_role=config.get("policies.role_name"),
                    region=config.region,
                    retry_max_attempts=2,
                    client_kwargs=config.get("boto3.client_kwargs", {}),
                )
        elif principal_type == "user":
            managed_policy_details = await sync_to_async(
                get_user_managed_policy_documents)(
                    {
                        "UserName": principal_name
                    },
                    account_number=account_id,
                    assume_role=config.get("policies.role_name"),
                    region=config.region,
                    retry_max_attempts=2,
                    client_kwargs=config.get("boto3.client_kwargs", {}),
                )
        else:
            raise Exception("Invalid principal type")
        res = WebResponse(
            status=Status2.success,
            status_code=200,
            data=managed_policy_details,
        )
        self.write(res.json())