From 1b715e0812dba3d9f2c4d3f2daa0cbd4313f8def Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Thu, 27 Nov 2014 11:37:08 -0500 Subject: [PATCH] We now handle correctly the SOGo logout when using SAML (#2376 and #2379) --- Documentation/SOGoInstallationGuide.asciidoc | 4 +- NEWS | 1 + SoObjects/SOGo/SOGoCASSession.h | 4 +- SoObjects/SOGo/SOGoCASSession.m | 4 +- SoObjects/SOGo/SOGoCache.h | 1 + SoObjects/SOGo/SOGoCache.m | 14 +++++- SoObjects/SOGo/SOGoSAML2Session.h | 6 +++ SoObjects/SOGo/SOGoSAML2Session.m | 51 ++++++++++++++------ SoObjects/SOGo/SOGoWebAuthenticator.m | 2 +- UI/MainUI/SOGoSAML2Actions.m | 18 ++++++- UI/MainUI/SOGoUserHomePage.m | 46 ++++++++++++++++++ 11 files changed, 125 insertions(+), 26 deletions(-) diff --git a/Documentation/SOGoInstallationGuide.asciidoc b/Documentation/SOGoInstallationGuide.asciidoc index 728c3d9bf..b28e512a4 100644 --- a/Documentation/SOGoInstallationGuide.asciidoc +++ b/Documentation/SOGoInstallationGuide.asciidoc @@ -453,7 +453,9 @@ the setup of your identity provider. Make sure this file is readable by the SOGo |S |SOGoSAML2LogoutEnabled |Boolean value indicated whether the "Logout" link is enabled when using -SAML2 as authentication mechanism. +SAML2 as authentication mechanism. When using this feature, SOGo will invoke +the IdP to proceed with the logout procedure. When the user clicks on the logout +button, a redirection will be made to the IdP to trigger the logout. |D |SOGoTimeZone |Parameter used to set a default time zone for users. The default diff --git a/NEWS b/NEWS index 2ee8838af..6c4e3e58f 100644 --- a/NEWS +++ b/NEWS @@ -8,6 +8,7 @@ Bug fixes - Now possible to specify the username attribute for SAML2 (SOGoSAML2LoginAttribute) (#2381) - Added support for IdP-initiated SAML2 logout (#2377) - We now generate SAML2 metadata on the fly (#2378) + - We now handle correctly the SOGo logout when using SAML (#2376 and #2379) 2.2.10 (2014-11-21) ------------------- diff --git a/SoObjects/SOGo/SOGoCASSession.h b/SoObjects/SOGo/SOGoCASSession.h index ade1cf0a5..e7d6fabb9 100644 --- a/SoObjects/SOGo/SOGoCASSession.h +++ b/SoObjects/SOGo/SOGoCASSession.h @@ -1,8 +1,6 @@ /* SOGoCASSession.h - this file is part of SOGo * - * Copyright (C) 2010 Inverse inc. - * - * Author: Wolfgang Sourdeau + * Copyright (C) 2010-2014 Inverse inc. * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/SoObjects/SOGo/SOGoCASSession.m b/SoObjects/SOGo/SOGoCASSession.m index 4d684c382..8c77df29b 100644 --- a/SoObjects/SOGo/SOGoCASSession.m +++ b/SoObjects/SOGo/SOGoCASSession.m @@ -1,8 +1,6 @@ /* SOGoCASSession.m - this file is part of SOGo * - * Copyright (C) 2010 Inverse inc. - * - * Author: Wolfgang Sourdeau + * Copyright (C) 2010-2014 Inverse inc. * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/SoObjects/SOGo/SOGoCache.h b/SoObjects/SOGo/SOGoCache.h index 648fc4b60..69ef6f942 100644 --- a/SoObjects/SOGo/SOGoCache.h +++ b/SoObjects/SOGo/SOGoCache.h @@ -132,6 +132,7 @@ - (NSDictionary *) saml2LoginDumpsForIdentifier: (NSString *) identifier; - (void) setSaml2LoginDumps: (NSDictionary *) dump forIdentifier: (NSString *) identifier; +- (void) removeSAML2LoginDumpsForIdentifier: (NSString *) identifier; // // ACL caching support diff --git a/SoObjects/SOGo/SOGoCache.m b/SoObjects/SOGo/SOGoCache.m index 557beb09a..13e171e09 100644 --- a/SoObjects/SOGo/SOGoCache.m +++ b/SoObjects/SOGo/SOGoCache.m @@ -673,11 +673,13 @@ static memcached_st *handle = NULL; { key = [NSString stringWithFormat: @"cas-ticket:%@", ticket]; [self removeValueForKey: key]; - [self debugWithFormat: @"Removed session: %@", session]; + [self debugWithFormat: @"Removed CAS session: %@", session]; } } +// // SAML2 support +// - (NSDictionary *) saml2LoginDumpsForIdentifier: (NSString *) identifier { NSString *key, *jsonString; @@ -698,6 +700,16 @@ static memcached_st *handle = NULL; [self setValue: [dump jsonRepresentation] forKey: key]; } +- (void) removeSAML2LoginDumpsForIdentifier: (NSString *) identifier +{ + NSString *key; + + key = [NSString stringWithFormat: @"saml2-login:%@", identifier]; + + [self removeValueForKey: key]; + [self debugWithFormat: @"Removed SAML2 session for identifier: %@", identifier]; +} + // // ACL caching code // diff --git a/SoObjects/SOGo/SOGoSAML2Session.h b/SoObjects/SOGo/SOGoSAML2Session.h index e08217441..0592f38e1 100644 --- a/SoObjects/SOGo/SOGoSAML2Session.h +++ b/SoObjects/SOGo/SOGoSAML2Session.h @@ -37,8 +37,12 @@ NSString *login; NSString *identifier; NSString *assertion; + NSString *identity; + NSString *session; } ++ (LassoServer *) lassoServerInContext: (WOContext *) context; + + (NSString *) metadataInContext: (WOContext *) context certificate: (NSString *) certificate; @@ -54,6 +58,8 @@ - (NSString *) login; - (NSString *) identifier; - (NSString *) assertion; +- (NSString *) identity; +- (NSString *) session; @end diff --git a/SoObjects/SOGo/SOGoSAML2Session.m b/SoObjects/SOGo/SOGoSAML2Session.m index 261b3c057..96ced1516 100644 --- a/SoObjects/SOGo/SOGoSAML2Session.m +++ b/SoObjects/SOGo/SOGoSAML2Session.m @@ -105,8 +105,7 @@ static NSMapTable *serverTable = nil; lasso_init (); } -static LassoServer * -LassoServerInContext (WOContext *context) ++ (LassoServer *) lassoServerInContext: (WOContext *) context { NSString *urlString, *metadata, *filename, *keyContent, *certContent, *idpKeyFilename, *idpCertFilename; @@ -170,7 +169,7 @@ LassoServerInContext (WOContext *context) NSString *url; GList *providers; - server = LassoServerInContext (context); + server = [SOGoSAML2Session lassoServerInContext: context]; tempLogin = lasso_login_new (server); providers = g_hash_table_get_keys (server->providers); @@ -239,6 +238,8 @@ LassoServerInContext (WOContext *context) login = nil; identifier = nil; assertion = nil; + identity = nil; + session = nil; } return self; @@ -350,7 +351,7 @@ LassoServerInContext (WOContext *context) if ((self = [self init])) { - server = LassoServerInContext (context); + server = [SOGoSAML2Session lassoServerInContext: context]; lassoLogin = lasso_login_new (server); if (saml2Dump) { @@ -358,12 +359,17 @@ LassoServerInContext (WOContext *context) ASSIGN (login, [saml2Dump objectForKey: @"login"]); ASSIGN (identifier, [saml2Dump objectForKey: @"identifier"]); ASSIGN (assertion, [saml2Dump objectForKey: @"assertion"]); - dump = [[saml2Dump objectForKey: @"identity"] UTF8String]; + + ASSIGN(identity, [saml2Dump objectForKey: @"identity"]); + dump = [identity UTF8String]; if (dump) lasso_profile_set_identity_from_dump (profile, dump); - dump = [[saml2Dump objectForKey: @"session"] UTF8String]; + + ASSIGN (session, [saml2Dump objectForKey: @"session"]); + dump = [session UTF8String]; if (dump) lasso_profile_set_session_from_dump (profile, dump); + lasso_login_accept_sso (lassoLogin); // if (rc) // [NSException raiseSAML2Exception: rc]; @@ -381,6 +387,9 @@ LassoServerInContext (WOContext *context) [login release]; [identifier release]; [assertion release]; + [identity release]; + [session release]; + [super dealloc]; } @@ -433,13 +442,23 @@ LassoServerInContext (WOContext *context) return assertion; } +- (NSString *) identity +{ + return identity; +} + +- (NSString *) session +{ + return session; +} + - (void) processAuthnResponse: (NSString *) authnResponse { lasso_error_t rc; gchar *responseData, *dump; LassoProfile *profile; - LassoIdentity *identity; - LassoSession *session; + LassoIdentity *lasso_identity; + LassoSession *lasso_session; NSString *nsDump; NSMutableDictionary *saml2Dump; @@ -463,22 +482,22 @@ LassoServerInContext (WOContext *context) profile = LASSO_PROFILE (lassoLogin); - session = lasso_profile_get_session (profile); - if (session) + lasso_session = lasso_profile_get_session (profile); + if (lasso_session) { - dump = lasso_session_dump (session); + dump = lasso_session_dump (lasso_session); nsDump = [NSString stringWithUTF8String: dump]; [saml2Dump setObject: nsDump forKey: @"session"]; - lasso_session_destroy (session); + lasso_session_destroy (lasso_session); } - identity = lasso_profile_get_identity (profile); - if (identity) + lasso_identity = lasso_profile_get_identity (profile); + if (lasso_identity) { - dump = lasso_identity_dump (identity); + dump = lasso_identity_dump (lasso_identity); nsDump = [NSString stringWithUTF8String: dump]; [saml2Dump setObject: nsDump forKey: @"identity"]; - lasso_identity_destroy (identity); + lasso_identity_destroy (lasso_identity); } [[SOGoCache sharedCache] setSaml2LoginDumps: saml2Dump diff --git a/SoObjects/SOGo/SOGoWebAuthenticator.m b/SoObjects/SOGo/SOGoWebAuthenticator.m index 6d6aacad1..bd386d035 100644 --- a/SoObjects/SOGo/SOGoWebAuthenticator.m +++ b/SoObjects/SOGo/SOGoWebAuthenticator.m @@ -1,6 +1,6 @@ /* SOGoWebAuthenticator.m - this file is part of SOGo * - * Copyright (C) 2007-2013 Inverse inc. + * Copyright (C) 2007-2014 Inverse inc. * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/UI/MainUI/SOGoSAML2Actions.m b/UI/MainUI/SOGoSAML2Actions.m index 834213493..4a92221bf 100644 --- a/UI/MainUI/SOGoSAML2Actions.m +++ b/UI/MainUI/SOGoSAML2Actions.m @@ -32,6 +32,7 @@ #import #import +#import #import #import #import @@ -65,9 +66,12 @@ return response; } +// +// +// - (WOResponse *) saml2SingleLogoutServiceAction { - NSString *userName, *value, *cookieName; + NSString *userName, *value, *cookieName, *domain, *username, *password; SOGoWebAuthenticator *auth; WOResponse *response; NSCalendarDate *date; @@ -96,6 +100,18 @@ cookieName = [auth cookieNameInContext: context]; value = [[context request] cookieValueForKey: cookieName]; creds = [auth parseCredentials: value]; + + // We first delete our memcached entry + value = [SOGoSession valueForSessionKey: [creds lastObject]]; + domain = nil; + + [SOGoSession decodeValue: value + usingKey: [creds objectAtIndex: 0] + login: &username + domain: &domain + password: &password]; + + [[SOGoCache sharedCache] removeSAML2LoginDumpsForIdentifier: password]; if ([creds count] > 1) [SOGoSession deleteValueForSessionKey: [creds objectAtIndex: 1]]; diff --git a/UI/MainUI/SOGoUserHomePage.m b/UI/MainUI/SOGoUserHomePage.m index 85e6c669c..b3527a085 100644 --- a/UI/MainUI/SOGoUserHomePage.m +++ b/UI/MainUI/SOGoUserHomePage.m @@ -33,7 +33,12 @@ #import #import + +#import #import +#if defined(SAML2_CONFIG) +#import +#endif #import #import #import @@ -304,6 +309,47 @@ redirectURL = [SOGoCASSession CASURLWithAction: @"logout" andParameters: nil]; } +#if defined(SAML2_CONFIG) + else if ([[sd authenticationType] isEqualToString: @"saml2"]) + { + NSString *username, *password, *domain, *value; + SOGoSAML2Session *saml2Session; + SOGoWebAuthenticator *auth; + LassoServer *server; + LassoLogout *logout; + NSArray *creds; + + auth = [[self clientObject] authenticatorInContext: context]; + value = [[context request] cookieValueForKey: [auth cookieNameInContext: context]]; + creds = [auth parseCredentials: value]; + + value = [SOGoSession valueForSessionKey: [creds lastObject]]; + + domain = nil; + + [SOGoSession decodeValue: value + usingKey: [creds objectAtIndex: 0] + login: &username + domain: &domain + password: &password]; + + saml2Session = [SOGoSAML2Session SAML2SessionWithIdentifier: password + inContext: context]; + + server = [SOGoSAML2Session lassoServerInContext: context]; + + logout = lasso_logout_new(server); + + lasso_profile_set_session_from_dump(LASSO_PROFILE(logout), [[saml2Session session] UTF8String]); + lasso_profile_set_identity_from_dump(LASSO_PROFILE(logout), [[saml2Session session] UTF8String]); + lasso_logout_init_request(logout, NULL, LASSO_HTTP_METHOD_REDIRECT); + lasso_logout_build_request_msg(logout); + redirectURL = [NSString stringWithFormat: @"%s", LASSO_PROFILE(logout)->msg_url]; + + // We destroy our cache entry, the session will be taken care by the caller + [[SOGoCache sharedCache] removeSAML2LoginDumpsForIdentifier: password]; + } +#endif else { container = [[self clientObject] container];