def setUp(self) -> None: from consoleme.config import config from consoleme.exceptions.exceptions import NoMatchingRequest self.NoMatchingRequest = NoMatchingRequest auth = get_plugin_by_name(config.get("plugins.auth", "default_auth"))() self.Group = auth.Group
async def should_auto_approve_policy_v2(extended_request: ExtendedRequestModel, user, user_groups): """ This uses your fancy internal logic to determine if a request should be auto-approved or not. The default plugin set included in ConsoleMe OSS will return False. """ aws = get_plugin_by_name(config.get("plugins.aws", "default_aws"))() return await aws.should_auto_approve_policy_v2(extended_request, user, user_groups)
async def generate_credential_authorization_mapping( self, authorization_mapping: Dict[user_or_group, RoleAuthorizations] ) -> Dict[user_or_group, RoleAuthorizations]: """This will list accounts that meet the account attribute search criteria.""" group_mapping = get_plugin_by_name( config.get("plugins.group_mapping", "default_group_mapping"))() # Generate mapping from internal plugin authorization_mapping = ( await group_mapping.generate_credential_authorization_mapping( authorization_mapping)) # Return mapping return authorization_mapping
from consoleme.config import config from consoleme.handlers.base import BaseAPIV1Handler from consoleme.lib.account_indexers import get_account_id_to_name_mapping from consoleme.lib.auth import ( can_admin_policies, can_create_roles, can_delete_roles, can_edit_dynamic_config, ) from consoleme.lib.generic import get_random_security_logo, is_in_group from consoleme.lib.plugins import get_plugin_by_name stats = get_plugin_by_name(config.get("plugins.metrics", "default_metrics"))() log = config.get_logger() class UserProfileHandler(BaseAPIV1Handler): async def get(self): """ Provide information about site configuration for the frontend :return: """ is_contractor = config.config_plugin().is_contractor(self.user) site_config = { "consoleme_logo": await get_random_security_logo(), "google_tracking_uri": config.get("google_analytics.tracking_url"), "documentation_url": config.get("documentation_page"), "support_contact": config.get("support_contact"), "support_chat_url": config.get("support_chat_url"), "security_logo": config.get("security_logo.image"), "security_url": config.get("security_logo.url"),
from consoleme.handlers.v2.templated_resources import TemplatedResourceDetailHandler from consoleme.handlers.v2.typeahead import ( ResourceTypeAheadHandlerV2, SelfServiceStep1ResourceTypeahead, ) from consoleme.handlers.v2.user import ( LoginConfigurationHandler, LoginHandler, UserManagementHandler, UserRegistrationHandler, ) from consoleme.handlers.v2.user_profile import UserProfileHandler from consoleme.lib.auth import mk_jwks_validator from consoleme.lib.plugins import get_plugin_by_name internal_routes = get_plugin_by_name( config.get("plugins.internal_routes", "default_internal_routes"))() log = config.get_logger() def make_jwt_validator(): jwk_url = config.get("sso.jwk_url") if not jwk_url: raise Exception("Config 'sso.jwk_url' is not defined") jwk_set = requests.get(jwk_url).json() keys = [k for k in jwk_set["keys"] if k["kty"] == "RSA"] jwk_schema = config.get("sso.jwk_schema") if not jwk_schema: raise Exception("Config 'sso.jwk_schema' is not defined") return mk_jwks_validator(keys, jwk_schema["header"], jwk_schema["payload"])
import asyncio import time from typing import Any from asgiref.sync import sync_to_async from consoleme.config import config from consoleme.exceptions.exceptions import NoMatchingRequest from consoleme.lib.auth import can_admin_all from consoleme.lib.cache import store_json_results_in_redis_and_s3 from consoleme.lib.dynamo import UserDynamoHandler from consoleme.lib.plugins import get_plugin_by_name auth = get_plugin_by_name(config.get("plugins.auth", "default_auth"))() async def can_approve_reject_request(user, secondary_approvers, groups): # Allow admins to approve and reject all requests if can_admin_all(user, groups): return True if secondary_approvers: for g in secondary_approvers: if g in groups or g == user: return True return False async def can_cancel_request(current_user, requesting_user, groups): # Allow the requesting user to cancel their own request if current_user == requesting_user:
import sys from datetime import datetime, timedelta import ujson as json from consoleme.config import config from consoleme.exceptions.exceptions import MustBeFte from consoleme.handlers.base import BaseHandler from consoleme.lib.account_indexers import get_account_id_to_name_mapping from consoleme.lib.auth import can_admin_policies from consoleme.lib.aws import fetch_resource_details from consoleme.lib.plugins import get_plugin_by_name log = config.get_logger() stats = get_plugin_by_name(config.get("plugins.metrics", "default_metrics"))() aws = get_plugin_by_name(config.get("plugins.aws", "default_aws"))() group_mapping = get_plugin_by_name( config.get("plugins.group_mapping", "default_group_mapping") )() auth = get_plugin_by_name(config.get("plugins.auth", "default_auth"))() internal_policies = get_plugin_by_name( config.get("plugins.internal_policies", "default_policies") )() class ResourceDetailHandler(BaseHandler): async def get(self, account_id, resource_type, region=None, resource_name=None): if not self.user: return if config.get("policy_editor.disallow_contractors", True) and self.contractor: if self.user not in config.get(
) from consoleme.lib.account_indexers import get_account_id_to_name_mapping from consoleme.lib.generic import generate_random_string, iterate_and_format_dict from consoleme.lib.plugins import get_plugin_by_name from consoleme.models import ( ChangeGeneratorModel, ChangeGeneratorModelArray, ChangeModelArray, CrudChangeGeneratorModel, InlinePolicyChangeModel, PolicyModel, ResourceModel, Status, ) group_mapping = get_plugin_by_name( config.get("plugins.group_mapping", "default_group_mapping"))() ALL_ACCOUNTS = None async def _generate_policy_statement(actions: List, resources: List, effect: str, condition: Dict) -> Dict: """ Generates an IAM policy resource given actions, effects, resources, and conditions :param actions: a List of actions :param resources: a List of AWS resource ARNs or wildcards :param effect: an Effect (Allow|Deny) :return: """ policy_statement = { "Action": list(set(actions)),
NoGroupsException, NoUserException, SilentException, WebAuthNError, ) from consoleme.lib.alb_auth import authenticate_user_by_alb_auth from consoleme.lib.auth import AuthenticationError from consoleme.lib.jwt import generate_jwt_token, validate_and_return_jwt_token from consoleme.lib.oidc import authenticate_user_by_oidc from consoleme.lib.plugins import get_plugin_by_name from consoleme.lib.redis import RedisHandler from consoleme.lib.saml import authenticate_user_by_saml from consoleme.lib.tracing import ConsoleMeTracer log = config.get_logger() stats = get_plugin_by_name(config.get("plugins.metrics", "default_metrics"))() auth = get_plugin_by_name(config.get("plugins.auth", "default_auth"))() group_mapping = get_plugin_by_name( config.get("plugins.group_mapping", "default_group_mapping"))() class BaseJSONHandler(tornado.web.RequestHandler): # These methods are returned in OPTIONS requests. # Default methods can be overridden by setting this variable in child classes. allowed_methods = ["GET", "HEAD", "PUT", "PATCH", "POST", "DELETE"] def __init__(self, *args, **kwargs): self.jwt_validator = kwargs.pop("jwt_validator", None) self.auth_required = kwargs.pop("auth_required", True) if self.jwt_validator is None: raise TypeError("Missing required keyword arg jwt_validator")
import re from typing import Dict, List, Optional import ujson as json from policyuniverse.expander_minimizer import _expand_wildcard_action from consoleme.config import config from consoleme.exceptions.exceptions import InvalidRequestParameter, MustBeFte from consoleme.handlers.base import BaseAPIV1Handler, BaseHandler, BaseMtlsHandler from consoleme.lib.account_indexers import get_account_id_to_name_mapping from consoleme.lib.cache import retrieve_json_data_from_redis_or_s3 from consoleme.lib.plugins import get_plugin_by_name from consoleme.lib.redis import redis_get, redis_hgetall log = config.get_logger() stats = get_plugin_by_name(config.get("plugins.metrics", "default_metrics"))() aws = get_plugin_by_name(config.get("plugins.aws", "default_aws"))() auth = get_plugin_by_name(config.get("plugins.auth", "default_auth"))() class AutocompleteHandler(BaseAPIV1Handler): async def get(self): """ /api/v1/policyuniverse/autocomplete/?prefix= --- get: description: Supplies autocompleted permissions for the ace code editor. responses: 200: description: Returns a list of the matching permissions. """
async def get_formatted_policy_changes(account_id, arn, request): aws = get_plugin_by_name(config.get("plugins.aws", "default_aws"))() existing_role: dict = await aws.fetch_iam_role(account_id, arn, force_refresh=True) policy_changes: list = json.loads(request.get("policy_changes")) formatted_policy_changes = [] # Parse request json and figure out how to present to the page for policy_change in policy_changes: if not policy_change.get("inline_policies"): policy_change["inline_policies"] = [] if len(policy_change.get("inline_policies")) > 1: raise InvalidRequestParameter( "Only one inline policy change at a time is currently supported." ) for inline_policy in policy_change.get("inline_policies"): if policy_change.get("arn") != arn: raise InvalidRequestParameter( "Only one role can be changed in a request") policy_name = inline_policy.get("policy_name") await validate_policy_name(policy_name) policy_document = inline_policy.get("policy_document") old_policy = {} new_policy: bool = False existing_policy_document = {} if request.get("status") == "approved": old_policy = request.get("old_policy", {}) if old_policy: existing_policy_document = json.loads(old_policy)[0] if not old_policy: existing_inline_policies = existing_role["policy"].get( "RolePolicyList", []) existing_policy_document = {} for existing_policy in existing_inline_policies: if existing_policy["PolicyName"] == policy_name: existing_policy_document = existing_policy[ "PolicyDocument"] # Generate dictionary with old / new policy documents diff = DeepDiff(existing_policy_document, policy_document) if not existing_policy_document: new_policy = True formatted_policy_changes.append({ "name": policy_name, "old": existing_policy_document, "new": policy_document, "diff": diff, "new_policy": new_policy, }) assume_role_policy_document = policy_change.get( "assume_role_policy_document") if assume_role_policy_document: if policy_change.get("arn") != arn: raise InvalidRequestParameter( "Only one role can be changed in a request") existing_ar_policy = existing_role["policy"][ "AssumeRolePolicyDocument"] old_policy = request.get("old_policy", {}) if old_policy: existing_ar_policy = json.loads(old_policy)[0] diff = DeepDiff( existing_ar_policy, assume_role_policy_document.get("assume_role_policy_document"), ) formatted_policy_changes.append({ "name": "AssumeRolePolicyDocument", "old": existing_ar_policy, "new": assume_role_policy_document.get("assume_role_policy_document"), "new_policy": False, "diff": diff, }) resource_policy_documents = request.get("resource_policies") if resource_policy_documents: for resource in resource_policy_documents: existing_policy_document = None # TODO: make this actually fetch the resource policy # existing_policy_document = aws.fetch_resource_policy() new_policy_document = resource["policy_document"] diff = DeepDiff(existing_policy_document, new_policy_document) formatted_policy_changes.append({ "name": "ResourcePolicy", "old": existing_policy_document, "new": new_policy_document, "new_policy": not existing_policy_document, "diff": diff, }) return {"changes": formatted_policy_changes, "role": existing_role}
from threading import Timer from typing import Any, Dict, List, Optional, Union import logmatic import ujson as json import yaml from asgiref.sync import async_to_sync from pytz import timezone from consoleme.lib.aws_secret_manager import get_aws_secret from consoleme.lib.plugins import get_plugin_by_name config_plugin_entrypoint = os.environ.get( "CONSOLEME_CONFIG_ENTRYPOINT", "default_config" ) config_plugin = get_plugin_by_name(config_plugin_entrypoint) def dict_merge(dct: dict, merge_dct: dict): """Recursively merge two dictionaries, including nested dicts""" for k, v in merge_dct.items(): if ( k in dct and isinstance(dct[k], dict) and isinstance(merge_dct[k], collections.Mapping) ): dict_merge(dct[k], merge_dct[k]) else: # Prefer original dict values over merged dict values if they already exist if k not in dct.keys(): dct[k] = merge_dct[k]
async def should_auto_approve_policy(events, user, user_groups): aws = get_plugin_by_name(config.get("plugins.aws", "default_aws"))() result = await aws.should_auto_approve_policy(events, user, user_groups) return result
from typing import Any, Dict import sentry_sdk import ujson as json from asgiref.sync import sync_to_async from cloudaux.aws.sts import boto3_cached_conn from consoleme.config import config from consoleme.exceptions.exceptions import ( DataNotRetrievable, MissingConfigurationValue, ) from consoleme.lib.dynamo import UserDynamoHandler from consoleme.lib.plugins import get_plugin_by_name aws = get_plugin_by_name(config.get("plugins.aws", "default_aws"))() log = config.get_logger() async def get_resource_from_cloudtrail_deny(ct_event, raw_ct_event): """ Naive attempt to determine resource from Access Deny CloudTrail event. If we can't parse it from the Cloudtrail message, we return `*`. """ resources = [ resource["ARN"] for resource in raw_ct_event.get("resources", []) if "ARN" in resource ] if resources: resource: str = max(resources, key=len)