forked from MirrorHub/synapse
Merge pull request #299 from stevenhammerton/sh-cas-required-attribute
SH CAS Required Attribute
This commit is contained in:
commit
427943907f
2 changed files with 61 additions and 27 deletions
|
@ -27,13 +27,17 @@ class CasConfig(Config):
|
||||||
if cas_config:
|
if cas_config:
|
||||||
self.cas_enabled = True
|
self.cas_enabled = True
|
||||||
self.cas_server_url = cas_config["server_url"]
|
self.cas_server_url = cas_config["server_url"]
|
||||||
|
self.cas_required_attributes = cas_config.get("required_attributes", {})
|
||||||
else:
|
else:
|
||||||
self.cas_enabled = False
|
self.cas_enabled = False
|
||||||
self.cas_server_url = None
|
self.cas_server_url = None
|
||||||
|
self.cas_required_attributes = {}
|
||||||
|
|
||||||
def default_config(self, config_dir_path, server_name, **kwargs):
|
def default_config(self, config_dir_path, server_name, **kwargs):
|
||||||
return """
|
return """
|
||||||
# Enable CAS for registration and login.
|
# Enable CAS for registration and login.
|
||||||
#cas_config:
|
#cas_config:
|
||||||
# server_url: "https://cas-server.com"
|
# server_url: "https://cas-server.com"
|
||||||
|
# #required_attributes:
|
||||||
|
# # name: value
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -45,8 +45,8 @@ class LoginRestServlet(ClientV1RestServlet):
|
||||||
self.idp_redirect_url = hs.config.saml2_idp_redirect_url
|
self.idp_redirect_url = hs.config.saml2_idp_redirect_url
|
||||||
self.saml2_enabled = hs.config.saml2_enabled
|
self.saml2_enabled = hs.config.saml2_enabled
|
||||||
self.cas_enabled = hs.config.cas_enabled
|
self.cas_enabled = hs.config.cas_enabled
|
||||||
|
|
||||||
self.cas_server_url = hs.config.cas_server_url
|
self.cas_server_url = hs.config.cas_server_url
|
||||||
|
self.cas_required_attributes = hs.config.cas_required_attributes
|
||||||
self.servername = hs.config.server_name
|
self.servername = hs.config.server_name
|
||||||
|
|
||||||
def on_GET(self, request):
|
def on_GET(self, request):
|
||||||
|
@ -125,6 +125,47 @@ class LoginRestServlet(ClientV1RestServlet):
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def do_cas_login(self, cas_response_body):
|
def do_cas_login(self, cas_response_body):
|
||||||
|
user, attributes = self.parse_cas_response(cas_response_body)
|
||||||
|
|
||||||
|
for required_attribute, required_value in self.cas_required_attributes.items():
|
||||||
|
# If required attribute was not in CAS Response - Forbidden
|
||||||
|
if required_attribute not in attributes:
|
||||||
|
raise LoginError(401, "Unauthorized", errcode=Codes.UNAUTHORIZED)
|
||||||
|
|
||||||
|
# Also need to check value
|
||||||
|
if required_value is not None:
|
||||||
|
actual_value = attributes[required_attribute]
|
||||||
|
# If required attribute value does not match expected - Forbidden
|
||||||
|
if required_value != actual_value:
|
||||||
|
raise LoginError(401, "Unauthorized", errcode=Codes.UNAUTHORIZED)
|
||||||
|
|
||||||
|
user_id = UserID.create(user, self.hs.hostname).to_string()
|
||||||
|
auth_handler = self.handlers.auth_handler
|
||||||
|
user_exists = yield auth_handler.does_user_exist(user_id)
|
||||||
|
if user_exists:
|
||||||
|
user_id, access_token, refresh_token = (
|
||||||
|
yield auth_handler.login_with_cas_user_id(user_id)
|
||||||
|
)
|
||||||
|
result = {
|
||||||
|
"user_id": user_id, # may have changed
|
||||||
|
"access_token": access_token,
|
||||||
|
"refresh_token": refresh_token,
|
||||||
|
"home_server": self.hs.hostname,
|
||||||
|
}
|
||||||
|
|
||||||
|
else:
|
||||||
|
user_id, access_token = (
|
||||||
|
yield self.handlers.registration_handler.register(localpart=user)
|
||||||
|
)
|
||||||
|
result = {
|
||||||
|
"user_id": user_id, # may have changed
|
||||||
|
"access_token": access_token,
|
||||||
|
"home_server": self.hs.hostname,
|
||||||
|
}
|
||||||
|
|
||||||
|
defer.returnValue((200, result))
|
||||||
|
|
||||||
|
def parse_cas_response(self, cas_response_body):
|
||||||
root = ET.fromstring(cas_response_body)
|
root = ET.fromstring(cas_response_body)
|
||||||
if not root.tag.endswith("serviceResponse"):
|
if not root.tag.endswith("serviceResponse"):
|
||||||
raise LoginError(401, "Invalid CAS response", errcode=Codes.UNAUTHORIZED)
|
raise LoginError(401, "Invalid CAS response", errcode=Codes.UNAUTHORIZED)
|
||||||
|
@ -133,33 +174,22 @@ class LoginRestServlet(ClientV1RestServlet):
|
||||||
for child in root[0]:
|
for child in root[0]:
|
||||||
if child.tag.endswith("user"):
|
if child.tag.endswith("user"):
|
||||||
user = child.text
|
user = child.text
|
||||||
user_id = UserID.create(user, self.hs.hostname).to_string()
|
if child.tag.endswith("attributes"):
|
||||||
auth_handler = self.handlers.auth_handler
|
attributes = {}
|
||||||
user_exists = yield auth_handler.does_user_exist(user_id)
|
for attribute in child:
|
||||||
if user_exists:
|
# ElementTree library expands the namespace in attribute tags
|
||||||
user_id, access_token, refresh_token = (
|
# to the full URL of the namespace.
|
||||||
yield auth_handler.login_with_cas_user_id(user_id)
|
# See (https://docs.python.org/2/library/xml.etree.elementtree.html)
|
||||||
)
|
# We don't care about namespace here and it will always be encased in
|
||||||
result = {
|
# curly braces, so we remove them.
|
||||||
"user_id": user_id, # may have changed
|
if "}" in attribute.tag:
|
||||||
"access_token": access_token,
|
attributes[attribute.tag.split("}")[1]] = attribute.text
|
||||||
"refresh_token": refresh_token,
|
else:
|
||||||
"home_server": self.hs.hostname,
|
attributes[attribute.tag] = attribute.text
|
||||||
}
|
if user is None or attributes is None:
|
||||||
|
raise LoginError(401, "Invalid CAS response", errcode=Codes.UNAUTHORIZED)
|
||||||
|
|
||||||
else:
|
return (user, attributes)
|
||||||
user_id, access_token = (
|
|
||||||
yield self.handlers.registration_handler.register(localpart=user)
|
|
||||||
)
|
|
||||||
result = {
|
|
||||||
"user_id": user_id, # may have changed
|
|
||||||
"access_token": access_token,
|
|
||||||
"home_server": self.hs.hostname,
|
|
||||||
}
|
|
||||||
|
|
||||||
defer.returnValue((200, result))
|
|
||||||
|
|
||||||
raise LoginError(401, "Invalid CAS response", errcode=Codes.UNAUTHORIZED)
|
|
||||||
|
|
||||||
|
|
||||||
class LoginFallbackRestServlet(ClientV1RestServlet):
|
class LoginFallbackRestServlet(ClientV1RestServlet):
|
||||||
|
|
Loading…
Reference in a new issue