From aeabd85c90e657b8b55831bd4c318bd8a9d421cf Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Fri, 2 Nov 2012 15:31:49 -0400 Subject: [PATCH] Finalized support for SAML2 logon --- SoObjects/SOGo/SOGoSAML2Session.h | 17 ++ SoObjects/SOGo/SOGoSAML2Session.m | 354 +++++++++++++++++++++++++- SoObjects/SOGo/SOGoSession.h | 1 + SoObjects/SOGo/SOGoSession.m | 2 + SoObjects/SOGo/SOGoWebAuthenticator.h | 7 + SoObjects/SOGo/SOGoWebAuthenticator.m | 61 ++++- UI/Common/UIxPageFrame.m | 6 +- UI/MainUI/GNUmakefile.preamble | 4 + UI/MainUI/SOGoRootPage.m | 135 +++++----- UI/MainUI/SOGoSAML2Actions.m | 67 +++++ UI/MainUI/product.plist | 45 ++-- 11 files changed, 616 insertions(+), 83 deletions(-) diff --git a/SoObjects/SOGo/SOGoSAML2Session.h b/SoObjects/SOGo/SOGoSAML2Session.h index dbfbe0664..4500b64c6 100644 --- a/SoObjects/SOGo/SOGoSAML2Session.h +++ b/SoObjects/SOGo/SOGoSAML2Session.h @@ -28,13 +28,30 @@ #import +#include + @class NSString; @interface SOGoSAML2Session : NSObject { + LassoLogin *lassoLogin; + + NSString *login; + NSString *identifier; } + (NSString *) metadataInContext: (WOContext *) context; ++ (NSString *) authenticationURLInContext: (WOContext *) context; + ++ (SOGoSAML2Session *) SAML2SessionInContext: (WOContext *) context; + ++ (SOGoSAML2Session *) SAML2SessionWithIdentifier: (NSString *) newIdentifier + inContext: (WOContext *) context; + +- (void) processAuthnResponse: (NSString *) authnResponse; + +- (NSString *) login; +- (NSString *) identifier; @end diff --git a/SoObjects/SOGo/SOGoSAML2Session.m b/SoObjects/SOGo/SOGoSAML2Session.m index 57e5d8623..c02dad3e0 100644 --- a/SoObjects/SOGo/SOGoSAML2Session.m +++ b/SoObjects/SOGo/SOGoSAML2Session.m @@ -20,7 +20,18 @@ * Boston, MA 02111-1307, USA. */ +#include +#include +#include +#include +#include +#include +#include + #import +#import +#import +#import #import #import @@ -29,25 +40,154 @@ #import #import +#import "SOGoCache.h" +#import "SOGoSAML2Exceptions.h" +#import "SOGoSystemDefaults.h" + #import "SOGoSAML2Session.h" +@interface WOContext (SOGoSAML2Extension) + +- (NSString *) SAML2ServerURLString; + +@end + +@implementation WOContext (SOGoSAML2Extension) + +- (NSString *) SAML2ServerURLString +{ + NSString *appName; + NSURL *serverURL; + + appName = [[WOApplication application] name]; + serverURL = [NSURL URLWithString: [NSString stringWithFormat: @"/%@", + appName] + relativeToURL: [self serverURL]]; + + return [serverURL absoluteString]; +} + +@end + @implementation SOGoSAML2Session +static NSMapTable *serverTable = nil; + ++ (void) initialize +{ + if (!serverTable) + { + serverTable = [NSMapTable mapTableWithStrongToWeakObjects]; + [serverTable retain]; + } + lasso_init (); +} + +static LassoServer * +LassoServerInContext (WOContext *context) +{ + NSString *urlString, *metadata, *filename, *keyContent, *certContent, + *idpKeyFilename, *idpCertFilename; + LassoServer *server; + SOGoSystemDefaults *sd; + + urlString = [context SAML2ServerURLString]; + server = NSMapGet (serverTable, urlString); + if (!server) + { + sd = [SOGoSystemDefaults sharedSystemDefaults]; + + filename = [sd SAML2PrivateKeyLocation]; + if (!filename) + [NSException raise: NSInvalidArgumentException + format: @"'SAML2PrivateKeyLocation' not set"]; + keyContent = [NSString stringWithContentsOfFile: filename]; + if (!keyContent) + [NSException raise: NSGenericException + format: @"private key file '%@' could not be read", + filename]; + + filename = [sd SAML2CertificateLocation]; + if (!filename) + [NSException raise: NSInvalidArgumentException + format: @"'SAML2CertificateLocation' not set"]; + certContent = [NSString stringWithContentsOfFile: filename]; + if (!certContent) + [NSException raise: NSGenericException + format: @"certificate file '%@' could not be read", + filename]; + + metadata = [SOGoSAML2Session metadataInContext: context]; + /* FIXME: enable key password in config ? */ + server = lasso_server_new_from_buffers ([metadata UTF8String], + [keyContent UTF8String], + NULL, + [certContent UTF8String]); + + filename = [sd SAML2IdpMetadataLocation]; + idpKeyFilename = [sd SAML2IdpPublicKeyLocation]; + idpCertFilename = [sd SAML2IdpCertificateLocation]; + lasso_server_add_provider (server, LASSO_PROVIDER_ROLE_IDP, + [filename UTF8String], + [idpKeyFilename UTF8String], + [idpCertFilename UTF8String]); + NSMapInsert (serverTable, urlString, server); + } + + return server; +} + ++ (NSString *) authenticationURLInContext: (WOContext *) context +{ + lasso_error_t rc; + LassoServer *server; + LassoLogin *tempLogin; + LassoSamlp2AuthnRequest *request; + NSString *url; + GList *providers; + + server = LassoServerInContext (context); + tempLogin = lasso_login_new (server); + + providers = g_hash_table_get_keys (server->providers); + rc = lasso_login_init_authn_request (tempLogin, providers->data, LASSO_HTTP_METHOD_REDIRECT); + if (rc) + [NSException raiseSAML2Exception: rc]; + + request = LASSO_SAMLP2_AUTHN_REQUEST (LASSO_PROFILE (tempLogin)->request); + if (request->NameIDPolicy->Format) { + g_free (request->NameIDPolicy->Format); + } + request->NameIDPolicy->Format = g_strdup(LASSO_SAML2_NAME_IDENTIFIER_FORMAT_PERSISTENT); + request->NameIDPolicy->AllowCreate = 1; + request->ForceAuthn = TRUE; + request->IsPassive = FALSE; + if (request->ProtocolBinding) { + g_free (request->ProtocolBinding); + } + // request->NameIDPolicy = strdup (LASSO_LIB_NAMEID_POLICY_TYPE_FEDERATED); + // request->consent = strdup (LASSO_LIB_CONSENT_OBTAINED); + rc = lasso_login_build_authn_request_msg (tempLogin); + if (rc) + [NSException raiseSAML2Exception: rc]; + + url = [NSString stringWithUTF8String: LASSO_PROFILE (tempLogin)->msg_url]; + + g_object_unref (tempLogin); + + return url; +} + + (NSString *) metadataInContext: (WOContext *) context { - NSString *metadata, *serverURLString, *filename, *appName; + NSString *metadata, *serverURLString, *filename; NSBundle *bundle; - NSURL *serverURL; bundle = [NSBundle bundleForClass: self]; filename = [bundle pathForResource: @"SOGoSAML2Metadata" ofType: @"xml"]; if (filename) { - appName = [[WOApplication application] name]; - serverURL = [NSURL URLWithString: [NSString stringWithFormat: @"/%@/so", - appName] - relativeToURL: [context serverURL]]; - serverURLString = [serverURL absoluteString]; + serverURLString = [context SAML2ServerURLString]; metadata = [[NSString stringWithContentsOfFile: filename] stringByReplacingString: @"%{base_url}" withString: serverURLString]; @@ -58,4 +198,204 @@ return metadata; } +- (id) init +{ + if ((self = [super init])) + { + lassoLogin = NULL; + } + + return self; +} + +- (void) _updateDataFromLogin +{ + // LassoSamlp2Response *response; + LassoSaml2Assertion *assertion; + GList *statementList, *attributeList; + LassoSaml2AttributeStatement *statement; + LassoSaml2Attribute *attribute; + LassoSaml2AttributeValue *value; + LassoMiscTextNode *textNode; + LassoSaml2NameID *nameIdentifier; + + NSLog (@"lassoLogin: class = %s", g_type_name_from_instance (lassoLogin)); + + assertion = LASSO_SAML2_ASSERTION (lasso_login_get_assertion (lassoLogin)); + if (assertion) + { + /* deduce user login */ + [login release]; + login = nil; + + statementList = assertion->AttributeStatement; + while (!login && statementList) + { + statement = LASSO_SAML2_ATTRIBUTE_STATEMENT (statementList->data); + attributeList = statement->Attribute; + while (!login && attributeList) + { + attribute = LASSO_SAML2_ATTRIBUTE (attributeList->data); + if (strcmp (attribute->Name, "uid") == 0) + { + value = LASSO_SAML2_ATTRIBUTE_VALUE (attribute->AttributeValue->data); + textNode = value->any->data; + login = [NSString stringWithUTF8String: textNode->content]; + [login retain]; + } + else + attributeList = attributeList->next; + } + statementList = statementList->next; + } + } + + nameIdentifier + = LASSO_SAML2_NAME_ID (LASSO_PROFILE (lassoLogin)->nameIdentifier); + if (nameIdentifier) + { + /* deduce session id */ + [identifier release]; + identifier = [NSString stringWithUTF8String: nameIdentifier->content]; + [identifier retain]; + } +} + +- (id) _initWithDump: (NSDictionary *) saml2Dump + inContext: (WOContext *) context +{ + // lasso_error_t rc; + LassoServer *server; + LassoProfile *profile; + const gchar *dump; + + if ((self = [self init])) + { + server = LassoServerInContext (context); + lassoLogin = lasso_login_new (server); + if (saml2Dump) + { + profile = LASSO_PROFILE (lassoLogin); + ASSIGN (login, [saml2Dump objectForKey: @"login"]); + ASSIGN (identifier, [saml2Dump objectForKey: @"identifier"]); + dump = [[saml2Dump objectForKey: @"identity"] UTF8String]; + if (dump) + lasso_profile_set_identity_from_dump (profile, dump); + dump = [[saml2Dump objectForKey: @"session"] UTF8String]; + if (dump) + lasso_profile_set_session_from_dump (profile, dump); + lasso_login_accept_sso (lassoLogin); + // if (rc) + // [NSException raiseSAML2Exception: rc]; + [self _updateDataFromLogin]; + } + } + + return self; +} + +- (void) dealloc +{ + if (lassoLogin) + g_object_unref (lassoLogin); + [login release]; + [identifier release]; + [super dealloc]; +} + ++ (SOGoSAML2Session *) _SAML2SessionWithDump: (NSDictionary *) saml2Dump + inContext: (WOContext *) context +{ + SOGoSAML2Session *newSession; + + newSession = [[self alloc] _initWithDump: saml2Dump inContext: context]; + [newSession autorelease]; + + return newSession; +} + ++ (SOGoSAML2Session *) SAML2SessionInContext: (WOContext *) context +{ + return [self _SAML2SessionWithDump: nil inContext: context]; +} + ++ (SOGoSAML2Session *) SAML2SessionWithIdentifier: (NSString *) identifier + inContext: (WOContext *) context +{ + SOGoSAML2Session *session = nil; + NSDictionary *saml2Dump; + + if (identifier) + { + saml2Dump = [[SOGoCache sharedCache] + saml2LoginDumpsForIdentifier: identifier]; + if (saml2Dump) + session = [self _SAML2SessionWithDump: saml2Dump + inContext: context]; + } + + return session; +} + +- (NSString *) login +{ + return login; +} + +- (NSString *) identifier +{ + return identifier; +} + +- (void) processAuthnResponse: (NSString *) authnResponse +{ + lasso_error_t rc; + gchar *responseData, *dump; + LassoProfile *profile; + LassoIdentity *identity; + LassoSession *session; + NSString *nsDump; + NSMutableDictionary *saml2Dump; + + responseData = strdup ([authnResponse UTF8String]); + + rc = lasso_login_process_authn_response_msg (lassoLogin, responseData); + if (rc) + [NSException raiseSAML2Exception: rc]; + + rc = lasso_login_accept_sso (lassoLogin); + if (rc) + [NSException raiseSAML2Exception: rc]; + + [self _updateDataFromLogin]; + + saml2Dump = [NSMutableDictionary dictionary]; + [saml2Dump setObject: login forKey: @"login"]; + [saml2Dump setObject: identifier forKey: @"identifier"]; + + profile = LASSO_PROFILE (lassoLogin); + + session = lasso_profile_get_session (profile); + if (session) + { + dump = lasso_session_dump (session); + nsDump = [NSString stringWithUTF8String: dump]; + [saml2Dump setObject: nsDump forKey: @"session"]; + lasso_session_destroy (session); + } + + identity = lasso_profile_get_identity (profile); + if (identity) + { + dump = lasso_identity_dump (identity); + nsDump = [NSString stringWithUTF8String: dump]; + [saml2Dump setObject: nsDump forKey: @"identity"]; + lasso_identity_destroy (identity); + } + + [[SOGoCache sharedCache] setSaml2LoginDumps: saml2Dump + forIdentifier: identifier]; + free (responseData); +} + @end diff --git a/SoObjects/SOGo/SOGoSession.h b/SoObjects/SOGo/SOGoSession.h index f0b5fc128..42e2ddd9a 100644 --- a/SoObjects/SOGo/SOGoSession.h +++ b/SoObjects/SOGo/SOGoSession.h @@ -43,6 +43,7 @@ login: (NSString **) theLogin domain: (NSString **) theDomain password: (NSString **) thePassword; + @end #endif diff --git a/SoObjects/SOGo/SOGoSession.m b/SoObjects/SOGo/SOGoSession.m index 1c8f9abbd..e2137349f 100644 --- a/SoObjects/SOGo/SOGoSession.m +++ b/SoObjects/SOGo/SOGoSession.m @@ -26,6 +26,8 @@ #include "SOGoCache.h" +#import + #import #import #import diff --git a/SoObjects/SOGo/SOGoWebAuthenticator.h b/SoObjects/SOGo/SOGoWebAuthenticator.h index 870a4c416..5a3533c70 100644 --- a/SoObjects/SOGo/SOGoWebAuthenticator.h +++ b/SoObjects/SOGo/SOGoWebAuthenticator.h @@ -32,6 +32,9 @@ @class NSString; +@class WOContext; +@class WOCookie; + @class SOGoUser; @interface SOGoWebAuthenticator : SoCookieAuthenticator @@ -45,6 +48,10 @@ expire: (int *) _expire grace: (int *) _grace; +- (WOCookie *) cookieWithUsername: (NSString *) username + andPassword: (NSString *) password + inContext: (WOContext *) context; + @end #endif /* _SOGOWEBAUTHENTICATOR_H_ */ diff --git a/SoObjects/SOGo/SOGoWebAuthenticator.m b/SoObjects/SOGo/SOGoWebAuthenticator.m index ac35d31e2..521349742 100644 --- a/SoObjects/SOGo/SOGoWebAuthenticator.m +++ b/SoObjects/SOGo/SOGoWebAuthenticator.m @@ -32,6 +32,7 @@ #import #import #import +#import #import #import #import @@ -47,7 +48,9 @@ #import "SOGoSystemDefaults.h" #import "SOGoUser.h" #import "SOGoUserManager.h" - +#if defined(SAML2_CONFIG) +#import "SOGoSAML2Session.h" +#endif #import "SOGoWebAuthenticator.h" @implementation SOGoWebAuthenticator @@ -107,11 +110,13 @@ { SOGoCASSession *session; SOGoSystemDefaults *sd; + NSString *authenticationType; BOOL rc; sd = [SOGoSystemDefaults sharedSystemDefaults]; - if ([[sd authenticationType] isEqualToString: @"cas"]) + authenticationType = [sd authenticationType]; + if ([authenticationType isEqualToString: @"cas"]) { session = [SOGoCASSession CASSessionWithIdentifier: _pwd fromProxy: NO]; if (session) @@ -119,6 +124,18 @@ else rc = NO; } +#if defined(SAML2_CONFIG) + else if ([authenticationType isEqualToString: @"saml2"]) + { + SOGoSAML2Session *saml2Session; + WOContext *context; + + context = [[WOApplication application] context]; + saml2Session = [SOGoSAML2Session SAML2SessionWithIdentifier: _pwd + inContext: context]; + rc = [[saml2Session login] isEqualToString: _login]; + } +#endif /* SAML2_CONFIG */ else rc = [[SOGoUserManager sharedUserManager] checkLogin: _login password: _pwd @@ -314,4 +331,44 @@ [response addCookie: authCookie]; } +- (WOCookie *) cookieWithUsername: (NSString *) username + andPassword: (NSString *) password + inContext: (WOContext *) context +{ + WOCookie *authCookie; + NSString *cookieValue, *cookieString, *appName, *sessionKey, *userKey, *securedPassword; + + // + // We create a new cookie - thus we create a new session + // associated to the user. For security, we generate: + // + // A- a session key + // B- a user key + // + // In memcached, the session key will be associated to the user's password + // which will be XOR'ed with the user key. + // + sessionKey = [SOGoSession generateKeyForLength: 16]; + userKey = [SOGoSession generateKeyForLength: 64]; + + NSString *value = [NSString stringWithFormat: @"%@:%@", username, password]; + securedPassword = [SOGoSession securedValue: value usingKey: userKey]; + + + [SOGoSession setValue: securedPassword forSessionKey: sessionKey]; + + //cookieString = [NSString stringWithFormat: @"%@:%@", + // username, password]; + cookieString = [NSString stringWithFormat: @"%@:%@", + userKey, sessionKey]; + cookieValue = [NSString stringWithFormat: @"basic %@", + [cookieString stringByEncodingBase64]]; + authCookie = [WOCookie cookieWithName: [self cookieNameInContext: context] + value: cookieValue]; + appName = [[context request] applicationName]; + [authCookie setPath: [NSString stringWithFormat: @"/%@/", appName]]; + + return authCookie; +} + @end /* SOGoWebAuthenticator */ diff --git a/UI/Common/UIxPageFrame.m b/UI/Common/UIxPageFrame.m index 60d9bca41..215088a55 100644 --- a/UI/Common/UIxPageFrame.m +++ b/UI/Common/UIxPageFrame.m @@ -485,13 +485,17 @@ BOOL canLogoff; id auth; SOGoSystemDefaults *sd; + NSString *authType; auth = [[self clientObject] authenticatorInContext: context]; if ([auth respondsToSelector: @selector (cookieNameInContext:)]) { sd = [SOGoSystemDefaults sharedSystemDefaults]; - if ([[sd authenticationType] isEqualToString: @"cas"]) + authType = [sd authenticationType]; + if ([authType isEqualToString: @"cas"]) canLogoff = [sd CASLogoutEnabled]; + else if ([authType isEqualToString: @"saml2"]) + canLogoff = [sd SAML2LogoutEnabled]; else canLogoff = [[auth cookieNameInContext: context] length] > 0; } diff --git a/UI/MainUI/GNUmakefile.preamble b/UI/MainUI/GNUmakefile.preamble index 1f7acaf9c..ec6ba0c4f 100644 --- a/UI/MainUI/GNUmakefile.preamble +++ b/UI/MainUI/GNUmakefile.preamble @@ -4,3 +4,7 @@ ADDITIONAL_CPPFLAGS += \ -DSOGO_MAJOR_VERSION=$(MAJOR_VERSION) \ -DSOGO_MINOR_VERSION=$(MINOR_VERSION) \ -DSOGO_SUBMINOR_VERSION=$(SUBMINOR_VERSION) + +ifeq ($(HAS_LIBRARY_lasso), yes) +ADDITIONAL_CPPFLAGS += $(LASSO_CFLAGS) +endif diff --git a/UI/MainUI/SOGoRootPage.m b/UI/MainUI/SOGoRootPage.m index 1d2ed529d..52508de50 100644 --- a/UI/MainUI/SOGoRootPage.m +++ b/UI/MainUI/SOGoRootPage.m @@ -47,6 +47,9 @@ #import #import #import +#if defined(SAML2_CONFIG) +#import +#endif /* SAML2_ENABLE */ #import #import #import @@ -100,46 +103,6 @@ return ([[self cookieUsername] length]); } -- (WOCookie *) _cookieWithUsername: (NSString *) username - andPassword: (NSString *) password - forAuthenticator: (SOGoWebAuthenticator *) auth -{ - WOCookie *authCookie; - NSString *cookieValue, *cookieString, *appName, *sessionKey, *userKey, *securedPassword; - - // - // We create a new cookie - thus we create a new session - // associated to the user. For security, we generate: - // - // A- a session key - // B- a user key - // - // In memcached, the session key will be associated to the user's password - // which will be XOR'ed with the user key. - // - sessionKey = [SOGoSession generateKeyForLength: 16]; - userKey = [SOGoSession generateKeyForLength: 64]; - - NSString *value = [NSString stringWithFormat: @"%@:%@", username, password]; - securedPassword = [SOGoSession securedValue: value usingKey: userKey]; - - - [SOGoSession setValue: securedPassword forSessionKey: sessionKey]; - - //cookieString = [NSString stringWithFormat: @"%@:%@", - // username, password]; - cookieString = [NSString stringWithFormat: @"%@:%@", - userKey, sessionKey]; - cookieValue = [NSString stringWithFormat: @"basic %@", - [cookieString stringByEncodingBase64]]; - authCookie = [WOCookie cookieWithName: [auth cookieNameInContext: context] - value: cookieValue]; - appName = [[context request] applicationName]; - [authCookie setPath: [NSString stringWithFormat: @"/%@/", appName]]; - - return authCookie; -} - - (WOCookie *) _cookieWithUsername: (NSString *) username { WOCookie *loginCookie; @@ -172,7 +135,8 @@ return loginCookie; } -- (WOCookie *) _casLocationCookie: (BOOL) cookieReset +- (WOCookie *) _authLocationCookie: (BOOL) cookieReset + withName: (NSString *) cookieName { WOCookie *locationCookie; NSString *appName; @@ -180,7 +144,7 @@ NSCalendarDate *date; rq = [context request]; - locationCookie = [WOCookie cookieWithName: @"cas-location" value: [rq uri]]; + locationCookie = [WOCookie cookieWithName: cookieName value: [rq uri]]; appName = [rq applicationName]; [locationCookie setPath: [NSString stringWithFormat: @"/%@/", appName]]; if (cookieReset) @@ -261,9 +225,9 @@ username = [NSString stringWithFormat: @"%@@%@", username, domain]; } - authCookie = [self _cookieWithUsername: username - andPassword: password - forAuthenticator: auth]; + authCookie = [auth cookieWithUsername: username + andPassword: password + inContext: context]; [response addCookie: authCookie]; supportedLanguages = [[SOGoSystemDefaults sharedSystemDefaults] @@ -357,14 +321,15 @@ { auth = [[WOApplication application] authenticatorInContext: context]; - casCookie = [self _cookieWithUsername: login - andPassword: [casSession identifier] - forAuthenticator: auth]; + casCookie = [auth cookieWithUsername: login + andPassword: [casSession identifier] + inContext: context]; [casSession updateCache]; newLocation = [rq cookieValueForKey: @"cas-location"]; /* login callback, we expire the "cas-location" cookie, created below */ - casLocationCookie = [self _casLocationCookie: YES]; + casLocationCookie = [self _authLocationCookie: YES + withName: @"cas-location"]; } } } @@ -388,7 +353,8 @@ newLocation = [SOGoCASSession CASURLWithAction: @"login" andParameters: [self _casRedirectKeys]]; - casLocationCookie = [self _casLocationCookie: NO]; + casLocationCookie = [self _authLocationCookie: NO + withName: @"cas-location"]; } response = [self redirectToLocation: newLocation]; if (casCookie) @@ -399,6 +365,51 @@ return response; } +#if defined(SAML2_CONFIG) +- (id ) _saml2DefaultAction +{ + WOResponse *response; + NSString *login, *newLocation, *oldLocation; + WOCookie *saml2LocationCookie; + WORequest *rq; + + saml2LocationCookie = nil; + + newLocation = nil; + + login = [[context activeUser] login]; + if ([login isEqualToString: @"anonymous"]) + login = nil; + + if (login) + { + rq = [context request]; + newLocation = [rq cookieValueForKey: @"saml2-location"]; + if (newLocation) + saml2LocationCookie = [self _authLocationCookie: YES + withName: @"saml2-location"]; + else + { + oldLocation = [[self clientObject] baseURLInContext: context]; + newLocation = [NSString stringWithFormat: @"%@%@", + oldLocation, [login stringByEscapingURL]]; + } + } + else + { + newLocation = [SOGoSAML2Session authenticationURLInContext: context]; + saml2LocationCookie = [self _authLocationCookie: NO + withName: @"saml2-location"]; + } + + response = [self redirectToLocation: newLocation]; + if (saml2LocationCookie) + [response addCookie: saml2LocationCookie]; + + return response; +} +#endif /* SAML2_CONFIG */ + - (id ) _standardDefaultAction { NSObject *response; @@ -433,13 +444,21 @@ - (id ) defaultAction { - SOGoSystemDefaults *sd; + NSString *authenticationType; + id result; - sd = [SOGoSystemDefaults sharedSystemDefaults]; + authenticationType = [[SOGoSystemDefaults sharedSystemDefaults] + authenticationType]; + if ([authenticationType isEqualToString: @"cas"]) + result = [self _casDefaultAction]; +#if defined(SAML2_CONFIG) + else if ([authenticationType isEqualToString: @"saml2"]) + result = [self _saml2DefaultAction]; +#endif /* SAML2_CONFIG */ + else + result = [self _standardDefaultAction]; - return ([[sd authenticationType] isEqualToString: @"cas"] - ? [self _casDefaultAction] - : [self _standardDefaultAction]); + return result; } - (BOOL) isPublicInContext: (WOContext *) localContext @@ -553,9 +572,9 @@ } response = [self responseWith204]; - authCookie = [self _cookieWithUsername: username - andPassword: newPassword - forAuthenticator: auth]; + authCookie = [auth cookieWithUsername: username + andPassword: newPassword + inContext: context]; [response addCookie: authCookie]; } else diff --git a/UI/MainUI/SOGoSAML2Actions.m b/UI/MainUI/SOGoSAML2Actions.m index f08325c3c..d081f63d2 100644 --- a/UI/MainUI/SOGoSAML2Actions.m +++ b/UI/MainUI/SOGoSAML2Actions.m @@ -20,11 +20,22 @@ * Boston, MA 02111-1307, USA. */ +#import +#import + +#import #import +#import +#import +#import #import +#import #import +#import +#import #import +#import @interface SOGoSAML2Actions : WODirectAction @end @@ -47,4 +58,60 @@ return response; } +- (WOCookie *) _authLocationResetCookieWithName: (NSString *) cookieName +{ + WOCookie *locationCookie; + NSString *appName; + WORequest *rq; + NSCalendarDate *date; + + rq = [context request]; + locationCookie = [WOCookie cookieWithName: cookieName value: [rq uri]]; + appName = [rq applicationName]; + [locationCookie setPath: [NSString stringWithFormat: @"/%@/", appName]]; + date = [NSCalendarDate calendarDate]; + [date setTimeZone: [NSTimeZone timeZoneWithAbbreviation: @"GMT"]]; + [locationCookie setExpires: [date yesterday]]; + + return locationCookie; +} + +- (WOResponse *) saml2SignOnPOSTAction +{ + WORequest *rq; + WOResponse *response; + SoApplication *application; + SOGoSAML2Session *newSession; + WOCookie *authCookie; + NSString *login, *oldLocation, *newLocation; + SOGoWebAuthenticator *auth; + + rq = [context request]; + if ([[rq method] isEqualToString: @"POST"]) + { + newSession = [SOGoSAML2Session SAML2SessionInContext: context]; + [newSession processAuthnResponse: [rq formValueForKey: @"SAMLResponse"]]; + login = [newSession login]; + + application = [SoApplication application]; + auth = [application authenticatorInContext: context]; + authCookie = [auth cookieWithUsername: login + andPassword: [newSession identifier] + inContext: context]; + + oldLocation = [[context clientObject] baseURLInContext: context]; + newLocation = [NSString stringWithFormat: @"%@/%@", + oldLocation, [login stringByEscapingURL]]; + + response = [context response]; + [response setStatus: 302]; + [response setHeader: @"text/plain; charset=utf-8" + forKey: @"content-type"]; + [response setHeader: newLocation forKey: @"location"]; + [response addCookie: authCookie]; + } + + return response; +} + @end diff --git a/UI/MainUI/product.plist b/UI/MainUI/product.plist index 4bbb791db..399eaad05 100644 --- a/UI/MainUI/product.plist +++ b/UI/MainUI/product.plist @@ -124,21 +124,36 @@ actionClass = "SOGoSAML2Actions"; actionName = "saml2Metadata"; }; - /* crash = { - protectedBy = ""; - pageName = "SOGoRootPage"; - actionName = "crash"; - }; - exception = { - protectedBy = ""; - pageName = "SOGoRootPage"; - actionName = "exception"; - }; - raisedException = { - protectedBy = ""; - pageName = "SOGoRootPage"; - actionName = "raisedException"; - }; */ + saml2-signon-post = { + protectedBy = ""; + actionClass = "SOGoSAML2Actions"; + actionName = "saml2SignOnPOST"; + }; + /* saml2-signon-redirect = { + protectedBy = ""; + actionClass = "SOGoSAML2Actions"; + actionName = "saml2SignOnRedirect"; + }; + saml2-signon-soap = { + protectedBy = ""; + actionClass = "SOGoSAML2Actions"; + actionName = "saml2SignOnSOAP"; + }; + crash = { + protectedBy = ""; + pageName = "SOGoRootPage"; + actionName = "crash"; + }; + exception = { + protectedBy = ""; + pageName = "SOGoRootPage"; + actionName = "exception"; + }; + raisedException = { + protectedBy = ""; + pageName = "SOGoRootPage"; + actionName = "raisedException"; + }; */ connect = { protectedBy = ""; pageName = "SOGoRootPage";