moto/moto/sts/models.py

145 lines
4.8 KiB
Python
Raw Normal View History

from base64 import b64decode
2013-05-24 21:22:34 +00:00
import datetime
import xmltodict
2017-03-12 04:41:12 +00:00
from moto.core import BaseBackend, BaseModel
from moto.core.utils import iso_8601_datetime_with_milliseconds, BackendDict
from moto.core import get_account_id
2019-10-31 15:44:26 +00:00
from moto.sts.utils import (
random_access_key_id,
random_secret_access_key,
random_session_token,
random_assumed_role_id,
Fix saml-assertion parsing in assume-role-with-saml (#3523) * Retrieve SAML Attribute by Name instead of relying on order which is too fragile * Handle case when SAML Attribute SessionDuration is not provided, as it is not a required attribute from SAML response When session duration not provided, AWS consider by default a duration of one hour as cited in the following documentation: "If this attribute is not present, then the credential last for one hour (the default value of the DurationSeconds parameter of the AssumeRoleWithSAML API)." https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_saml_assertions.html#saml_role-session-duration Traceback was: [...] File "/Users/benjamin.brabant/Projects/PERSO/moto/moto/sts/responses.py", line 79, in assume_role_with_saml role = sts_backend.assume_role_with_saml( File "/Users/benjamin.brabant/Projects/PERSO/moto/moto/sts/models.py", line 99, in assume_role_with_saml role = AssumedRole(**kwargs) TypeError: __init__() missing 1 required positional argument: 'duration' * Process saml xml namespaces properly instead of relying on textual prefix that can vary between identity providers * Handle when SAML response AttributeValue xml tag contains attributes that force xmltodict to build a dictionary as for complex types instead of directly returning string value Leverage force_cdata option of xmltodict parser that always return a complex dictionary even if xml tag contains only text and no attributes. * Improve existing test_assume_role_with_saml to be coherent with other assume_role_with_saml tests and remove dead code at the same time
2020-12-08 09:08:40 +00:00
DEFAULT_STS_SESSION_DURATION,
2019-10-31 15:44:26 +00:00
)
from typing import Mapping
2013-05-24 21:22:34 +00:00
2017-03-12 04:41:12 +00:00
class Token(BaseModel):
def __init__(self, duration, name=None):
2016-09-07 18:40:52 +00:00
now = datetime.datetime.utcnow()
2013-05-24 21:22:34 +00:00
self.expiration = now + datetime.timedelta(seconds=duration)
self.name = name
self.policy = None
2013-05-24 21:22:34 +00:00
@property
def expiration_ISO8601(self):
2014-11-30 04:34:40 +00:00
return iso_8601_datetime_with_milliseconds(self.expiration)
2013-05-24 21:22:34 +00:00
2017-03-12 04:41:12 +00:00
class AssumedRole(BaseModel):
2013-05-24 21:22:34 +00:00
def __init__(self, role_session_name, role_arn, policy, duration, external_id):
self.session_name = role_session_name
self.role_arn = role_arn
2013-05-24 21:22:34 +00:00
self.policy = policy
2016-09-07 18:40:52 +00:00
now = datetime.datetime.utcnow()
2013-05-24 21:22:34 +00:00
self.expiration = now + datetime.timedelta(seconds=duration)
self.external_id = external_id
self.access_key_id = "ASIA" + random_access_key_id()
self.secret_access_key = random_secret_access_key()
self.session_token = random_session_token()
self.assumed_role_id = "AROA" + random_assumed_role_id()
2013-05-24 21:22:34 +00:00
@property
def expiration_ISO8601(self):
2014-11-30 04:34:40 +00:00
return iso_8601_datetime_with_milliseconds(self.expiration)
2013-05-24 21:22:34 +00:00
@property
def user_id(self):
return self.assumed_role_id + ":" + self.session_name
@property
def arn(self):
2022-03-10 14:39:59 +00:00
return (
"arn:aws:sts::{account_id}:assumed-role/{role_name}/{session_name}".format(
account_id=get_account_id(),
2022-03-10 14:39:59 +00:00
role_name=self.role_arn.split("/")[-1],
session_name=self.session_name,
)
2019-08-21 17:47:12 +00:00
)
2013-05-24 21:22:34 +00:00
class STSBackend(BaseBackend):
def __init__(self, region_name, account_id):
super().__init__(region_name, account_id)
self.assumed_roles = []
@staticmethod
def default_vpc_endpoint_service(service_region, zones):
"""Default VPC endpoint service."""
return BaseBackend.default_vpc_endpoint_service_factory(
service_region, zones, "sts"
)
2013-05-24 21:22:34 +00:00
def get_session_token(self, duration):
token = Token(duration=duration)
return token
def get_federation_token(self, name, duration):
token = Token(duration=duration, name=name)
return token
2013-05-24 21:22:34 +00:00
def assume_role(self, **kwargs):
role = AssumedRole(**kwargs)
self.assumed_roles.append(role)
2013-05-24 21:22:34 +00:00
return role
def get_assumed_role_from_access_key(self, access_key_id):
for assumed_role in self.assumed_roles:
if assumed_role.access_key_id == access_key_id:
return assumed_role
return None
def assume_role_with_web_identity(self, **kwargs):
return self.assume_role(**kwargs)
def assume_role_with_saml(self, **kwargs):
del kwargs["principal_arn"]
saml_assertion_encoded = kwargs.pop("saml_assertion")
saml_assertion_decoded = b64decode(saml_assertion_encoded)
Fix saml-assertion parsing in assume-role-with-saml (#3523) * Retrieve SAML Attribute by Name instead of relying on order which is too fragile * Handle case when SAML Attribute SessionDuration is not provided, as it is not a required attribute from SAML response When session duration not provided, AWS consider by default a duration of one hour as cited in the following documentation: "If this attribute is not present, then the credential last for one hour (the default value of the DurationSeconds parameter of the AssumeRoleWithSAML API)." https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_saml_assertions.html#saml_role-session-duration Traceback was: [...] File "/Users/benjamin.brabant/Projects/PERSO/moto/moto/sts/responses.py", line 79, in assume_role_with_saml role = sts_backend.assume_role_with_saml( File "/Users/benjamin.brabant/Projects/PERSO/moto/moto/sts/models.py", line 99, in assume_role_with_saml role = AssumedRole(**kwargs) TypeError: __init__() missing 1 required positional argument: 'duration' * Process saml xml namespaces properly instead of relying on textual prefix that can vary between identity providers * Handle when SAML response AttributeValue xml tag contains attributes that force xmltodict to build a dictionary as for complex types instead of directly returning string value Leverage force_cdata option of xmltodict parser that always return a complex dictionary even if xml tag contains only text and no attributes. * Improve existing test_assume_role_with_saml to be coherent with other assume_role_with_saml tests and remove dead code at the same time
2020-12-08 09:08:40 +00:00
namespaces = {
"urn:oasis:names:tc:SAML:2.0:protocol": "samlp",
"urn:oasis:names:tc:SAML:2.0:assertion": "saml",
}
saml_assertion = xmltodict.parse(
saml_assertion_decoded.decode("utf-8"),
force_cdata=True,
process_namespaces=True,
namespaces=namespaces,
namespace_separator="|",
)
Fix saml-assertion parsing in assume-role-with-saml (#3523) * Retrieve SAML Attribute by Name instead of relying on order which is too fragile * Handle case when SAML Attribute SessionDuration is not provided, as it is not a required attribute from SAML response When session duration not provided, AWS consider by default a duration of one hour as cited in the following documentation: "If this attribute is not present, then the credential last for one hour (the default value of the DurationSeconds parameter of the AssumeRoleWithSAML API)." https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_saml_assertions.html#saml_role-session-duration Traceback was: [...] File "/Users/benjamin.brabant/Projects/PERSO/moto/moto/sts/responses.py", line 79, in assume_role_with_saml role = sts_backend.assume_role_with_saml( File "/Users/benjamin.brabant/Projects/PERSO/moto/moto/sts/models.py", line 99, in assume_role_with_saml role = AssumedRole(**kwargs) TypeError: __init__() missing 1 required positional argument: 'duration' * Process saml xml namespaces properly instead of relying on textual prefix that can vary between identity providers * Handle when SAML response AttributeValue xml tag contains attributes that force xmltodict to build a dictionary as for complex types instead of directly returning string value Leverage force_cdata option of xmltodict parser that always return a complex dictionary even if xml tag contains only text and no attributes. * Improve existing test_assume_role_with_saml to be coherent with other assume_role_with_saml tests and remove dead code at the same time
2020-12-08 09:08:40 +00:00
saml_assertion_attributes = saml_assertion["samlp|Response"]["saml|Assertion"][
"saml|AttributeStatement"
]["saml|Attribute"]
Fix saml-assertion parsing in assume-role-with-saml (#3523) * Retrieve SAML Attribute by Name instead of relying on order which is too fragile * Handle case when SAML Attribute SessionDuration is not provided, as it is not a required attribute from SAML response When session duration not provided, AWS consider by default a duration of one hour as cited in the following documentation: "If this attribute is not present, then the credential last for one hour (the default value of the DurationSeconds parameter of the AssumeRoleWithSAML API)." https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_saml_assertions.html#saml_role-session-duration Traceback was: [...] File "/Users/benjamin.brabant/Projects/PERSO/moto/moto/sts/responses.py", line 79, in assume_role_with_saml role = sts_backend.assume_role_with_saml( File "/Users/benjamin.brabant/Projects/PERSO/moto/moto/sts/models.py", line 99, in assume_role_with_saml role = AssumedRole(**kwargs) TypeError: __init__() missing 1 required positional argument: 'duration' * Process saml xml namespaces properly instead of relying on textual prefix that can vary between identity providers * Handle when SAML response AttributeValue xml tag contains attributes that force xmltodict to build a dictionary as for complex types instead of directly returning string value Leverage force_cdata option of xmltodict parser that always return a complex dictionary even if xml tag contains only text and no attributes. * Improve existing test_assume_role_with_saml to be coherent with other assume_role_with_saml tests and remove dead code at the same time
2020-12-08 09:08:40 +00:00
for attribute in saml_assertion_attributes:
if (
attribute["@Name"]
== "https://aws.amazon.com/SAML/Attributes/RoleSessionName"
):
kwargs["role_session_name"] = attribute["saml|AttributeValue"]["#text"]
Fix saml-assertion parsing in assume-role-with-saml (#3523) * Retrieve SAML Attribute by Name instead of relying on order which is too fragile * Handle case when SAML Attribute SessionDuration is not provided, as it is not a required attribute from SAML response When session duration not provided, AWS consider by default a duration of one hour as cited in the following documentation: "If this attribute is not present, then the credential last for one hour (the default value of the DurationSeconds parameter of the AssumeRoleWithSAML API)." https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_saml_assertions.html#saml_role-session-duration Traceback was: [...] File "/Users/benjamin.brabant/Projects/PERSO/moto/moto/sts/responses.py", line 79, in assume_role_with_saml role = sts_backend.assume_role_with_saml( File "/Users/benjamin.brabant/Projects/PERSO/moto/moto/sts/models.py", line 99, in assume_role_with_saml role = AssumedRole(**kwargs) TypeError: __init__() missing 1 required positional argument: 'duration' * Process saml xml namespaces properly instead of relying on textual prefix that can vary between identity providers * Handle when SAML response AttributeValue xml tag contains attributes that force xmltodict to build a dictionary as for complex types instead of directly returning string value Leverage force_cdata option of xmltodict parser that always return a complex dictionary even if xml tag contains only text and no attributes. * Improve existing test_assume_role_with_saml to be coherent with other assume_role_with_saml tests and remove dead code at the same time
2020-12-08 09:08:40 +00:00
if (
attribute["@Name"]
== "https://aws.amazon.com/SAML/Attributes/SessionDuration"
):
kwargs["duration"] = int(attribute["saml|AttributeValue"]["#text"])
Fix saml-assertion parsing in assume-role-with-saml (#3523) * Retrieve SAML Attribute by Name instead of relying on order which is too fragile * Handle case when SAML Attribute SessionDuration is not provided, as it is not a required attribute from SAML response When session duration not provided, AWS consider by default a duration of one hour as cited in the following documentation: "If this attribute is not present, then the credential last for one hour (the default value of the DurationSeconds parameter of the AssumeRoleWithSAML API)." https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_saml_assertions.html#saml_role-session-duration Traceback was: [...] File "/Users/benjamin.brabant/Projects/PERSO/moto/moto/sts/responses.py", line 79, in assume_role_with_saml role = sts_backend.assume_role_with_saml( File "/Users/benjamin.brabant/Projects/PERSO/moto/moto/sts/models.py", line 99, in assume_role_with_saml role = AssumedRole(**kwargs) TypeError: __init__() missing 1 required positional argument: 'duration' * Process saml xml namespaces properly instead of relying on textual prefix that can vary between identity providers * Handle when SAML response AttributeValue xml tag contains attributes that force xmltodict to build a dictionary as for complex types instead of directly returning string value Leverage force_cdata option of xmltodict parser that always return a complex dictionary even if xml tag contains only text and no attributes. * Improve existing test_assume_role_with_saml to be coherent with other assume_role_with_saml tests and remove dead code at the same time
2020-12-08 09:08:40 +00:00
if "duration" not in kwargs:
kwargs["duration"] = DEFAULT_STS_SESSION_DURATION
kwargs["external_id"] = None
kwargs["policy"] = None
role = AssumedRole(**kwargs)
self.assumed_roles.append(role)
return role
def get_caller_identity(self):
# Logic resides in responses.py
# Fake method here to make implementation coverage script aware that this method is implemented
pass
2017-02-24 02:37:43 +00:00
sts_backends: Mapping[str, STSBackend] = BackendDict(
STSBackend, "sts", use_boto3_regions=False, additional_regions=["global"]
)