SAML2 Improvements and redirect stuff
Signed-off-by: Alexander Trost <galexrt@googlemail.com>
This commit is contained in:
parent
d828d1dc57
commit
dc3e586938
|
@ -57,6 +57,7 @@ class LoginType(object):
|
||||||
EMAIL_IDENTITY = u"m.login.email.identity"
|
EMAIL_IDENTITY = u"m.login.email.identity"
|
||||||
MSISDN = u"m.login.msisdn"
|
MSISDN = u"m.login.msisdn"
|
||||||
RECAPTCHA = u"m.login.recaptcha"
|
RECAPTCHA = u"m.login.recaptcha"
|
||||||
|
SSO = u"m.login.sso"
|
||||||
TERMS = u"m.login.terms"
|
TERMS = u"m.login.terms"
|
||||||
DUMMY = u"m.login.dummy"
|
DUMMY = u"m.login.dummy"
|
||||||
|
|
||||||
|
|
|
@ -75,6 +75,7 @@ class SAML2Config(Config):
|
||||||
# override them.
|
# override them.
|
||||||
#
|
#
|
||||||
#saml2_config:
|
#saml2_config:
|
||||||
|
# enabled: true
|
||||||
# sp_config:
|
# sp_config:
|
||||||
# # point this to the IdP's metadata. You can use either a local file or
|
# # point this to the IdP's metadata. You can use either a local file or
|
||||||
# # (preferably) a URL.
|
# # (preferably) a URL.
|
||||||
|
|
|
@ -727,6 +727,9 @@ class AuthHandler(BaseHandler):
|
||||||
if canonical_user_id:
|
if canonical_user_id:
|
||||||
defer.returnValue((canonical_user_id, None))
|
defer.returnValue((canonical_user_id, None))
|
||||||
|
|
||||||
|
if login_type == LoginType.SSO:
|
||||||
|
known_login_type = True
|
||||||
|
|
||||||
if not known_login_type:
|
if not known_login_type:
|
||||||
raise SynapseError(400, "Unknown login type %s" % login_type)
|
raise SynapseError(400, "Unknown login type %s" % login_type)
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,9 @@ from synapse.rest.well_known import WellKnownBuilder
|
||||||
from synapse.types import UserID, map_username_to_mxid_localpart
|
from synapse.types import UserID, map_username_to_mxid_localpart
|
||||||
from synapse.util.msisdn import phone_number_to_msisdn
|
from synapse.util.msisdn import phone_number_to_msisdn
|
||||||
|
|
||||||
|
import saml2
|
||||||
|
from saml2.client import Saml2Client
|
||||||
|
|
||||||
from .base import ClientV1RestServlet, client_path_patterns
|
from .base import ClientV1RestServlet, client_path_patterns
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
@ -93,6 +96,7 @@ class LoginRestServlet(ClientV1RestServlet):
|
||||||
self.jwt_enabled = hs.config.jwt_enabled
|
self.jwt_enabled = hs.config.jwt_enabled
|
||||||
self.jwt_secret = hs.config.jwt_secret
|
self.jwt_secret = hs.config.jwt_secret
|
||||||
self.jwt_algorithm = hs.config.jwt_algorithm
|
self.jwt_algorithm = hs.config.jwt_algorithm
|
||||||
|
self.saml2_enabled = hs.config.saml2_enabled
|
||||||
self.cas_enabled = hs.config.cas_enabled
|
self.cas_enabled = hs.config.cas_enabled
|
||||||
self.auth_handler = self.hs.get_auth_handler()
|
self.auth_handler = self.hs.get_auth_handler()
|
||||||
self.registration_handler = hs.get_registration_handler()
|
self.registration_handler = hs.get_registration_handler()
|
||||||
|
@ -104,6 +108,9 @@ class LoginRestServlet(ClientV1RestServlet):
|
||||||
flows = []
|
flows = []
|
||||||
if self.jwt_enabled:
|
if self.jwt_enabled:
|
||||||
flows.append({"type": LoginRestServlet.JWT_TYPE})
|
flows.append({"type": LoginRestServlet.JWT_TYPE})
|
||||||
|
if self.saml2_enabled:
|
||||||
|
flows.append({"type": LoginRestServlet.SSO_TYPE})
|
||||||
|
flows.append({"type": LoginRestServlet.TOKEN_TYPE})
|
||||||
if self.cas_enabled:
|
if self.cas_enabled:
|
||||||
flows.append({"type": LoginRestServlet.SSO_TYPE})
|
flows.append({"type": LoginRestServlet.SSO_TYPE})
|
||||||
|
|
||||||
|
@ -474,6 +481,43 @@ class CasTicketServlet(ClientV1RestServlet):
|
||||||
return user, attributes
|
return user, attributes
|
||||||
|
|
||||||
|
|
||||||
|
class SSORedirectServlet(RestServlet):
|
||||||
|
PATTERNS = client_path_patterns("/login/sso/redirect")
|
||||||
|
|
||||||
|
def __init__(self, hs):
|
||||||
|
super(SSORedirectServlet, self).__init__()
|
||||||
|
self.saml2_sp_config = hs.config.saml2_sp_config
|
||||||
|
|
||||||
|
def on_GET(self, request):
|
||||||
|
args = request.args
|
||||||
|
|
||||||
|
saml_client = Saml2Client(self.saml2_sp_config)
|
||||||
|
reqid, info = saml_client.prepare_for_authenticate()
|
||||||
|
|
||||||
|
redirect_url = None
|
||||||
|
|
||||||
|
# Select the IdP URL to send the AuthN request to
|
||||||
|
for key, value in info['headers']:
|
||||||
|
if key is 'Location':
|
||||||
|
redirect_url = value
|
||||||
|
|
||||||
|
if redirect_url is None:
|
||||||
|
raise LoginError(401, "Unsuccessful SSO SAML2 redirect url response",
|
||||||
|
errcode=Codes.UNAUTHORIZED)
|
||||||
|
|
||||||
|
relay_state = "/_matrix/client/r0/login"
|
||||||
|
if b"redirectUrl" in args:
|
||||||
|
relay_state = args[b"redirectUrl"][0]
|
||||||
|
|
||||||
|
url_parts = list(urllib.parse.urlparse(redirect_url))
|
||||||
|
query = dict(urllib.parse.parse_qsl(url_parts[4]))
|
||||||
|
query.update({"RelayState": relay_state})
|
||||||
|
url_parts[4] = urllib.parse.urlencode(query)
|
||||||
|
|
||||||
|
request.redirect(urllib.parse.urlunparse(url_parts))
|
||||||
|
finish_request(request)
|
||||||
|
|
||||||
|
|
||||||
class SSOAuthHandler(object):
|
class SSOAuthHandler(object):
|
||||||
"""
|
"""
|
||||||
Utility class for Resources and Servlets which handle the response from a SSO
|
Utility class for Resources and Servlets which handle the response from a SSO
|
||||||
|
@ -549,3 +593,5 @@ def register_servlets(hs, http_server):
|
||||||
if hs.config.cas_enabled:
|
if hs.config.cas_enabled:
|
||||||
CasRedirectServlet(hs).register(http_server)
|
CasRedirectServlet(hs).register(http_server)
|
||||||
CasTicketServlet(hs).register(http_server)
|
CasTicketServlet(hs).register(http_server)
|
||||||
|
if hs.config.saml2_enabled:
|
||||||
|
SSORedirectServlet(hs).register(http_server)
|
||||||
|
|
|
@ -56,6 +56,7 @@ var show_login = function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (matrixLogin.serverAcceptsSso) {
|
if (matrixLogin.serverAcceptsSso) {
|
||||||
|
$("#sso_form").attr("action", "/_matrix/client/r0/login/sso/redirect");
|
||||||
$("#sso_flow").show();
|
$("#sso_flow").show();
|
||||||
} else if (matrixLogin.serverAcceptsCas) {
|
} else if (matrixLogin.serverAcceptsCas) {
|
||||||
$("#sso_form").attr("action", "/_matrix/client/r0/login/cas/redirect");
|
$("#sso_form").attr("action", "/_matrix/client/r0/login/cas/redirect");
|
||||||
|
@ -79,7 +80,7 @@ var fetch_info = function(cb) {
|
||||||
$.get(matrixLogin.endpoint, function(response) {
|
$.get(matrixLogin.endpoint, function(response) {
|
||||||
var serverAcceptsPassword = false;
|
var serverAcceptsPassword = false;
|
||||||
var serverAcceptsCas = false;
|
var serverAcceptsCas = false;
|
||||||
for (var i=0; i<response.flows.length; i++) {
|
for (var i = 0; i < response.flows.length; i++) {
|
||||||
var flow = response.flows[i];
|
var flow = response.flows[i];
|
||||||
if ("m.login.cas" === flow.type) {
|
if ("m.login.cas" === flow.type) {
|
||||||
matrixLogin.serverAcceptsCas = true;
|
matrixLogin.serverAcceptsCas = true;
|
||||||
|
@ -121,6 +122,7 @@ matrixLogin.onLogin = function(response) {
|
||||||
// clobber this function
|
// clobber this function
|
||||||
console.log("onLogin - This function should be replaced to proceed.");
|
console.log("onLogin - This function should be replaced to proceed.");
|
||||||
console.log(response);
|
console.log(response);
|
||||||
|
alert("Login successful!");
|
||||||
};
|
};
|
||||||
|
|
||||||
var parseQsFromUrl = function(query) {
|
var parseQsFromUrl = function(query) {
|
||||||
|
@ -143,7 +145,7 @@ var try_token = function() {
|
||||||
if (pos == -1) {
|
if (pos == -1) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
var qs = parseQsFromUrl(window.location.href.substr(pos+1));
|
var qs = parseQsFromUrl(window.location.href.substr(pos + 1));
|
||||||
|
|
||||||
var loginToken = qs.loginToken;
|
var loginToken = qs.loginToken;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue