Skip to content

feat: cookie parameters in proxy configuration #363

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 11 additions & 4 deletions src/satosa/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,13 +199,13 @@ def _load_state(self, context):
state = cookie_to_state(
context.cookie,
self.config["COOKIE_STATE_NAME"],
self.config["STATE_ENCRYPTION_KEY"],
self.config["STATE_ENCRYPTION_KEY"]
)
except SATOSAStateError as e:
state = State()
finally:
context.state = state
msg = "Loaded state {state} from cookie {cookie}".format(state=state, cookie=context.cookie)
msg = f"Loaded state {state} from cookie {context.cookie}"
logline = lu.LOG_FMT.format(id=lu.get_session_id(context.state), message=msg)
logger.info(logline)

Expand All @@ -220,8 +220,15 @@ def _save_state(self, resp, context):
:param context: Session context
"""

cookie = state_to_cookie(context.state, self.config["COOKIE_STATE_NAME"], "/",
self.config["STATE_ENCRYPTION_KEY"])
cookie = state_to_cookie(context.state,
self.config["COOKIE_STATE_NAME"],
"/",
self.config["STATE_ENCRYPTION_KEY"],
self.config.get("COOKIE_DOMAIN"),
self.config.get("COOKIE_SECURE", True),
self.config.get("COOKIE_HTTPONLY", True),
self.config.get("COOKIE_MAX_AGE", "")
)
resp.headers.append(tuple(cookie.output().split(": ", 1)))

def run(self, context):
Expand Down
20 changes: 14 additions & 6 deletions src/satosa/satosa_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def __init__(self, config):

# Load sensitive config from environment variables
for key in SATOSAConfig.sensitive_dict_keys:
val = os.environ.get("SATOSA_{key}".format(key=key))
val = os.environ.get(f"SATOSA_{key}")
if val:
self._config[key] = val

Expand All @@ -56,16 +56,22 @@ def __init__(self, config):
plugin_configs.append(plugin_config)
break
else:
raise SATOSAConfigurationError('Failed to load plugin config \'{}\''.format(config))
raise SATOSAConfigurationError(
f"Failed to load plugin config '{config}'"
)
self._config[key] = plugin_configs

for parser in parsers:
_internal_attributes = parser(self._config["INTERNAL_ATTRIBUTES"])
_internal_attributes = parser(
self._config["INTERNAL_ATTRIBUTES"]
)
if _internal_attributes is not None:
self._config["INTERNAL_ATTRIBUTES"] = _internal_attributes
break
if not self._config["INTERNAL_ATTRIBUTES"]:
raise SATOSAConfigurationError("Could not load attribute mapping from 'INTERNAL_ATTRIBUTES.")
raise SATOSAConfigurationError(
"Could not load attribute mapping from 'INTERNAL_ATTRIBUTES."
)

def _verify_dict(self, conf):
"""
Expand All @@ -86,8 +92,10 @@ def _verify_dict(self, conf):
raise SATOSAConfigurationError("Missing key '%s' in config" % key)

for key in SATOSAConfig.sensitive_dict_keys:
if key not in conf and "SATOSA_{key}".format(key=key) not in os.environ:
raise SATOSAConfigurationError("Missing key '%s' from config and ENVIRONMENT" % key)
if key not in conf and f"SATOSA_{key}" not in os.environ:
raise SATOSAConfigurationError(
f"Missing key '{key}' from config and ENVIRONMENT"
)

def __getitem__(self, item):
"""
Expand Down
28 changes: 19 additions & 9 deletions src/satosa/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,14 @@
_SESSION_ID_KEY = "SESSION_ID"


def state_to_cookie(state, name, path, encryption_key):
def state_to_cookie(state:str,
name:str,
path:str,
encryption_key:str,
domain:str=None,
secure:bool=True,
httponly:bool=True,
max_age:str=""):
"""
Saves a state to a cookie

Expand All @@ -42,15 +49,17 @@ def state_to_cookie(state, name, path, encryption_key):
:param encryption_key: Key to encrypt the state information
:return: A cookie
"""

cookie_data = "" if state.delete else state.urlstate(encryption_key)

cookie = SimpleCookie()
cookie[name] = cookie_data
cookie[name]["samesite"] = "None"
cookie[name]["secure"] = True
cookie[name]["secure"] = secure
if httponly:
cookie[name]["httponly"] = httponly
if domain:
cookie[name]["domain"] = domain
cookie[name]["path"] = path
cookie[name]["max-age"] = 0 if state.delete else ""
cookie[name]["max-age"] = 0 if state.delete else max_age

msg = "Saved state in cookie {name} with properties {props}".format(
name=name, props=list(cookie[name].items())
Expand All @@ -61,7 +70,7 @@ def state_to_cookie(state, name, path, encryption_key):
return cookie


def cookie_to_state(cookie_str, name, encryption_key):
def cookie_to_state(cookie_str:str, name:str, encryption_key:str):
"""
Loads a state from a cookie

Expand All @@ -79,8 +88,7 @@ def cookie_to_state(cookie_str, name, encryption_key):
cookie = SimpleCookie(cookie_str)
state = State(cookie[name].value, encryption_key)
except KeyError as e:
msg_tmpl = 'No cookie named {name} in {data}'
msg = msg_tmpl.format(name=name, data=cookie_str)
msg = f'No cookie named {name} in {cookie_str}'
raise SATOSAStateError(msg) from e
except ValueError as e:
msg_tmpl = 'Failed to process {name} from {data}'
Expand Down Expand Up @@ -183,7 +191,9 @@ def __init__(self, urlstate_data=None, encryption_key=None):

urlstate_data = {} if urlstate_data is None else urlstate_data
if urlstate_data and not encryption_key:
raise ValueError("If an 'urlstate_data' is supplied 'encrypt_key' must be specified.")
raise ValueError(
"If an 'urlstate_data' is supplied 'encrypt_key' must be specified."
)

if urlstate_data:
urlstate_data = urlstate_data.encode("utf-8")
Expand Down