Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
synapse
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Package registry
Container Registry
Model registry
Operate
Terraform modules
Monitor
Service Desk
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
Timo Ley
synapse
Commits
f5ea8b48
Unverified
Commit
f5ea8b48
authored
4 years ago
by
Patrick Cloke
Committed by
GitHub
4 years ago
Browse files
Options
Downloads
Patches
Plain Diff
Reject unknown UI auth sessions (instead of silently generating a new one) (#7268)
parent
0f8f02bc
No related branches found
No related tags found
No related merge requests found
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
changelog.d/7268.bugfix
+1
-0
1 addition, 0 deletions
changelog.d/7268.bugfix
synapse/handlers/auth.py
+94
-65
94 additions, 65 deletions
synapse/handlers/auth.py
with
95 additions
and
65 deletions
changelog.d/7268.bugfix
0 → 100644
+
1
−
0
View file @
f5ea8b48
Reject unknown session IDs during user interactive authentication instead of silently creating a new session.
This diff is collapsed.
Click to expand it.
synapse/handlers/auth.py
+
94
−
65
View file @
f5ea8b48
...
@@ -257,10 +257,6 @@ class AuthHandler(BaseHandler):
...
@@ -257,10 +257,6 @@ class AuthHandler(BaseHandler):
Takes a dictionary sent by the client in the login / registration
Takes a dictionary sent by the client in the login / registration
protocol and handles the User-Interactive Auth flow.
protocol and handles the User-Interactive Auth flow.
As a side effect, this function fills in the
'
creds
'
key on the user
'
s
session with a map, which maps each auth-type (str) to the relevant
identity authenticated by that auth-type (mostly str, but for captcha, bool).
If no auth flows have been completed successfully, raises an
If no auth flows have been completed successfully, raises an
InteractiveAuthIncompleteError. To handle this, you can use
InteractiveAuthIncompleteError. To handle this, you can use
synapse.rest.client.v2_alpha._base.interactive_auth_handler as a
synapse.rest.client.v2_alpha._base.interactive_auth_handler as a
...
@@ -304,50 +300,47 @@ class AuthHandler(BaseHandler):
...
@@ -304,50 +300,47 @@ class AuthHandler(BaseHandler):
del
clientdict
[
"
auth
"
]
del
clientdict
[
"
auth
"
]
if
"
session
"
in
authdict
:
if
"
session
"
in
authdict
:
sid
=
authdict
[
"
session
"
]
sid
=
authdict
[
"
session
"
]
session
=
self
.
_get_session_info
(
sid
)
# If there's no session ID, create a new session.
if
len
(
clientdict
)
>
0
:
if
not
sid
:
# This was designed to allow the client to omit the parameters
session
=
self
.
_create_session
(
# and just supply the session in subsequent calls so it split
clientdict
,
(
request
.
uri
,
request
.
method
,
clientdict
),
description
# auth between devices by just sharing the session, (eg. so you
# could continue registration from your phone having clicked the
# email auth link on there). It's probably too open to abuse
# because it lets unauthenticated clients store arbitrary objects
# on a homeserver.
# Revisit: Assuming the REST APIs do sensible validation, the data
# isn't arbintrary.
session
[
"
clientdict
"
]
=
clientdict
self
.
_save_session
(
session
)
elif
"
clientdict
"
in
session
:
clientdict
=
session
[
"
clientdict
"
]
# Ensure that the queried operation does not vary between stages of
# the UI authentication session. This is done by generating a stable
# comparator based on the URI, method, and body (minus the auth dict)
# and storing it during the initial query. Subsequent queries ensure
# that this comparator has not changed.
comparator
=
(
request
.
uri
,
request
.
method
,
clientdict
)
if
"
ui_auth
"
not
in
session
:
session
[
"
ui_auth
"
]
=
comparator
self
.
_save_session
(
session
)
elif
session
[
"
ui_auth
"
]
!=
comparator
:
raise
SynapseError
(
403
,
"
Requested operation has changed during the UI authentication session.
"
,
)
)
session_id
=
session
[
"
id
"
]
# Add a human readable description to the session.
else
:
if
"
description
"
not
in
session
:
session
=
self
.
_get_session_info
(
sid
)
session
[
"
description
"
]
=
description
session_id
=
sid
self
.
_save_session
(
session
)
if
not
clientdict
:
# This was designed to allow the client to omit the parameters
# and just supply the session in subsequent calls so it split
# auth between devices by just sharing the session, (eg. so you
# could continue registration from your phone having clicked the
# email auth link on there). It's probably too open to abuse
# because it lets unauthenticated clients store arbitrary objects
# on a homeserver.
# Revisit: Assuming the REST APIs do sensible validation, the data
# isn't arbitrary.
clientdict
=
session
[
"
clientdict
"
]
# Ensure that the queried operation does not vary between stages of
# the UI authentication session. This is done by generating a stable
# comparator based on the URI, method, and body (minus the auth dict)
# and storing it during the initial query. Subsequent queries ensure
# that this comparator has not changed.
comparator
=
(
request
.
uri
,
request
.
method
,
clientdict
)
if
session
[
"
ui_auth
"
]
!=
comparator
:
raise
SynapseError
(
403
,
"
Requested operation has changed during the UI authentication session.
"
,
)
if
not
authdict
:
if
not
authdict
:
raise
InteractiveAuthIncompleteError
(
raise
InteractiveAuthIncompleteError
(
self
.
_auth_dict_for_flows
(
flows
,
session
)
self
.
_auth_dict_for_flows
(
flows
,
session
_id
)
)
)
if
"
creds
"
not
in
session
:
session
[
"
creds
"
]
=
{}
creds
=
session
[
"
creds
"
]
creds
=
session
[
"
creds
"
]
# check auth type currently being presented
# check auth type currently being presented
...
@@ -387,9 +380,9 @@ class AuthHandler(BaseHandler):
...
@@ -387,9 +380,9 @@ class AuthHandler(BaseHandler):
list
(
clientdict
),
list
(
clientdict
),
)
)
return
creds
,
clientdict
,
session
[
"
id
"
]
return
creds
,
clientdict
,
session
_id
ret
=
self
.
_auth_dict_for_flows
(
flows
,
session
)
ret
=
self
.
_auth_dict_for_flows
(
flows
,
session
_id
)
ret
[
"
completed
"
]
=
list
(
creds
)
ret
[
"
completed
"
]
=
list
(
creds
)
ret
.
update
(
errordict
)
ret
.
update
(
errordict
)
raise
InteractiveAuthIncompleteError
(
ret
)
raise
InteractiveAuthIncompleteError
(
ret
)
...
@@ -407,8 +400,6 @@ class AuthHandler(BaseHandler):
...
@@ -407,8 +400,6 @@ class AuthHandler(BaseHandler):
raise
LoginError
(
400
,
""
,
Codes
.
MISSING_PARAM
)
raise
LoginError
(
400
,
""
,
Codes
.
MISSING_PARAM
)
sess
=
self
.
_get_session_info
(
authdict
[
"
session
"
])
sess
=
self
.
_get_session_info
(
authdict
[
"
session
"
])
if
"
creds
"
not
in
sess
:
sess
[
"
creds
"
]
=
{}
creds
=
sess
[
"
creds
"
]
creds
=
sess
[
"
creds
"
]
result
=
await
self
.
checkers
[
stagetype
].
check_auth
(
authdict
,
clientip
)
result
=
await
self
.
checkers
[
stagetype
].
check_auth
(
authdict
,
clientip
)
...
@@ -448,7 +439,7 @@ class AuthHandler(BaseHandler):
...
@@ -448,7 +439,7 @@ class AuthHandler(BaseHandler):
value: The data to store
value: The data to store
"""
"""
sess
=
self
.
_get_session_info
(
session_id
)
sess
=
self
.
_get_session_info
(
session_id
)
sess
.
setdefault
(
"
serverdict
"
,
{})
[
key
]
=
value
sess
[
"
serverdict
"
]
[
key
]
=
value
self
.
_save_session
(
sess
)
self
.
_save_session
(
sess
)
def
get_session_data
(
def
get_session_data
(
...
@@ -463,7 +454,7 @@ class AuthHandler(BaseHandler):
...
@@ -463,7 +454,7 @@ class AuthHandler(BaseHandler):
default: Value to return if the key has not been set
default: Value to return if the key has not been set
"""
"""
sess
=
self
.
_get_session_info
(
session_id
)
sess
=
self
.
_get_session_info
(
session_id
)
return
sess
.
setdefault
(
"
serverdict
"
,
{})
.
get
(
key
,
default
)
return
sess
[
"
serverdict
"
]
.
get
(
key
,
default
)
async
def
_check_auth_dict
(
async
def
_check_auth_dict
(
self
,
authdict
:
Dict
[
str
,
Any
],
clientip
:
str
self
,
authdict
:
Dict
[
str
,
Any
],
clientip
:
str
...
@@ -519,7 +510,7 @@ class AuthHandler(BaseHandler):
...
@@ -519,7 +510,7 @@ class AuthHandler(BaseHandler):
}
}
def
_auth_dict_for_flows
(
def
_auth_dict_for_flows
(
self
,
flows
:
List
[
List
[
str
]],
session
:
Dict
[
str
,
Any
]
self
,
flows
:
List
[
List
[
str
]],
session
_id
:
str
,
)
->
Dict
[
str
,
Any
]:
)
->
Dict
[
str
,
Any
]:
public_flows
=
[]
public_flows
=
[]
for
f
in
flows
:
for
f
in
flows
:
...
@@ -538,29 +529,72 @@ class AuthHandler(BaseHandler):
...
@@ -538,29 +529,72 @@ class AuthHandler(BaseHandler):
params
[
stage
]
=
get_params
[
stage
]()
params
[
stage
]
=
get_params
[
stage
]()
return
{
return
{
"
session
"
:
session
[
"
id
"
]
,
"
session
"
:
session
_id
,
"
flows
"
:
[{
"
stages
"
:
f
}
for
f
in
public_flows
],
"
flows
"
:
[{
"
stages
"
:
f
}
for
f
in
public_flows
],
"
params
"
:
params
,
"
params
"
:
params
,
}
}
def
_get_session_info
(
self
,
session_id
:
Optional
[
str
])
->
dict
:
def
_create_session
(
self
,
clientdict
:
Dict
[
str
,
Any
],
ui_auth
:
Tuple
[
bytes
,
bytes
,
Dict
[
str
,
Any
]],
description
:
str
,
)
->
dict
:
"""
"""
Gets or creates a session g
ive
n
a session
ID
.
Creates a new user interact
ive a
uthentication
session.
The session can be used to track data across multiple requests, e.g. for
The session can be used to track data across multiple requests, e.g. for
interactive authentication.
interactive authentication.
"""
if
session_id
not
in
self
.
sessions
:
session_id
=
None
if
not
session_id
:
Each session has the following keys:
# create a new session
while
session_id
is
None
or
session_id
in
self
.
sessions
:
id:
session_id
=
stringutils
.
random_string
(
24
)
A unique identifier for this session. Passed back to the client
self
.
sessions
[
session_id
]
=
{
"
id
"
:
session_id
}
and returned for each stage.
clientdict:
The dictionary from the client root level, not the
'
auth
'
key.
ui_auth:
A tuple which is checked at each stage of the authentication to
ensure that the asked for operation has not changed.
creds:
A map, which maps each auth-type (str) to the relevant identity
authenticated by that auth-type (mostly str, but for captcha, bool).
serverdict:
A map of data that is stored server-side and cannot be modified
by the client.
description:
A string description of the operation that the current
authentication is authorising.
Returns:
The newly created session.
"""
session_id
=
None
while
session_id
is
None
or
session_id
in
self
.
sessions
:
session_id
=
stringutils
.
random_string
(
24
)
self
.
sessions
[
session_id
]
=
{
"
id
"
:
session_id
,
"
clientdict
"
:
clientdict
,
"
ui_auth
"
:
ui_auth
,
"
creds
"
:
{},
"
serverdict
"
:
{},
"
description
"
:
description
,
}
return
self
.
sessions
[
session_id
]
return
self
.
sessions
[
session_id
]
def
_get_session_info
(
self
,
session_id
:
str
)
->
dict
:
"""
Gets a session given a session ID.
The session can be used to track data across multiple requests, e.g. for
interactive authentication.
"""
try
:
return
self
.
sessions
[
session_id
]
except
KeyError
:
raise
SynapseError
(
400
,
"
Unknown session ID: %s
"
%
(
session_id
,))
async
def
get_access_token_for_user_id
(
async
def
get_access_token_for_user_id
(
self
,
user_id
:
str
,
device_id
:
Optional
[
str
],
valid_until_ms
:
Optional
[
int
]
self
,
user_id
:
str
,
device_id
:
Optional
[
str
],
valid_until_ms
:
Optional
[
int
]
):
):
...
@@ -1030,11 +1064,8 @@ class AuthHandler(BaseHandler):
...
@@ -1030,11 +1064,8 @@ class AuthHandler(BaseHandler):
The HTML to render.
The HTML to render.
"""
"""
session
=
self
.
_get_session_info
(
session_id
)
session
=
self
.
_get_session_info
(
session_id
)
# Get the human readable operation of what is occurring, falling back to
# a generic message if it isn't available for some reason.
description
=
session
.
get
(
"
description
"
,
"
modify your account
"
)
return
self
.
_sso_auth_confirm_template
.
render
(
return
self
.
_sso_auth_confirm_template
.
render
(
description
=
description
,
redirect_url
=
redirect_url
,
description
=
session
[
"
description
"
]
,
redirect_url
=
redirect_url
,
)
)
def
complete_sso_ui_auth
(
def
complete_sso_ui_auth
(
...
@@ -1050,8 +1081,6 @@ class AuthHandler(BaseHandler):
...
@@ -1050,8 +1081,6 @@ class AuthHandler(BaseHandler):
"""
"""
# Mark the stage of the authentication as successful.
# Mark the stage of the authentication as successful.
sess
=
self
.
_get_session_info
(
session_id
)
sess
=
self
.
_get_session_info
(
session_id
)
if
"
creds
"
not
in
sess
:
sess
[
"
creds
"
]
=
{}
creds
=
sess
[
"
creds
"
]
creds
=
sess
[
"
creds
"
]
# Save the user who authenticated with SSO, this will be used to ensure
# Save the user who authenticated with SSO, this will be used to ensure
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment