mirror of
https://github.com/inverse-inc/sogo.git
synced 2026-02-17 07:33:57 +00:00
opendid part2
This commit is contained in:
10
Main/SOGo.m
10
Main/SOGo.m
@@ -327,17 +327,9 @@ static BOOL debugLeaks;
|
||||
}
|
||||
|
||||
//Create mandatory openId table, if used
|
||||
if([[defaults authenticationType] isEqualToString: @"openid"])
|
||||
if([defaults hasOpenIdType])
|
||||
{
|
||||
value = [defaults stringForKey: @"OCSOpenIdURL"];
|
||||
if (value)
|
||||
[[fm openIdFolder] createFolderIfNotExists];
|
||||
else
|
||||
{
|
||||
[self errorWithFormat: @"No value specified for 'OCSOpenIdURL' for auth mode %@", [defaults authenticationType]];
|
||||
ok = NO;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -132,14 +132,24 @@
|
||||
|
||||
- (NGImap4ConnectionManager *) mailManager
|
||||
{
|
||||
return [NGImap4ConnectionManager defaultConnectionManager];
|
||||
SOGoSystemDefaults *sd;
|
||||
NSString *imapAuthMech, *domain;
|
||||
|
||||
domain = [[[self context] activeUser] loginDomain];
|
||||
sd = [SOGoSystemDefaults sharedSystemDefaults];
|
||||
if([sd doesLoginTypeByDomain])
|
||||
imapAuthMech = [sd getImapAuthMechForDomain: domain];
|
||||
else
|
||||
imapAuthMech = nil;
|
||||
|
||||
return [NGImap4ConnectionManager defaultConnectionManager: imapAuthMech];
|
||||
}
|
||||
|
||||
- (NGImap4Connection *) _createIMAP4Connection
|
||||
{
|
||||
NGImap4ConnectionManager *manager;
|
||||
NGImap4Connection *newConnection;
|
||||
NSString *password;
|
||||
NSString *password, *domain;
|
||||
NGInternetSocketAddress *host;
|
||||
SOGoSystemDefaults *sd;
|
||||
BOOL usesSSO;
|
||||
@@ -152,12 +162,15 @@
|
||||
host = [NGInternetSocketAddress addressWithPort:0 onHost:[[self imap4URL] host]];
|
||||
|
||||
sd = [SOGoSystemDefaults sharedSystemDefaults];
|
||||
usesSSO = [sd isSsoUsed];
|
||||
|
||||
domain = [[[self context] activeUser] loginDomain];
|
||||
usesSSO = [sd isSsoUsed: domain];
|
||||
|
||||
if (![[[self mailAccountFolder] nameInContainer] isEqualToString: @"0"] &&
|
||||
usesSSO &&
|
||||
[host isLocalhost])
|
||||
{
|
||||
//
|
||||
[self errorWithFormat: @"Trying to use localhost for additional IMAP account - aborting."];
|
||||
return nil;
|
||||
}
|
||||
@@ -229,9 +242,7 @@
|
||||
if (!login)
|
||||
login = [[[[self container] context] activeUser] login];
|
||||
|
||||
cacheKey = [NSString stringWithFormat: @"%@+%@",
|
||||
login,
|
||||
[[self mailAccountFolder] nameInContainer]];
|
||||
cacheKey = [NSString stringWithFormat: @"%@+%@", login, [[self mailAccountFolder] nameInContainer]];
|
||||
imap4 = [sogoCache imap4ConnectionForKey: cacheKey];
|
||||
if (!imap4)
|
||||
{
|
||||
|
||||
@@ -95,6 +95,7 @@
|
||||
return keysWithFormat;
|
||||
}
|
||||
|
||||
|
||||
- (NSComparisonResult) caseInsensitiveDisplayNameCompare: (NSDictionary *) theDictionary
|
||||
{
|
||||
return [[self objectForKey: @"cn"] caseInsensitiveCompare: [theDictionary objectForKey: @"cn"]];
|
||||
|
||||
@@ -80,7 +80,10 @@ static int cssEscapingCount;
|
||||
{
|
||||
hostR = [self rangeOfString: @"://"];
|
||||
locationR = [[self substringFromIndex: (hostR.location + hostR.length)] rangeOfString: @"/"];
|
||||
if(locationR.location != NSNotFound)
|
||||
newURL = [self substringFromIndex: (hostR.location + hostR.length + locationR.location)];
|
||||
else
|
||||
newURL = @"";
|
||||
}
|
||||
|
||||
return newURL;
|
||||
|
||||
@@ -62,6 +62,7 @@ extern NSString *SOGoDefaultsSourceUnmutableSource;
|
||||
|
||||
- (void) setBool: (BOOL) value forKey: (NSString *) key;
|
||||
- (BOOL) boolForKey: (NSString *) key;
|
||||
- (BOOL) boolForKey: (NSString *) key andDict: (NSDictionary*) _dict;
|
||||
|
||||
- (void) setFloat: (float) value forKey: (NSString *) key;
|
||||
- (float) floatForKey: (NSString *) key;
|
||||
|
||||
@@ -164,6 +164,29 @@ static Class NSStringKlass = Nil;
|
||||
return value;
|
||||
}
|
||||
|
||||
- (BOOL) boolForKey: (NSString *) key andDict: (NSDictionary*) _dict
|
||||
{
|
||||
id boolForKey;
|
||||
BOOL value;
|
||||
|
||||
boolForKey = [_dict objectForKey: key];
|
||||
if (boolForKey)
|
||||
{
|
||||
if ([boolForKey respondsToSelector: @selector (boolValue)])
|
||||
value = [boolForKey boolValue];
|
||||
else
|
||||
{
|
||||
[self warnWithFormat: @"expected a boolean for '%@' (ignored)",
|
||||
key];
|
||||
value = NO;
|
||||
}
|
||||
}
|
||||
else
|
||||
value = NO;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
- (void) setFloat: (float) value
|
||||
forKey: (NSString *) key
|
||||
{
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
https://openid.net/developers/how-connect-works/ */
|
||||
|
||||
#import <NGObjWeb/WOResponse.h>
|
||||
#import <SOGo/SOGoObject.h>
|
||||
|
||||
|
||||
@class NSString;
|
||||
@@ -33,7 +34,7 @@
|
||||
@class NSJSONSerialization;
|
||||
|
||||
|
||||
@interface SOGoOpenIdSession : NSObject
|
||||
@interface SOGoOpenIdSession : SOGoObject
|
||||
{
|
||||
//For cache
|
||||
BOOL cacheUpdateNeeded;
|
||||
@@ -48,6 +49,9 @@
|
||||
NSString *openIdEmailParam;
|
||||
BOOL openIdEnableRefreshToken;
|
||||
|
||||
NSString *forDomain;
|
||||
|
||||
|
||||
//From request to well-known/configuration
|
||||
NSString *authorizationEndpoint;
|
||||
NSString *tokenEndpoint;
|
||||
@@ -66,16 +70,20 @@
|
||||
}
|
||||
|
||||
+ (BOOL) checkUserConfig;
|
||||
+ (SOGoOpenIdSession *) OpenIdSession;
|
||||
+ (SOGoOpenIdSession *) OpenIdSession: (NSString *) _domain;
|
||||
+ (SOGoOpenIdSession *) OpenIdSessionWithConfig: (NSDictionary *) _config;
|
||||
+ (SOGoOpenIdSession *) OpenIdSessionWithToken: (NSString *) token domain: (NSString *) _domain;
|
||||
+ (SOGoOpenIdSession *) OpenIdSessionWithTokenAndConfig: (NSString *) token config: (NSDictionary *) _config;
|
||||
+ (void) deleteValueForSessionKey: (NSString *) theSessionKey;
|
||||
|
||||
- (void) initialize;
|
||||
- (void) initializeWithConfig: (NSDictionary *) _config;
|
||||
- (BOOL) sessionIsOK;
|
||||
- (WOResponse *) _performOpenIdRequest: (NSString *) endpoint
|
||||
method: (NSString *) method
|
||||
headers: (NSDictionary *) headers
|
||||
body: (NSData *) body;
|
||||
- (NSMutableDictionary *) fecthConfiguration;
|
||||
- (NSMutableDictionary *) fecthConfiguration: (NSString *) _domain;
|
||||
- (void) setAccessToken;
|
||||
- (NSString *) getRefreshToken;
|
||||
- (NSString *) getToken;
|
||||
|
||||
@@ -23,6 +23,9 @@
|
||||
#import <NGObjWeb/WOResponse.h>
|
||||
#import <NGExtensions/NSObject+Logs.h>
|
||||
|
||||
#import <SOGo/SOGoUser.h>
|
||||
|
||||
|
||||
#import <GDLContentStore/GCSOpenIdFolder.h>
|
||||
#import <GDLContentStore/GCSFolderManager.h>
|
||||
|
||||
@@ -52,32 +55,67 @@ static BOOL SOGoOpenIDDebugEnabled = YES;
|
||||
return ([sd openIdConfigUrl] && [sd openIdScope] && [sd openIdClient] && [sd openIdClientSecret]);
|
||||
}
|
||||
|
||||
- (void) initialize
|
||||
- (void) initializeWithConfig: (NSDictionary *) _config
|
||||
{
|
||||
SOGoSystemDefaults *sd;
|
||||
id refreshTokenBool;
|
||||
|
||||
// //From sogo.conf
|
||||
// openIdConfigUrl = nil;
|
||||
// openIdScope = nil;
|
||||
// openIdClient = nil;
|
||||
// openIdClientSecret = nil;
|
||||
if([_config objectForKey: @"SOGoOpenIdConfigUrl"] &&
|
||||
[_config objectForKey: @"SOGoOpenIdScope"] &&
|
||||
[_config objectForKey: @"SOGoOpenIdClient"] &&
|
||||
[_config objectForKey: @"SOGoOpenIdClientSecret"])
|
||||
{
|
||||
openIdConfigUrl = [_config objectForKey: @"SOGoOpenIdConfigUrl"];
|
||||
openIdScope = [_config objectForKey: @"SOGoOpenIdScope"];
|
||||
openIdClient = [_config objectForKey: @"SOGoOpenIdClient"];
|
||||
openIdClientSecret = [_config objectForKey: @"SOGoOpenIdClientSecret"];
|
||||
openIdEmailParam = [_config objectForKey: @"SOGoOpenIdEmailParam"];
|
||||
|
||||
// //From request to well-known/configuration
|
||||
// //SHoud be ste in sogo.cong in case of oauth
|
||||
// authorizationEndpoint = nil;
|
||||
// tokenEndpoint = nil;
|
||||
// introspectionEndpoint = nil;
|
||||
// userinfoEndpoint = nil;
|
||||
// endSessionEndpoint = nil;
|
||||
// revocationEndpoint = nil;
|
||||
openIdEnableRefreshToken = NO;
|
||||
refreshTokenBool = [_config objectForKey: @"SOGoOpenIdEnableRefreshToken"];
|
||||
if (refreshTokenBool && [refreshTokenBool respondsToSelector: @selector (boolValue)])
|
||||
openIdEnableRefreshToken = [refreshTokenBool boolValue];
|
||||
|
||||
// //Access token
|
||||
// accessToken = nil;
|
||||
userTokenInterval = [_config objectForKey: @"SOGoOpenIdTokenCheckInterval"];
|
||||
[self _loadSessionFromCache: forDomain];
|
||||
|
||||
if(cacheUpdateNeeded)
|
||||
{
|
||||
[self fecthConfiguration: forDomain];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
[self errorWithFormat: @"Missing parameters from sogo.conf"];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) initialize: (NSString*) _domain
|
||||
{
|
||||
SOGoSystemDefaults *sd;
|
||||
NSDictionary *config;
|
||||
NSString *type;
|
||||
|
||||
sd = [SOGoSystemDefaults sharedSystemDefaults];
|
||||
SOGoOpenIDDebugEnabled = [sd openIdDebugEnabled];
|
||||
openIdSessionIsOK = NO;
|
||||
if ([[self class] checkUserConfig])
|
||||
|
||||
//Check if there is a root config or config per domain
|
||||
if(_domain != nil && [sd doesLoginTypeByDomain])
|
||||
{
|
||||
forDomain = _domain;
|
||||
type = [sd getLoginTypeForDomain: _domain];
|
||||
if(type != nil && [type isEqualToString: @"openid"])
|
||||
{
|
||||
config = [sd getLoginConfigForDomain: _domain];
|
||||
[self initializeWithConfig: config];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self errorWithFormat: @"Missing parameters from sogo.conf"];
|
||||
}
|
||||
}
|
||||
else if ([[self class] checkUserConfig])
|
||||
{
|
||||
openIdConfigUrl = [sd openIdConfigUrl];
|
||||
openIdScope = [sd openIdScope];
|
||||
@@ -86,12 +124,13 @@ static BOOL SOGoOpenIDDebugEnabled = YES;
|
||||
openIdEmailParam = [sd openIdEmailParam];
|
||||
openIdEnableRefreshToken = [sd openIdEnableRefreshToken];
|
||||
userTokenInterval = [sd openIdTokenCheckInterval];
|
||||
forDomain = _domain;
|
||||
|
||||
[self _loadSessionFromCache];
|
||||
[self _loadSessionFromCache: _domain];
|
||||
|
||||
if(cacheUpdateNeeded)
|
||||
{
|
||||
[self fecthConfiguration];
|
||||
[self fecthConfiguration: _domain];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -101,6 +140,13 @@ static BOOL SOGoOpenIDDebugEnabled = YES;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString *) _get_user_domain
|
||||
{
|
||||
SOGoUser* user = [[context activeUser] login];
|
||||
NSLog(@"user is %@", user);
|
||||
|
||||
}
|
||||
|
||||
- (WOResponse *) _performOpenIdRequest: (NSString *) endpoint
|
||||
method: (NSString *) method
|
||||
headers: (NSDictionary *) headers
|
||||
@@ -113,12 +159,13 @@ static BOOL SOGoOpenIDDebugEnabled = YES;
|
||||
WOHTTPConnection *httpConnection;
|
||||
|
||||
|
||||
[self _get_user_domain];
|
||||
url = [NSURL URLWithString: endpoint];
|
||||
if (url)
|
||||
{
|
||||
if(SOGoOpenIDDebugEnabled)
|
||||
{
|
||||
NSLog(@"OpenId perform request: %@ %@", method, [endpoint hostlessURL]);
|
||||
NSLog(@"OpenId perform request: %@ %@", method, endpoint);
|
||||
NSLog(@"OpenId perform request, headers %@", headers);
|
||||
if(body)
|
||||
NSLog(@"OpenId perform request: content %@", [[NSString alloc] initWithData:body encoding:NSUTF8StringEncoding]);
|
||||
@@ -156,13 +203,13 @@ static BOOL SOGoOpenIDDebugEnabled = YES;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSMutableDictionary *) fecthConfiguration
|
||||
- (NSMutableDictionary *) fecthConfiguration: (NSString*) _domain
|
||||
{
|
||||
NSString *location, *content;
|
||||
NSString *content;
|
||||
WOResponse * response;
|
||||
NSUInteger status;
|
||||
NSMutableDictionary *result;
|
||||
NSDictionary *config;
|
||||
NSDictionary *config, *headers;
|
||||
NSURL *url;
|
||||
|
||||
result = [NSMutableDictionary dictionary];
|
||||
@@ -171,9 +218,14 @@ static BOOL SOGoOpenIDDebugEnabled = YES;
|
||||
url = [NSURL URLWithString: self->openIdConfigUrl ];
|
||||
if (url)
|
||||
{
|
||||
if(_domain != nil && [_domain length] > 0)
|
||||
headers = [NSDictionary dictionaryWithObject: _domain forKey: @"sogo-user-domain"];
|
||||
else
|
||||
headers = nil;
|
||||
|
||||
response = [self _performOpenIdRequest: self->openIdConfigUrl
|
||||
method: @"GET"
|
||||
headers: nil
|
||||
headers: headers
|
||||
body: nil];
|
||||
|
||||
if (response)
|
||||
@@ -185,12 +237,17 @@ static BOOL SOGoOpenIDDebugEnabled = YES;
|
||||
config = [content objectFromJSONString];
|
||||
self->authorizationEndpoint = [config objectForKey: @"authorization_endpoint"];
|
||||
self->tokenEndpoint = [config objectForKey: @"token_endpoint"];
|
||||
self->introspectionEndpoint = [config objectForKey: @"introspection_endpoint"];
|
||||
self->userinfoEndpoint = [config objectForKey: @"userinfo_endpoint"];
|
||||
self->endSessionEndpoint = [config objectForKey: @"end_session_endpoint"];
|
||||
|
||||
//Optionnals?
|
||||
if([config objectForKey: @"introspection_endpoint"])
|
||||
self->introspectionEndpoint = [config objectForKey: @"introspection_endpoint"];
|
||||
if([config objectForKey: @"revocation_endpoint"])
|
||||
self->revocationEndpoint = [config objectForKey: @"revocation_endpoint"];
|
||||
|
||||
openIdSessionIsOK = YES;
|
||||
[self _saveSessionToCache];
|
||||
[self _saveSessionToCache: _domain];
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -206,18 +263,29 @@ static BOOL SOGoOpenIDDebugEnabled = YES;
|
||||
return result;
|
||||
}
|
||||
|
||||
+ (SOGoOpenIdSession *) OpenIdSession
|
||||
+ (SOGoOpenIdSession *) OpenIdSession: (NSString *) _domain
|
||||
{
|
||||
SOGoOpenIdSession *newSession;
|
||||
|
||||
newSession = [self new];
|
||||
[newSession autorelease];
|
||||
[newSession initialize];
|
||||
[newSession initialize: _domain];
|
||||
|
||||
return newSession;
|
||||
}
|
||||
|
||||
+ (SOGoOpenIdSession *) OpenIdSessionWithToken: (NSString *) token
|
||||
+ (SOGoOpenIdSession *) OpenIdSessionWithConfig: (NSDictionary *) _config
|
||||
{
|
||||
SOGoOpenIdSession *newSession;
|
||||
|
||||
newSession = [self new];
|
||||
[newSession autorelease];
|
||||
[newSession initializeWithConfig: _config];
|
||||
|
||||
return newSession;
|
||||
}
|
||||
|
||||
+ (SOGoOpenIdSession *) OpenIdSessionWithToken: (NSString *) token domain: (NSString *) _domain
|
||||
{
|
||||
SOGoOpenIdSession *newSession;
|
||||
|
||||
@@ -225,7 +293,7 @@ static BOOL SOGoOpenIDDebugEnabled = YES;
|
||||
{
|
||||
newSession = [self new];
|
||||
[newSession autorelease];
|
||||
[newSession initialize];
|
||||
[newSession initialize: _domain];
|
||||
|
||||
[newSession setAccessToken: token];
|
||||
}
|
||||
@@ -235,27 +303,55 @@ static BOOL SOGoOpenIDDebugEnabled = YES;
|
||||
return newSession;
|
||||
}
|
||||
|
||||
+ (SOGoOpenIdSession *) OpenIdSessionWithTokenAndConfig: (NSString *) token config: (NSDictionary *) _config
|
||||
{
|
||||
SOGoOpenIdSession *newSession;
|
||||
|
||||
if (token)
|
||||
{
|
||||
newSession = [self new];
|
||||
[newSession autorelease];
|
||||
[newSession initializeWithConfig: _config];
|
||||
|
||||
[newSession setAccessToken: token];
|
||||
}
|
||||
else
|
||||
newSession = nil;
|
||||
|
||||
return newSession;
|
||||
}
|
||||
|
||||
|
||||
- (BOOL) sessionIsOk
|
||||
{
|
||||
return self->openIdSessionIsOK;
|
||||
}
|
||||
|
||||
- (void) _loadSessionFromCache
|
||||
- (void) _loadSessionFromCache: (NSString*) _domain
|
||||
{
|
||||
SOGoCache *cache;
|
||||
NSString *jsonSession;
|
||||
NSString *jsonSession, *cacheKey;
|
||||
NSDictionary *sessionDict;
|
||||
|
||||
if(_domain != nil && [_domain length] > 0)
|
||||
cacheKey = [self->openIdConfigUrl stringByAppendingFormat: @":%@", _domain];
|
||||
else
|
||||
cacheKey = self->openIdConfigUrl;
|
||||
|
||||
cache = [SOGoCache sharedCache];
|
||||
jsonSession = [cache openIdSessionFromServer: self->openIdConfigUrl];
|
||||
jsonSession = [cache openIdSessionFromServer: cacheKey];
|
||||
if ([jsonSession length])
|
||||
{
|
||||
sessionDict = [jsonSession objectFromJSONString];
|
||||
ASSIGN (authorizationEndpoint, [sessionDict objectForKey: @"authorization_endpoint"]);
|
||||
ASSIGN (tokenEndpoint, [sessionDict objectForKey: @"token_endpoint"]);
|
||||
ASSIGN (introspectionEndpoint, [sessionDict objectForKey: @"introspection_endpoint"]);
|
||||
ASSIGN (userinfoEndpoint, [sessionDict objectForKey: @"userinfo_endpoint"]);
|
||||
ASSIGN (endSessionEndpoint, [sessionDict objectForKey: @"end_session_endpoint"]);
|
||||
|
||||
//Optionnals?
|
||||
if([sessionDict objectForKey: @"introspection_endpoint"])
|
||||
ASSIGN (introspectionEndpoint, [sessionDict objectForKey: @"introspection_endpoint"]);
|
||||
if([sessionDict objectForKey: @"revocation_endpoint"])
|
||||
ASSIGN (revocationEndpoint, [sessionDict objectForKey: @"revocation_endpoint"]);
|
||||
openIdSessionIsOK = YES;
|
||||
}
|
||||
@@ -263,24 +359,34 @@ static BOOL SOGoOpenIDDebugEnabled = YES;
|
||||
cacheUpdateNeeded = YES;
|
||||
}
|
||||
|
||||
- (void) _saveSessionToCache
|
||||
- (void) _saveSessionToCache: (NSString*) _domain
|
||||
{
|
||||
SOGoCache *cache;
|
||||
NSString *jsonSession;
|
||||
NSString *jsonSession, *cacheKey;
|
||||
NSMutableDictionary *sessionDict;
|
||||
|
||||
cache = [SOGoCache sharedCache];
|
||||
sessionDict = [NSMutableDictionary dictionary];
|
||||
[sessionDict setObject: authorizationEndpoint forKey: @"authorization_endpoint"];
|
||||
[sessionDict setObject: tokenEndpoint forKey: @"token_endpoint"];
|
||||
[sessionDict setObject: introspectionEndpoint forKey: @"introspection_endpoint"];
|
||||
[sessionDict setObject: userinfoEndpoint forKey: @"userinfo_endpoint"];
|
||||
[sessionDict setObject: endSessionEndpoint forKey: @"end_session_endpoint"];
|
||||
|
||||
//Optionnals?
|
||||
if(introspectionEndpoint)
|
||||
[sessionDict setObject: introspectionEndpoint forKey: @"introspection_endpoint"];
|
||||
if(revocationEndpoint)
|
||||
[sessionDict setObject: revocationEndpoint forKey: @"revocation_endpoint"];
|
||||
|
||||
jsonSession = [sessionDict jsonRepresentation];
|
||||
|
||||
if(_domain != nil && [_domain length] > 0)
|
||||
cacheKey = [self->openIdConfigUrl stringByAppendingFormat: @":%@", _domain];
|
||||
else
|
||||
cacheKey = self->openIdConfigUrl;
|
||||
|
||||
[cache setOpenIdSession: jsonSession
|
||||
forServer: self->openIdConfigUrl];
|
||||
forServer: cacheKey];
|
||||
}
|
||||
|
||||
|
||||
@@ -332,6 +438,8 @@ static BOOL SOGoOpenIDDebugEnabled = YES;
|
||||
logUrl = [logUrl stringByAppendingString: @"&response_type=code"];
|
||||
logUrl = [logUrl stringByAppendingFormat: @"&client_id=%@", self->openIdClient];
|
||||
logUrl = [logUrl stringByAppendingFormat: @"&redirect_uri=%@", oldLocation];
|
||||
if(self->forDomain != nil && [self->forDomain length] > 0)
|
||||
logUrl = [logUrl stringByAppendingFormat: @"&sogo_domain=%@", forDomain];
|
||||
// logurl = [self->logurl stringByAppendingFormat: @"&state=%@", state];
|
||||
|
||||
return logUrl;
|
||||
@@ -397,6 +505,10 @@ static BOOL SOGoOpenIDDebugEnabled = YES;
|
||||
form = [form stringByAppendingFormat: @"&client_secret=%@", self->openIdClientSecret];
|
||||
form = [form stringByAppendingFormat: @"&client_id=%@", self->openIdClient];
|
||||
|
||||
if(self->forDomain != nil && [self->forDomain length] > 0)
|
||||
headers = [NSDictionary dictionaryWithObjectsAndKeys: @"application/x-www-form-urlencoded", @"content-type",
|
||||
self->forDomain, @"sogo-user-domain", nil];
|
||||
else
|
||||
headers = [NSDictionary dictionaryWithObject: @"application/x-www-form-urlencoded" forKey: @"content-type"];
|
||||
|
||||
response = [self _performOpenIdRequest: location
|
||||
@@ -467,6 +579,10 @@ static BOOL SOGoOpenIDDebugEnabled = YES;
|
||||
form = [form stringByAppendingFormat: @"&client_secret=%@", self->openIdClientSecret];
|
||||
form = [form stringByAppendingFormat: @"&client_id=%@", self->openIdClient];
|
||||
|
||||
if(self->forDomain != nil && [self->forDomain length] > 0)
|
||||
headers = [NSDictionary dictionaryWithObjectsAndKeys: @"application/x-www-form-urlencoded", @"content-type",
|
||||
self->forDomain, @"sogo-user-domain", nil];
|
||||
else
|
||||
headers = [NSDictionary dictionaryWithObject: @"application/x-www-form-urlencoded" forKey: @"content-type"];
|
||||
|
||||
response = [self _performOpenIdRequest: location
|
||||
@@ -525,6 +641,11 @@ static BOOL SOGoOpenIDDebugEnabled = YES;
|
||||
if (url)
|
||||
{
|
||||
auth = [NSString stringWithFormat: @"Bearer %@", self->accessToken];
|
||||
if(self->forDomain != nil && [self->forDomain length] > 0)
|
||||
headers = [NSDictionary dictionaryWithObjectsAndKeys: @"application/x-www-form-urlencoded", @"content-type",
|
||||
self->forDomain, @"sogo-user-domain",
|
||||
auth, @"authorization", nil];
|
||||
else
|
||||
headers = [NSDictionary dictionaryWithObject: auth forKey: @"authorization"];
|
||||
|
||||
response = [self _performOpenIdRequest: location
|
||||
@@ -646,7 +767,7 @@ static BOOL SOGoOpenIDDebugEnabled = YES;
|
||||
return @"anonymous";
|
||||
}
|
||||
|
||||
- (BOOL) login: (NSString *) email
|
||||
- (NSString *) login: (NSString *) email
|
||||
{
|
||||
//Check if we need to fetch userinfo
|
||||
if(self->userTokenInterval > 0 && [self _loadUserFromCache: email])
|
||||
|
||||
@@ -247,12 +247,20 @@
|
||||
usingKey: theKey];
|
||||
|
||||
r = [decodedValue rangeOfString: @":"];
|
||||
if (r.location != NSNotFound)
|
||||
{
|
||||
*theLogin = [decodedValue substringToIndex: r.location];
|
||||
*thePassword = [decodedValue substringFromIndex: r.location+1];
|
||||
}
|
||||
else
|
||||
{
|
||||
*theLogin = nil;
|
||||
*thePassword = nil;
|
||||
}
|
||||
*theDomain = nil;
|
||||
|
||||
sd = [SOGoSystemDefaults sharedSystemDefaults];
|
||||
if ([sd enableDomainBasedUID])
|
||||
if (*theLogin &&[sd enableDomainBasedUID])
|
||||
{
|
||||
r = [*theLogin rangeOfString: @"@" options: NSBackwardsSearch];
|
||||
if (r.location != NSNotFound)
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#import <SOGo/NSDictionary+Utilities.h>
|
||||
#import <SOGo/NSString+Utilities.h>
|
||||
#import <SOGo/SOGoDomainDefaults.h>
|
||||
#import <SOGo/SOGoSystemDefaults.h>
|
||||
#import <SOGo/SOGoUser.h>
|
||||
#import <SOGo/SOGoTextTemplateFile.h>
|
||||
|
||||
@@ -697,8 +698,11 @@ static NSString *sieveScriptName = @"sogo";
|
||||
NSDictionary *result;
|
||||
NSString *login, *authname, *password;
|
||||
SOGoDomainDefaults *dd;
|
||||
SOGoSystemDefaults *sd;
|
||||
NGSieveClient *client;
|
||||
NSString *sieveServer, *sieveScheme, *sieveQuery, *imapServer;
|
||||
NSString *imapAuthMech, *userDomain;
|
||||
NSRange r;
|
||||
NSURL *url, *cUrl;
|
||||
int sievePort;
|
||||
BOOL connected;
|
||||
@@ -773,7 +777,20 @@ static NSString *sieveScriptName = @"sogo";
|
||||
url = [NSURL URLWithString: [NSString stringWithFormat: @"%@://%@:%d%@",
|
||||
sieveScheme, sieveServer, sievePort, sieveQuery]];
|
||||
|
||||
client = [[NGSieveClient alloc] initWithURL: url];
|
||||
//In case of differrent auth method for different domain, check it
|
||||
sd = [SOGoSystemDefaults sharedSystemDefaults];
|
||||
imapAuthMech = nil;
|
||||
if([sd doesLoginTypeByDomain])
|
||||
{
|
||||
r = [theUsername rangeOfString: @"@"];
|
||||
if (r.location != NSNotFound)
|
||||
{
|
||||
userDomain = [theUsername substringFromIndex: r.location+1];
|
||||
imapAuthMech = [sd getImapAuthMechForDomain: userDomain];
|
||||
}
|
||||
}
|
||||
|
||||
client = [[NGSieveClient alloc] initWithURL: url andAuthMech: imapAuthMech];
|
||||
|
||||
if (!client) {
|
||||
[self errorWithFormat: @"Sieve connection failed on %@", [url description]];
|
||||
|
||||
@@ -36,6 +36,11 @@ static const NSString *kDisableSharingCalendar = @"Calendar";
|
||||
+ (SOGoSystemDefaults *) sharedSystemDefaults;
|
||||
|
||||
- (NSArray *) domainIds;
|
||||
- (BOOL) doesLoginTypeByDomain;
|
||||
- (NSString *) getLoginTypeForDomain: (NSString*) _domain;
|
||||
- (NSString *) getLoginConfigForDomain: (NSDictionary*) _domain;
|
||||
- (NSString *) getImapAuthMechForDomain: (NSString*) _domain;
|
||||
- (NSString *) getSmtpAuthMechForDomain: (NSString*) _domain;
|
||||
- (BOOL) forbidUnknownDomainsAuth;
|
||||
- (NSArray *) domainsAllowed;
|
||||
- (BOOL) enableDomainBasedUID;
|
||||
@@ -89,7 +94,7 @@ NSComparisonResult languageSort(id el1, id el2, void *context);
|
||||
- (NSString *) loginSuffix;
|
||||
|
||||
- (NSString *) authenticationType;
|
||||
- (BOOL) isSsoUsed;
|
||||
- (BOOL) isSsoUsed: (NSString *) domain;
|
||||
- (NSString *) davAuthenticationType;
|
||||
|
||||
- (NSString *) CASServiceURL;
|
||||
@@ -101,7 +106,7 @@ NSComparisonResult languageSort(id el1, id el2, void *context);
|
||||
- (NSString *) openIdClientSecret;
|
||||
- (NSString *) openIdEmailParam;
|
||||
- (BOOL) openIdEnableRefreshToken;
|
||||
- (BOOL) openIdLogoutEnabled;
|
||||
- (BOOL) openIdLogoutEnabled: (NSString *) _domain;
|
||||
- (int) openIdTokenCheckInterval;
|
||||
|
||||
- (NSString *) SAML2PrivateKeyLocation;
|
||||
|
||||
@@ -262,6 +262,143 @@ _injectConfigurationFromFile (NSMutableDictionary *defaultsDict,
|
||||
return [domains allKeys];
|
||||
}
|
||||
|
||||
- (BOOL) doesLoginTypeByDomain
|
||||
{
|
||||
return ([self dictionaryForKey: @"SOGoLoginTypeByDomain"] != nil);
|
||||
}
|
||||
|
||||
- (NSString *) getLoginTypeForDomain: (NSString*) _domain
|
||||
{
|
||||
NSDictionary *domains, *config;
|
||||
NSString *type;
|
||||
if(![self doesLoginTypeByDomain])
|
||||
return nil;
|
||||
domains = [self dictionaryForKey: @"SOGoLoginTypeByDomain"];
|
||||
if([domains objectForKey: _domain])
|
||||
{
|
||||
config = [domains objectForKey: _domain];
|
||||
}
|
||||
else if([domains objectForKey: @"login_default"])
|
||||
{
|
||||
config = [domains objectForKey: @"login_default"];
|
||||
}
|
||||
else
|
||||
return nil;
|
||||
|
||||
if((type = [config objectForKey: @"type"]))
|
||||
{
|
||||
return type;
|
||||
}
|
||||
else
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSString *) getImapAuthMechForDomain: (NSString*) _domain
|
||||
{
|
||||
NSDictionary *domains, *config;
|
||||
NSString *type;
|
||||
|
||||
if(![self doesLoginTypeByDomain])
|
||||
return nil;
|
||||
|
||||
domains = [self dictionaryForKey: @"SOGoLoginTypeByDomain"];
|
||||
|
||||
if([domains objectForKey: _domain])
|
||||
{
|
||||
config = [domains objectForKey: _domain];
|
||||
}
|
||||
else if([domains objectForKey: @"login_default"])
|
||||
{
|
||||
config = [domains objectForKey: @"login_default"];
|
||||
}
|
||||
else
|
||||
return nil;
|
||||
|
||||
if((type = [config objectForKey: @"imapAuthMech"]))
|
||||
{
|
||||
return type;
|
||||
}
|
||||
else
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSString *) getSmtpAuthMechForDomain: (NSString*) _domain
|
||||
{
|
||||
NSDictionary *domains, *config;
|
||||
NSString *type;
|
||||
|
||||
if(![self doesLoginTypeByDomain])
|
||||
return nil;
|
||||
|
||||
domains = [self dictionaryForKey: @"SOGoLoginTypeByDomain"];
|
||||
|
||||
if([domains objectForKey: _domain])
|
||||
{
|
||||
config = [domains objectForKey: _domain];
|
||||
}
|
||||
else if([domains objectForKey: @"login_default"])
|
||||
{
|
||||
config = [domains objectForKey: @"login_default"];
|
||||
}
|
||||
else
|
||||
return nil;
|
||||
|
||||
if((type = [config objectForKey: @"smtpAuthMech"]))
|
||||
{
|
||||
return type;
|
||||
}
|
||||
else
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSString *) getLoginConfigForDomain: (NSDictionary*) _domain
|
||||
{
|
||||
NSDictionary *domains, *config;
|
||||
if(![self doesLoginTypeByDomain])
|
||||
return nil;
|
||||
domains = [self dictionaryForKey: @"SOGoLoginTypeByDomain"];
|
||||
if([domains objectForKey: _domain])
|
||||
{
|
||||
config = [domains objectForKey: _domain];
|
||||
}
|
||||
else if([domains objectForKey: @"login_default"])
|
||||
{
|
||||
config = [domains objectForKey: @"login_default"];
|
||||
}
|
||||
|
||||
if(config)
|
||||
return config;
|
||||
else
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (BOOL) hasOpenIdType
|
||||
{
|
||||
if([self doesLoginTypeByDomain])
|
||||
{
|
||||
NSDictionary *domainsConfig;
|
||||
NSEnumerator *e;
|
||||
NSString *domain, *type;
|
||||
if(![self doesLoginTypeByDomain])
|
||||
return NO;
|
||||
domainsConfig = [self dictionaryForKey: @"SOGoLoginTypeByDomain"];
|
||||
e = [domainsConfig keyEnumerator];
|
||||
while((domain = [e nextObject]))
|
||||
{
|
||||
if((type = [[domainsConfig objectForKey: domain] objectForKey: @"type"]))
|
||||
{
|
||||
if([type isEqualToString: @"openid"])
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
else
|
||||
return [[self authenticationType] isEqualToString: @"openid"];
|
||||
|
||||
}
|
||||
|
||||
|
||||
- (BOOL) enableDomainBasedUID
|
||||
{
|
||||
return [self boolForKey: @"SOGoEnableDomainBasedUID"];
|
||||
@@ -582,11 +719,13 @@ NSComparisonResult languageSort(id el1, id el2, void *context)
|
||||
return [[self stringForKey: @"SOGoAuthenticationType"] lowercaseString];
|
||||
}
|
||||
|
||||
- (BOOL) isSsoUsed
|
||||
- (BOOL) isSsoUsed: (NSString *) domain
|
||||
{
|
||||
NSString* authType;
|
||||
authType = [self authenticationType];
|
||||
|
||||
authType = [self getLoginTypeForDomain: domain];
|
||||
if(!authType)
|
||||
authType = [self authenticationType];
|
||||
return ([authType isEqualToString: @"cas"] || [authType isEqualToString: @"saml2"] || [authType isEqualToString: @"openid"]);
|
||||
}
|
||||
|
||||
@@ -635,8 +774,20 @@ NSComparisonResult languageSort(id el1, id el2, void *context)
|
||||
return emailParam;
|
||||
}
|
||||
|
||||
- (BOOL) openIdLogoutEnabled
|
||||
- (BOOL) openIdLogoutEnabled: (NSString *) _domain
|
||||
{
|
||||
if(_domain && [self doesLoginTypeByDomain])
|
||||
{
|
||||
NSDictionary *config;
|
||||
NSString *type;
|
||||
id value;
|
||||
if((config = [self getLoginConfigForDomain: _domain]))
|
||||
{
|
||||
if((type = [config objectForKey: @"type"]) && [type isEqualToString:@"openid"])
|
||||
return [self boolForKey: @"SOGoOpenIdLogoutEnabled" andDict: config];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
return [self boolForKey: @"SOGoOpenIdLogoutEnabled"];
|
||||
}
|
||||
|
||||
|
||||
@@ -91,6 +91,7 @@
|
||||
|
||||
/* properties */
|
||||
- (NSString *) domain;
|
||||
- (NSString *) loginDomain;
|
||||
- (id <SOGoSource>) authenticationSource;
|
||||
|
||||
- (NSArray *) allEmails;
|
||||
|
||||
@@ -301,6 +301,19 @@ static const NSString *kEncryptedUserNamePrefix = @"uenc";
|
||||
return [self _fetchFieldForUser: @"c_domain"];
|
||||
}
|
||||
|
||||
- (NSString *) loginDomain
|
||||
{
|
||||
NSRange r;
|
||||
NSString *domain = nil;
|
||||
r = [self->login rangeOfString: @"@"];
|
||||
if (r.location != NSNotFound)
|
||||
{
|
||||
domain = [self->login substringFromIndex: r.location+1];
|
||||
}
|
||||
return domain;
|
||||
}
|
||||
|
||||
|
||||
- (id <SOGoSource>) authenticationSource
|
||||
{
|
||||
NSString *sourceID;
|
||||
|
||||
@@ -587,6 +587,7 @@ static const NSString *kObfuscatedSecondaryEmailKey = @"obfuscatedSecondaryEmail
|
||||
sd = [SOGoSystemDefaults sharedSystemDefaults];
|
||||
|
||||
username = _login;
|
||||
NSLog(@"D login are %@", _login);
|
||||
|
||||
if (*_domain && ![*_domain isKindOfClass: NSNullK])
|
||||
{
|
||||
|
||||
@@ -134,11 +134,36 @@
|
||||
SOGoOpenIdSession * openIdSession;
|
||||
SOGoSystemDefaults *sd;
|
||||
NSString *authenticationType;
|
||||
NSString* loginDomain;
|
||||
BOOL rc;
|
||||
|
||||
sd = [SOGoSystemDefaults sharedSystemDefaults];
|
||||
|
||||
NSLog(@"B Credentials are %@", _login);
|
||||
//Basic check
|
||||
if(!_login)
|
||||
return NO;
|
||||
if(_login && [_login length] == 0)
|
||||
return NO;
|
||||
|
||||
loginDomain = nil;
|
||||
if(*_domain == nil || [*_domain length] == 0)
|
||||
{
|
||||
NSRange r;
|
||||
r = [_login rangeOfString: @"@"];
|
||||
if (r.location != NSNotFound)
|
||||
{
|
||||
loginDomain = [_login substringFromIndex: r.location+1];
|
||||
}
|
||||
}
|
||||
if(loginDomain)
|
||||
NSLog(@"C domain are %@", loginDomain);
|
||||
|
||||
if([sd doesLoginTypeByDomain])
|
||||
authenticationType = [sd getLoginTypeForDomain: loginDomain];
|
||||
else
|
||||
authenticationType = [sd authenticationType];
|
||||
|
||||
if ([authenticationType isEqualToString: @"cas"])
|
||||
{
|
||||
casSession = [SOGoCASSession CASSessionWithIdentifier: _pwd fromProxy: NO];
|
||||
@@ -149,7 +174,7 @@
|
||||
}
|
||||
else if ([authenticationType isEqualToString: @"openid"])
|
||||
{
|
||||
openIdSession = [SOGoOpenIdSession OpenIdSessionWithToken: _pwd];
|
||||
openIdSession = [SOGoOpenIdSession OpenIdSessionWithToken: _pwd domain: loginDomain];
|
||||
if (openIdSession)
|
||||
rc = [[openIdSession login: _login] isEqualToString: _login];
|
||||
else
|
||||
@@ -175,7 +200,6 @@
|
||||
expire: _expire
|
||||
grace: _grace
|
||||
useCache: _useCache];
|
||||
|
||||
//[self logWithFormat: @"Checked login with ppolicy enabled: %d %d %d", *_perr, *_expire, *_grace];
|
||||
|
||||
// It's important to return the real value here. The callee will handle
|
||||
@@ -249,12 +273,15 @@
|
||||
|
||||
sessionKey = [creds objectAtIndex:1];
|
||||
|
||||
NSLog(@"AAAA decodevalue for");
|
||||
[SOGoSession decodeValue: [SOGoSession valueForSessionKey: sessionKey]
|
||||
usingKey: userKey
|
||||
login: &login
|
||||
domain: &domain
|
||||
password: &pwd];
|
||||
|
||||
NSLog(@"A Credentials are %@", login);
|
||||
|
||||
if (![self checkLogin: login
|
||||
password: pwd
|
||||
domain: &domain
|
||||
@@ -276,32 +303,42 @@
|
||||
{
|
||||
NSString *authType, *password;
|
||||
SOGoSystemDefaults *sd;
|
||||
SOGoUser *user;
|
||||
NSRange r;
|
||||
NSString *loginDomain, *login;
|
||||
|
||||
password = [self passwordInContext: context];
|
||||
if ([password length])
|
||||
{
|
||||
user = [self userInContext: context];
|
||||
login = [user loginInDomain];
|
||||
r = [login rangeOfString: @"@"];
|
||||
if (r.location != NSNotFound)
|
||||
loginDomain = [login substringFromIndex: r.location+1];
|
||||
else
|
||||
loginDomain = nil;
|
||||
|
||||
sd = [SOGoSystemDefaults sharedSystemDefaults];
|
||||
if([sd doesLoginTypeByDomain])
|
||||
authType = [sd getLoginTypeForDomain: loginDomain];
|
||||
else
|
||||
authType = [sd authenticationType];
|
||||
|
||||
if ([authType isEqualToString: @"cas"])
|
||||
{
|
||||
SOGoCASSession *session;
|
||||
SOGoUser *user;
|
||||
NSString *service, *scheme;
|
||||
|
||||
session = [SOGoCASSession CASSessionWithIdentifier: password
|
||||
fromProxy: NO];
|
||||
|
||||
user = [self userInContext: context];
|
||||
// Try configured CAS service name first
|
||||
service = [[user domainDefaults] imapCASServiceName];
|
||||
if (!service)
|
||||
{
|
||||
// We must NOT assume the scheme exists
|
||||
scheme = [server scheme];
|
||||
|
||||
if (!scheme)
|
||||
scheme = @"imap";
|
||||
|
||||
service = [NSString stringWithFormat: @"%@://%@",
|
||||
scheme, [server host]];
|
||||
}
|
||||
@@ -310,17 +347,16 @@
|
||||
[session invalidateTicketForService: service];
|
||||
|
||||
password = [session ticketForService: service];
|
||||
|
||||
if ([password length] || renew)
|
||||
[session updateCache];
|
||||
}
|
||||
else if ([authType isEqualToString: @"openid"])
|
||||
{
|
||||
SOGoOpenIdSession* session;
|
||||
NSString* currentToken;
|
||||
|
||||
//If the token has been refresh during the request, we need to use the new access_token
|
||||
//as the one from the cookie is no more valid
|
||||
session = [SOGoOpenIdSession OpenIdSessionWithToken: password];
|
||||
session = [SOGoOpenIdSession OpenIdSessionWithToken: password domain: loginDomain];
|
||||
password = [session getCurrentToken];
|
||||
}
|
||||
#if defined(SAML2_CONFIG)
|
||||
@@ -453,21 +489,36 @@
|
||||
{
|
||||
NSArray *listCookies = nil;
|
||||
SOGoSystemDefaults *sd;
|
||||
NSString *authType;
|
||||
NSString *authType, *username, *login, *loginDomain;
|
||||
NSRange r;
|
||||
SOGoUser *user;
|
||||
|
||||
user = [self userInContext: _ctx];
|
||||
login = [user loginDomain];
|
||||
r = [login rangeOfString: @"@"];
|
||||
if (r.location != NSNotFound)
|
||||
loginDomain = [login substringFromIndex: r.location+1];
|
||||
else
|
||||
loginDomain = nil;
|
||||
|
||||
sd = [SOGoSystemDefaults sharedSystemDefaults];
|
||||
if(loginDomain && [sd doesLoginTypeByDomain])
|
||||
authType = [sd getLoginTypeForDomain: loginDomain];
|
||||
else
|
||||
authType = [sd authenticationType];
|
||||
if([authType isEqualToString:@"openid"] && [sd openIdEnableRefreshToken])
|
||||
{
|
||||
NSString *currentPassword, *newPassword, *username;
|
||||
NSString *currentPassword, *newPassword;
|
||||
SOGoOpenIdSession *openIdSession;
|
||||
|
||||
WOCookie* newCookie;
|
||||
|
||||
|
||||
currentPassword = [self passwordInContext: _ctx];
|
||||
newPassword = [self imapPasswordInContext: _ctx forURL: nil forceRenew: NO];
|
||||
if(currentPassword && newPassword && ![newPassword isEqualToString: currentPassword])
|
||||
{
|
||||
openIdSession = [SOGoOpenIdSession OpenIdSessionWithToken: newPassword];
|
||||
|
||||
openIdSession = [SOGoOpenIdSession OpenIdSessionWithToken: newPassword domain: loginDomain];
|
||||
if (openIdSession)
|
||||
username = [openIdSession login: @""]; //Force to refresh the name
|
||||
else
|
||||
|
||||
235
Tools/SOGoToolExpireOpenIdSessions.m
Normal file
235
Tools/SOGoToolExpireOpenIdSessions.m
Normal file
@@ -0,0 +1,235 @@
|
||||
/* SOGoToolExpireUserSessions.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2012-2021 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
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This file is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#import <Foundation/NSArray.h>
|
||||
#import <Foundation/NSCalendarDate.h>
|
||||
#import <Foundation/NSDictionary.h>
|
||||
#import <Foundation/NSException.h>
|
||||
#import <Foundation/NSUserDefaults.h>
|
||||
|
||||
#import <GDLAccess/EOAdaptorChannel.h>
|
||||
|
||||
#import <GDLContentStore/GCSChannelManager.h>
|
||||
#import <GDLContentStore/NSURL+GCS.h>
|
||||
|
||||
#import <NGExtensions/NSNull+misc.h>
|
||||
|
||||
#import <SOGo/NSString+Utilities.h>
|
||||
#import <SOGo/SOGoSession.h>
|
||||
#import <SOGo/SOGoOpenIdSession.h>
|
||||
|
||||
#import "SOGoTool.h"
|
||||
|
||||
@interface SOGoToolExpireUserSessions : SOGoTool
|
||||
@end
|
||||
|
||||
@implementation SOGoToolExpireUserSessions
|
||||
|
||||
+ (NSString *) command
|
||||
{
|
||||
return @"expire-sessions";
|
||||
}
|
||||
|
||||
+ (NSString *) description
|
||||
{
|
||||
return @"expires user sessions without activity for specified number of minutes";
|
||||
}
|
||||
|
||||
- (void) usage
|
||||
{
|
||||
fprintf (stderr, "expire-sessions [nbMinutes]\n\n"
|
||||
" nbMinutes Number of minutes of inactivity after which a user session will be expired\n"
|
||||
"\n"
|
||||
"The expire-sessions action should be configured as a cronjob.\n");
|
||||
}
|
||||
|
||||
- (BOOL) expireUserOpenIdSessionOlderThan: (int) nbMinutes
|
||||
{
|
||||
BOOL rc;
|
||||
EOAdaptorChannel *channel;
|
||||
GCSChannelManager *cm;
|
||||
NSArray *attrs;
|
||||
NSDictionary *qresult;
|
||||
NSException *ex;
|
||||
NSString *sql, *sessionsFolderURL, *sessionID;
|
||||
NSURL *tableURL;
|
||||
NSUserDefaults *ud;
|
||||
|
||||
unsigned int now, oldest;
|
||||
|
||||
rc = YES;
|
||||
ud = [NSUserDefaults standardUserDefaults];
|
||||
now = [[NSCalendarDate calendarDate] timeIntervalSince1970];
|
||||
oldest = now - (nbMinutes * 60);
|
||||
sessionID = nil;
|
||||
|
||||
sessionsFolderURL = [ud stringForKey: @"OCSOpenIdURL"];
|
||||
if (!sessionsFolderURL)
|
||||
{
|
||||
if (verbose)
|
||||
NSLog(@"Couldn't read OCSOpenIdURL");
|
||||
return rc = NO;
|
||||
}
|
||||
|
||||
tableURL = [[NSURL alloc] initWithString: sessionsFolderURL];
|
||||
cm = [GCSChannelManager defaultChannelManager];
|
||||
channel = [cm acquireOpenChannelForURL: tableURL];
|
||||
if (!channel)
|
||||
{
|
||||
/* FIXME: nice error msg */
|
||||
NSLog(@"Can't aquire channel");
|
||||
return rc = NO;
|
||||
}
|
||||
|
||||
sql = [NSString stringWithFormat: @"SELECT c_user_session FROM %@ WHERE c_access_token_expires_in <= %d",
|
||||
[tableURL gcsTableName], oldest];
|
||||
ex = [channel evaluateExpressionX: sql];
|
||||
if (ex)
|
||||
{
|
||||
NSLog(@"%@", [ex reason]);
|
||||
[ex raise];
|
||||
return rc = NO;
|
||||
}
|
||||
|
||||
attrs = [channel describeResults: NO];
|
||||
while ((qresult = [channel fetchAttributes: attrs withZone: NULL]))
|
||||
{
|
||||
sessionID = [qresult objectForKey: @"c_user_session"];
|
||||
if (sessionID)
|
||||
{
|
||||
if (verbose)
|
||||
NSLog(@"Removing session %@", sessionID);
|
||||
[SOGoOpenIdSession deleteValueForSessionKey: sessionID];
|
||||
}
|
||||
}
|
||||
[cm releaseChannel: channel immediately: YES];
|
||||
|
||||
if (verbose && sessionID == nil)
|
||||
NSLog(@"No session to remove on openId");
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
- (BOOL) expireUserSessionOlderThan: (int) nbMinutes
|
||||
{
|
||||
BOOL rc;
|
||||
EOAdaptorChannel *channel;
|
||||
GCSChannelManager *cm;
|
||||
NSArray *attrs;
|
||||
NSDictionary *qresult;
|
||||
NSException *ex;
|
||||
NSString *sql, *sessionsFolderURL, *sessionID, *authType;
|
||||
NSURL *tableURL;
|
||||
NSUserDefaults *ud;
|
||||
|
||||
unsigned int now, oldest;
|
||||
|
||||
rc = YES;
|
||||
ud = [NSUserDefaults standardUserDefaults];
|
||||
now = [[NSCalendarDate calendarDate] timeIntervalSince1970];
|
||||
oldest = now - (nbMinutes * 60);
|
||||
sessionID = nil;
|
||||
|
||||
sessionsFolderURL = [ud stringForKey: @"OCSSessionsFolderURL"];
|
||||
if (!sessionsFolderURL)
|
||||
{
|
||||
if (verbose)
|
||||
NSLog(@"Couldn't read OCSSessionsFolderURL");
|
||||
return rc = NO;
|
||||
}
|
||||
|
||||
tableURL = [[NSURL alloc] initWithString: sessionsFolderURL];
|
||||
cm = [GCSChannelManager defaultChannelManager];
|
||||
channel = [cm acquireOpenChannelForURL: tableURL];
|
||||
if (!channel)
|
||||
{
|
||||
/* FIXME: nice error msg */
|
||||
NSLog(@"Can't aquire channel");
|
||||
return rc = NO;
|
||||
}
|
||||
|
||||
sql = [NSString stringWithFormat: @"SELECT c_id FROM %@ WHERE c_lastseen <= %d",
|
||||
[tableURL gcsTableName], oldest];
|
||||
ex = [channel evaluateExpressionX: sql];
|
||||
if (ex)
|
||||
{
|
||||
NSLog(@"%@", [ex reason]);
|
||||
[ex raise];
|
||||
return rc = NO;
|
||||
}
|
||||
|
||||
attrs = [channel describeResults: NO];
|
||||
while ((qresult = [channel fetchAttributes: attrs withZone: NULL]))
|
||||
{
|
||||
sessionID = [qresult objectForKey: @"c_id"];
|
||||
if (sessionID)
|
||||
{
|
||||
if (verbose)
|
||||
NSLog(@"Removing session %@", sessionID);
|
||||
[SOGoSession deleteValueForSessionKey: sessionID];
|
||||
}
|
||||
}
|
||||
[cm releaseChannel: channel immediately: YES];
|
||||
|
||||
if (verbose && sessionID == nil)
|
||||
NSLog(@"No session to remove");
|
||||
|
||||
//doing openid session if needed
|
||||
authType = [ud stringForKey:@"SOGoAuthenticationType"];
|
||||
if([authType isEqualToString: @"openid"])
|
||||
{
|
||||
[self expireUserOpenIdSessionOlderThan: nbMinutes];
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
- (BOOL) run
|
||||
{
|
||||
BOOL rc;
|
||||
int sessionExpireMinutes = -1;
|
||||
|
||||
rc = NO;
|
||||
|
||||
if ([arguments count])
|
||||
{
|
||||
sessionExpireMinutes = [[arguments objectAtIndex: 0] intValue];
|
||||
}
|
||||
|
||||
NSLog(@"Remove all sessions older than %d min", sessionExpireMinutes);
|
||||
|
||||
if (sessionExpireMinutes == 0 && ![[arguments objectAtIndex: 0] isEqualToString:@"0"])
|
||||
{
|
||||
//If the input is not a number intValue return 0 so we check that's really the case
|
||||
[self usage];
|
||||
}
|
||||
else if (sessionExpireMinutes >= 0)
|
||||
{
|
||||
rc = [self expireUserSessionOlderThan: sessionExpireMinutes];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self usage];
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -488,14 +488,6 @@
|
||||
&& [user isSuperUser]);
|
||||
}
|
||||
|
||||
- (BOOL) usesCASAuthentication
|
||||
{
|
||||
SOGoSystemDefaults *sd;
|
||||
|
||||
sd = [SOGoSystemDefaults sharedSystemDefaults];
|
||||
|
||||
return [[sd authenticationType] isEqualToString: @"cas"];
|
||||
}
|
||||
|
||||
- (BOOL) usesOpenIdAuthentication
|
||||
{
|
||||
@@ -546,19 +538,32 @@
|
||||
BOOL canLogoff;
|
||||
id auth;
|
||||
SOGoSystemDefaults *sd;
|
||||
NSString *authType;
|
||||
NSString *authType, *login, *loginDomain;
|
||||
NSRange r;
|
||||
|
||||
auth = [[self clientObject] authenticatorInContext: context];
|
||||
if ([auth respondsToSelector: @selector (cookieNameInContext:)])
|
||||
{
|
||||
|
||||
sd = [SOGoSystemDefaults sharedSystemDefaults];
|
||||
|
||||
login = [[context activeUser] login];
|
||||
r = [login rangeOfString: @"@"];
|
||||
if (r.location != NSNotFound)
|
||||
loginDomain = [login substringFromIndex: r.location+1];
|
||||
else
|
||||
loginDomain = nil;
|
||||
if(loginDomain && [sd doesLoginTypeByDomain])
|
||||
authType = [sd getLoginTypeForDomain: loginDomain];
|
||||
else
|
||||
authType = [sd authenticationType];
|
||||
|
||||
if ([authType isEqualToString: @"cas"])
|
||||
canLogoff = [sd CASLogoutEnabled];
|
||||
else if ([authType isEqualToString: @"saml2"])
|
||||
canLogoff = [sd SAML2LogoutEnabled];
|
||||
else if ([authType isEqualToString: @"openid"])
|
||||
canLogoff = [sd openIdLogoutEnabled];
|
||||
canLogoff = [sd openIdLogoutEnabled: loginDomain];
|
||||
else
|
||||
canLogoff = [[auth cookieNameInContext: context] length] > 0;
|
||||
}
|
||||
|
||||
@@ -145,6 +145,7 @@ static const NSString *kJwtKey = @"jwt";
|
||||
|
||||
- (WOCookie *) _authLocationCookie: (BOOL) cookieReset
|
||||
withName: (NSString *) cookieName
|
||||
withValue: (NSString *) _value
|
||||
{
|
||||
WOCookie *locationCookie;
|
||||
NSString *appName;
|
||||
@@ -152,6 +153,9 @@ static const NSString *kJwtKey = @"jwt";
|
||||
NSCalendarDate *date;
|
||||
|
||||
rq = [context request];
|
||||
if(_value)
|
||||
locationCookie = [WOCookie cookieWithName: cookieName value: _value];
|
||||
else
|
||||
locationCookie = [WOCookie cookieWithName: cookieName value: [rq uri]];
|
||||
appName = [rq applicationName];
|
||||
[locationCookie setPath: [NSString stringWithFormat: @"/%@/", appName]];
|
||||
@@ -165,6 +169,28 @@ static const NSString *kJwtKey = @"jwt";
|
||||
return locationCookie;
|
||||
}
|
||||
|
||||
- (WOCookie *) _domainCookie: (BOOL) cookieReset
|
||||
withDomain: (NSString *) _domain
|
||||
{
|
||||
WOCookie *domainCookie;
|
||||
NSString *appName;
|
||||
WORequest *rq;
|
||||
NSCalendarDate *date;
|
||||
|
||||
rq = [context request];
|
||||
domainCookie = [WOCookie cookieWithName: @"sogo-user-domain" value: _domain];
|
||||
appName = [rq applicationName];
|
||||
[domainCookie setPath: [NSString stringWithFormat: @"/%@/", appName]];
|
||||
if (cookieReset)
|
||||
{
|
||||
date = [NSCalendarDate calendarDate];
|
||||
[date setTimeZone: [NSTimeZone timeZoneForSecondsFromGMT: 0]];
|
||||
[domainCookie setExpires: [date yesterday]];
|
||||
}
|
||||
|
||||
return domainCookie;
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
@@ -389,6 +415,7 @@ static const NSString *kJwtKey = @"jwt";
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
- (NSDictionary *) _casRedirectKeys
|
||||
{
|
||||
NSDictionary *redirectKeys;
|
||||
@@ -462,7 +489,8 @@ static const NSString *kJwtKey = @"jwt";
|
||||
/* login callback, we expire the "cas-location" cookie, created
|
||||
below */
|
||||
casLocationCookie = [self _authLocationCookie: YES
|
||||
withName: @"cas-location"];
|
||||
withName: @"cas-location"
|
||||
withValue: nil];
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -501,7 +529,8 @@ static const NSString *kJwtKey = @"jwt";
|
||||
newLocation = [SOGoCASSession CASURLWithAction: @"login"
|
||||
andParameters: [self _casRedirectKeys]];
|
||||
casLocationCookie = [self _authLocationCookie: NO
|
||||
withName: @"cas-location"];
|
||||
withName: @"cas-location"
|
||||
withValue: nil];
|
||||
}
|
||||
response = [self redirectToLocation: newLocation];
|
||||
if (casCookie)
|
||||
@@ -512,7 +541,7 @@ static const NSString *kJwtKey = @"jwt";
|
||||
return response;
|
||||
}
|
||||
|
||||
- (id <WOActionResults>) _openidDefaultAction
|
||||
- (id <WOActionResults>) _openidDefaultAction: (NSString *) _domain
|
||||
{
|
||||
WOResponse *response;
|
||||
NSString *login, *redirectLocation, *serverUrl;
|
||||
@@ -520,7 +549,7 @@ static const NSString *kJwtKey = @"jwt";
|
||||
NSURL *newLocation, *oldLocation;
|
||||
NSDictionary *formValues;
|
||||
SOGoUser *loggedInUser;
|
||||
WOCookie *openIdCookie, *openIdCookieLocation, *openIdRefreshCookie;
|
||||
WOCookie *openIdCookie, *openIdCookieLocation, *openIdRefreshCookie, *domainCookie;
|
||||
WORequest *rq;
|
||||
SOGoWebAuthenticator *auth;
|
||||
SOGoOpenIdSession *openIdSession;
|
||||
@@ -529,9 +558,16 @@ static const NSString *kJwtKey = @"jwt";
|
||||
openIdCookie = nil;
|
||||
openIdCookieLocation = nil;
|
||||
openIdRefreshCookie = nil;
|
||||
domainCookie = nil;
|
||||
newLocation = nil;
|
||||
|
||||
openIdSession = [SOGoOpenIdSession OpenIdSession];
|
||||
rq = [context request];
|
||||
|
||||
//Check if the domain is stored in a cookie if not given
|
||||
if(_domain == nil || [_domain length] == 0)
|
||||
_domain = [rq cookieValueForKey: @"sogo-user-domain"]; //_domain can still be nil aftert his
|
||||
|
||||
openIdSession = [SOGoOpenIdSession OpenIdSession: _domain];
|
||||
|
||||
if(![openIdSession sessionIsOk])
|
||||
{
|
||||
@@ -540,7 +576,6 @@ static const NSString *kJwtKey = @"jwt";
|
||||
}
|
||||
|
||||
login = [[context activeUser] login];
|
||||
rq = [context request];
|
||||
if ([login isEqualToString: @"anonymous"])
|
||||
login = nil;
|
||||
if (!login)
|
||||
@@ -548,7 +583,6 @@ static const NSString *kJwtKey = @"jwt";
|
||||
//You get here if you nerver been logged in or if you token is expired
|
||||
serverUrl = [[context serverURL] absoluteString];
|
||||
redirectLocation = [NSString stringWithFormat: @"%@/%@/", serverUrl, [rq applicationName]];
|
||||
NSLog(@"ServerUrl %@ and redirect: %@", serverUrl, redirectLocation);
|
||||
if((formValues = [rq formValues]) && [formValues objectForKey: @"code"])
|
||||
{
|
||||
//You get here if this is the callback of openid after you logged in
|
||||
@@ -559,6 +593,7 @@ static const NSString *kJwtKey = @"jwt";
|
||||
// sessionState = [value lastObject];
|
||||
// else
|
||||
// sessionState = value;
|
||||
|
||||
value = [formValues objectForKey: @"code"];
|
||||
if ([value isKindOfClass: [NSArray class]])
|
||||
code = [value lastObject];
|
||||
@@ -574,7 +609,8 @@ static const NSString *kJwtKey = @"jwt";
|
||||
inContext: context];
|
||||
}
|
||||
newLocation = [rq cookieValueForKey: @"openid-location"];
|
||||
openIdCookieLocation = [self _authLocationCookie: YES withName: @"openid-location"];
|
||||
openIdCookieLocation = [self _authLocationCookie: YES withName: @"openid-location" withValue: nil];
|
||||
domainCookie = [self _domainCookie: YES withDomain: _domain];
|
||||
}
|
||||
// else if((formValues = [rq formValues]) && [formValues objectForKey: @"action"])
|
||||
// {
|
||||
@@ -596,8 +632,13 @@ static const NSString *kJwtKey = @"jwt";
|
||||
// //To avoid making a redirection to openid server after a post request, we first redirect to a get method
|
||||
// newLocation = [NSString stringWithFormat: @"%@?action=redirect", redirectLocation];
|
||||
// else
|
||||
if(_domain != nil && [_domain length] > 0)
|
||||
{
|
||||
//add the domain cookie to get it after the redirect
|
||||
domainCookie = [self _domainCookie: NO withDomain: _domain];
|
||||
}
|
||||
newLocation = [openIdSession loginUrl: redirectLocation];
|
||||
openIdCookieLocation = [self _authLocationCookie: NO withName: @"openid-location"];
|
||||
openIdCookieLocation = [self _authLocationCookie: NO withName: @"openid-location" withValue: nil];
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -617,10 +658,14 @@ static const NSString *kJwtKey = @"jwt";
|
||||
[response addCookie: openIdCookie];
|
||||
if (openIdCookieLocation)
|
||||
[response addCookie: openIdCookieLocation];
|
||||
if(domainCookie)
|
||||
[response addCookie: domainCookie];
|
||||
//[response setStatus: 303];
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#if defined(SAML2_CONFIG)
|
||||
- (id <WOActionResults>) _saml2DefaultAction
|
||||
{
|
||||
@@ -644,7 +689,8 @@ static const NSString *kJwtKey = @"jwt";
|
||||
newLocation = [rq cookieValueForKey: @"saml2-location"];
|
||||
if (newLocation)
|
||||
saml2LocationCookie = [self _authLocationCookie: YES
|
||||
withName: @"saml2-location"];
|
||||
withName: @"saml2-location"
|
||||
withValue: nil];
|
||||
else
|
||||
{
|
||||
oldLocation = [[self clientObject] baseURLInContext: context];
|
||||
@@ -659,7 +705,8 @@ static const NSString *kJwtKey = @"jwt";
|
||||
{
|
||||
newLocation = [SOGoSAML2Session authenticationURLInContext: context];
|
||||
saml2LocationCookie = [self _authLocationCookie: NO
|
||||
withName: @"saml2-location"];
|
||||
withName: @"saml2-location"
|
||||
withValue: nil];
|
||||
}
|
||||
|
||||
response = [self redirectToLocation: newLocation];
|
||||
@@ -682,9 +729,7 @@ static const NSString *kJwtKey = @"jwt";
|
||||
if (login)
|
||||
{
|
||||
oldLocation = [[self clientObject] baseURLInContext: context];
|
||||
response
|
||||
= [self redirectToLocation: [NSString stringWithFormat: @"%@%@",
|
||||
oldLocation,
|
||||
response = [self redirectToLocation: [NSString stringWithFormat: @"%@%@", oldLocation,
|
||||
[[SOGoUser getEncryptedUsernameIfNeeded:login request: [context request]] stringByEscapingURL]]];
|
||||
}
|
||||
else
|
||||
@@ -699,23 +744,174 @@ static const NSString *kJwtKey = @"jwt";
|
||||
return response;
|
||||
}
|
||||
|
||||
- (WOResponse *) connectNameAction
|
||||
{
|
||||
WOResponse *response;
|
||||
WORequest *request;
|
||||
NSDictionary *params;
|
||||
NSString *username, *language, *domain, *type, *serverUrl, *redirectLocation;
|
||||
NSRange r;
|
||||
|
||||
request = [context request];
|
||||
params = [[request contentAsString] objectFromJSONString];
|
||||
|
||||
username = [params objectForKey: @"userName"];
|
||||
|
||||
//Extract the domain
|
||||
r = [username rangeOfString: @"@"];
|
||||
if (r.location != NSNotFound)
|
||||
{
|
||||
domain = [username substringFromIndex: r.location+1];
|
||||
type = [[SOGoSystemDefaults sharedSystemDefaults] getLoginTypeForDomain: domain];
|
||||
if(type != nil)
|
||||
{
|
||||
if([type isEqualToString: @"plain"])
|
||||
{
|
||||
//Only reload the page with the name
|
||||
serverUrl = [[context serverURL] absoluteString];
|
||||
redirectLocation = [NSString stringWithFormat: @"%@/%@/login?hint=%@", serverUrl, [request applicationName], username];
|
||||
//response = [self redirectToLocation: [NSString stringWithFormat: @"%@/", redirectLocation]];
|
||||
response = [self responseWithStatus: 200 andJSONRepresentation:
|
||||
[NSDictionary dictionaryWithObjectsAndKeys: redirectLocation, @"redirect", nil]];
|
||||
}
|
||||
else if([type isEqualToString: @"openid"])
|
||||
{
|
||||
SOGoOpenIdSession *openIdSession;
|
||||
WOCookie *domainCookie, *openIdCookieLocation;
|
||||
|
||||
//With openId, the user will be redirected to the openid server for login
|
||||
//With set the domain in a cookie to know it after the openid does the callbacl
|
||||
serverUrl = [[context serverURL] absoluteString];
|
||||
redirectLocation = [NSString stringWithFormat: @"%@/%@/", serverUrl, [request applicationName]];
|
||||
|
||||
openIdSession = [SOGoOpenIdSession OpenIdSession: domain];
|
||||
|
||||
domainCookie = [self _domainCookie: NO withDomain: domain];
|
||||
openIdCookieLocation = [self _authLocationCookie: NO withName: @"openid-location" withValue: redirectLocation];
|
||||
|
||||
response = [self responseWithStatus: 200 andJSONRepresentation:
|
||||
[NSDictionary dictionaryWithObjectsAndKeys: [openIdSession loginUrl: redirectLocation], @"redirect", nil]];
|
||||
[response addCookie: domainCookie];
|
||||
[response addCookie: openIdCookieLocation];
|
||||
}
|
||||
else if([type isEqualToString: @"cas"] || [type isEqualToString: @"saml2"])
|
||||
{
|
||||
[self logWithFormat: @"Unsupported type for now: %@", type];
|
||||
response = [self responseWithStatus: 400
|
||||
andString: @"Domain Authentication type not supported"];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self logWithFormat: @"Unknown type: %@", type];
|
||||
response = [self responseWithStatus: 400
|
||||
andString: @"Unknwon Authentication type"];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
[self logWithFormat: @"Auth type for Domain given is not set or there is no default value: %@", domain];
|
||||
response = [self responseWithStatus: 400
|
||||
andString: @"Domain unknown"];
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
[self logWithFormat: @"Domain is required but not found for user recovery exception for user %@", username];
|
||||
response = [self responseWithStatus: 400
|
||||
andString: @"Domain needed in the login"];
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
- (id <WOActionResults>) defaultAction
|
||||
{
|
||||
NSString *authenticationType;
|
||||
NSString *authenticationType, *loginDomain, *type, *_domain;
|
||||
SOGoSystemDefaults* sd;
|
||||
id <WOActionResults> result;
|
||||
|
||||
authenticationType = [[SOGoSystemDefaults sharedSystemDefaults]
|
||||
authenticationType];
|
||||
loginDomain = nil;
|
||||
sd = [SOGoSystemDefaults sharedSystemDefaults];
|
||||
if([sd doesLoginTypeByDomain])
|
||||
{
|
||||
NSString *login;
|
||||
//In this mode sogo will ask the mail of the user before doing any authentication
|
||||
//Check if a user is already logged in
|
||||
|
||||
_domain = [[context request] cookieValueForKey: @"sogo-user-domain"]; //_domain can still be nil aftert his
|
||||
if(_domain != nil)
|
||||
{
|
||||
//This is a callback of an openid session.
|
||||
return [self _openidDefaultAction: _domain];
|
||||
}
|
||||
|
||||
login = [[context activeUser] login];
|
||||
if ([login isEqualToString: @"anonymous"])
|
||||
login = nil;
|
||||
if(!login && !_domain)
|
||||
return [self _standardDefaultAction];
|
||||
else
|
||||
{
|
||||
//User already logged in. Extract the domain in that case
|
||||
NSRange r;
|
||||
r = [login rangeOfString: @"@"];
|
||||
if (r.location != NSNotFound)
|
||||
{
|
||||
loginDomain = [login substringFromIndex: r.location+1];
|
||||
type = [sd getLoginTypeForDomain: loginDomain];
|
||||
if(type)
|
||||
{
|
||||
if([type isEqualToString: @"plain"])
|
||||
{
|
||||
result = [self _standardDefaultAction];
|
||||
}
|
||||
else if([type isEqualToString: @"openid"])
|
||||
{
|
||||
result = [self _openidDefaultAction: loginDomain];
|
||||
}
|
||||
else if([type isEqualToString: @"cas"] || [type isEqualToString: @"saml2"])
|
||||
{
|
||||
[self logWithFormat: @"Unsupported type for now: %@", type];
|
||||
result = [self responseWithStatus: 400
|
||||
andString: @"Domain Authentication type not supported"];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self logWithFormat: @"Unknown type: %@", type];
|
||||
result = [self responseWithStatus: 400
|
||||
andString: @"Unknwon Authentication type"];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
[self logWithFormat: @"Auth type for Domain given is not set or there is no default value: %@", loginDomain];
|
||||
result = [self responseWithStatus: 400
|
||||
andString: @"Domain unknown"];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
loginDomain = nil;
|
||||
result = [self _standardDefaultAction];
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
authenticationType = [sd authenticationType];
|
||||
|
||||
if ([authenticationType isEqualToString: @"cas"])
|
||||
result = [self _casDefaultAction];
|
||||
else if ([authenticationType isEqualToString: @"openid"])
|
||||
result = [self _openidDefaultAction];
|
||||
#if defined(SAML2_CONFIG)
|
||||
result = [self _openidDefaultAction: loginDomain];
|
||||
#if defined(SAML2_CONFIG)
|
||||
else if ([authenticationType isEqualToString: @"saml2"])
|
||||
result = [self _saml2DefaultAction];
|
||||
#endif /* SAML2_CONFIG */
|
||||
#endif /* SAML2_CONFIG */
|
||||
else
|
||||
result = [self _standardDefaultAction];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -745,6 +941,57 @@ static const NSString *kJwtKey = @"jwt";
|
||||
return ([[self loginDomains] count] > 0);
|
||||
}
|
||||
|
||||
- (BOOL) doLoginUsernameFirst
|
||||
{
|
||||
return [[SOGoSystemDefaults sharedSystemDefaults] doesLoginTypeByDomain];
|
||||
}
|
||||
|
||||
- (BOOL) doFullLogin
|
||||
{
|
||||
//Either we directly do the full login (meaning the user inputs its username and password)
|
||||
//Or we do it in two times:
|
||||
//phase 1: user types its username first -> only show the username input
|
||||
//phase 2: user types its password -> show all inputs
|
||||
//In phase 2, the username will be in the query at key "login"
|
||||
if([self doLoginUsernameFirst]){
|
||||
WORequest *rq;
|
||||
BOOL hasLogin;
|
||||
NSDictionary *formValues;
|
||||
|
||||
rq = [context request];
|
||||
hasLogin = ((formValues=[rq formValues]) && [formValues objectForKey: @"hint"]);
|
||||
return hasLogin;
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL) doPartialLogin
|
||||
{
|
||||
return ![self doFullLogin];
|
||||
}
|
||||
|
||||
|
||||
- (NSString *) getLoginHint
|
||||
{
|
||||
id value;
|
||||
WORequest *rq;
|
||||
NSString* login;
|
||||
NSDictionary *formValues;
|
||||
|
||||
login = @"";
|
||||
|
||||
rq = [context request];
|
||||
if((formValues=[rq formValues]) && (value=[formValues objectForKey: @"hint"]))
|
||||
{
|
||||
if ([value isKindOfClass: [NSArray class]])
|
||||
login = [value lastObject];
|
||||
else
|
||||
login = value;
|
||||
}
|
||||
return login;
|
||||
}
|
||||
|
||||
- (BOOL) hasPasswordRecovery
|
||||
{
|
||||
return [[SOGoSystemDefaults sharedSystemDefaults] isPasswordRecoveryEnabled];
|
||||
@@ -1108,8 +1355,7 @@ static const NSString *kJwtKey = @"jwt";
|
||||
message = [[request contentAsString] objectFromJSONString];
|
||||
username = [message objectForKey: @"userName"];
|
||||
domain = [message objectForKey: @"domain"];
|
||||
if ([[SOGoSystemDefaults sharedSystemDefaults]
|
||||
isPasswordRecoveryEnabled]) {
|
||||
if ([[SOGoSystemDefaults sharedSystemDefaults] isPasswordRecoveryEnabled]) {
|
||||
// If no domain, try to retrieve domain from username
|
||||
if (nil != domain && domain != [NSNull null]) {
|
||||
domainName = domain;
|
||||
|
||||
@@ -420,26 +420,38 @@
|
||||
|
||||
- (NSString *) _logoutRedirectURL
|
||||
{
|
||||
NSString *redirectURL;
|
||||
NSString *redirectURL, *login, *loginDomain, *authType;
|
||||
SOGoSystemDefaults *sd;
|
||||
id container;
|
||||
NSRange r;
|
||||
|
||||
|
||||
login = [[context activeUser] login];
|
||||
r = [login rangeOfString: @"@"];
|
||||
if (r.location != NSNotFound)
|
||||
loginDomain = [login substringFromIndex: r.location+1];
|
||||
else
|
||||
loginDomain = nil;
|
||||
|
||||
sd = [SOGoSystemDefaults sharedSystemDefaults];
|
||||
if ([[sd authenticationType] isEqualToString: @"cas"])
|
||||
if(loginDomain && [sd doesLoginTypeByDomain])
|
||||
authType = [sd getLoginTypeForDomain: loginDomain];
|
||||
else
|
||||
authType = [sd authenticationType];
|
||||
|
||||
if ([authType isEqualToString: @"cas"])
|
||||
{
|
||||
redirectURL = [SOGoCASSession CASURLWithAction: @"logout"
|
||||
andParameters: nil];
|
||||
}
|
||||
else if ([[sd authenticationType] isEqualToString: @"openid"])
|
||||
else if ([authType isEqualToString: @"openid"])
|
||||
{
|
||||
SOGoOpenIdSession* session;
|
||||
session = [SOGoOpenIdSession OpenIdSession];
|
||||
session = [SOGoOpenIdSession OpenIdSession: loginDomain];
|
||||
redirectURL = [session logoutUrl];
|
||||
//delete openid session in database
|
||||
|
||||
}
|
||||
#if defined(SAML2_CONFIG)
|
||||
else if ([[sd authenticationType] isEqualToString: @"saml2"])
|
||||
else if ([authType isEqualToString: @"saml2"])
|
||||
{
|
||||
NSString *username, *password, *domain, *value;
|
||||
SOGoSAML2Session *saml2Session;
|
||||
|
||||
@@ -168,6 +168,16 @@
|
||||
pageName = "SOGoRootPage";
|
||||
actionName = "connect";
|
||||
};
|
||||
connectName = {
|
||||
protectedBy = "<public>";
|
||||
pageName = "SOGoRootPage";
|
||||
actionName = "connectName";
|
||||
};
|
||||
openid_redirect = {
|
||||
protectedBy = "<public>";
|
||||
pageName = "SOGoRootPage";
|
||||
actionName = "openIdRedirect";
|
||||
};
|
||||
changePassword = {
|
||||
protectedBy = "<public>";
|
||||
pageName = "SOGoRootPage";
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
<script type="text/javascript">
|
||||
var cookieUsername = <var:string var:value="cookieUsername.doubleQuotedString" const:escapeHTML="NO"/>;
|
||||
var language = '<var:string var:value="language" const:escapeHTML="NO"/>';
|
||||
var loginHint = '<var:string var:value="getLoginHint" const:escapeHTML="NO"/>'
|
||||
</script>
|
||||
|
||||
<!--
|
||||
@@ -44,7 +45,10 @@
|
||||
</div>
|
||||
</var:if>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="sg-login md-default-theme md-bg md-accent" flex-gt-md="50">
|
||||
<var:if condition="doFullLogin">
|
||||
<div id="login" class="sg-login-content md-padding">
|
||||
<form name="loginForm" layout="column"
|
||||
ng-cloak="ng-cloak"
|
||||
@@ -53,11 +57,13 @@
|
||||
<input type="hidden" ng-model="app.creds.loginSuffix" var:value="loginSuffix"/>
|
||||
</var:if>
|
||||
<div ng-if="!app.loginState">
|
||||
|
||||
<md-input-container class="md-block">
|
||||
<label><var:string label:value="Username"/></label>
|
||||
<md-icon>person</md-icon>
|
||||
<input autocorrect="off" autocapitalize="off" type="text" ng-model="app.creds.username" ng-required="true" ng-change="app.usernameChanged()" ng-blur="app.retrievePasswordRecoveryEnabled()" />
|
||||
</md-input-container>
|
||||
|
||||
<md-input-container class="md-block">
|
||||
<label><var:string label:value="Password"/></label>
|
||||
<md-icon>vpn_key</md-icon>
|
||||
@@ -370,6 +376,115 @@
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</var:if>
|
||||
<var:if condition="doPartialLogin">
|
||||
<div id="login" class="sg-login-content md-padding">
|
||||
<form name="loginForm" layout="column"
|
||||
ng-cloak="ng-cloak"
|
||||
ng-submit="app.loginName()">
|
||||
<div ng-if="!app.loginState">
|
||||
|
||||
<md-input-container class="md-block">
|
||||
<label><var:string label:value="Username"/></label>
|
||||
<md-icon>person</md-icon>
|
||||
<input autocorrect="off" autocapitalize="off" type="text" ng-model="app.creds.username" ng-required="true" ng-change="app.usernameChanged()" ng-blur="app.retrievePasswordRecoveryEnabled()" />
|
||||
</md-input-container>
|
||||
|
||||
<!-- LANGUAGES SELECT -->
|
||||
<div layout="row" layout-align="start end">
|
||||
<md-icon>language</md-icon>
|
||||
<md-input-container class="md-flex">
|
||||
<label><var:string label:value="choose"/></label>
|
||||
<md-select ng-model="app.creds.language"
|
||||
var:placeholder="localizedLanguage"
|
||||
ng-change="app.changeLanguage($event)">
|
||||
<var:foreach list="languages" item="item">
|
||||
<md-option var:value="item">
|
||||
<var:string value="languageText"/>
|
||||
</md-option>
|
||||
</var:foreach>
|
||||
</md-select>
|
||||
</md-input-container>
|
||||
</div>
|
||||
|
||||
<var:if condition="hasUrlCreateAccount">
|
||||
<div layout="row" layout-align="center center">
|
||||
<a var:href="urlCreateAccount" target="_blank" class="create-account-link"><var:string label:value="Create an account"/></a>
|
||||
</div>
|
||||
</var:if>
|
||||
</div>
|
||||
|
||||
<!-- CONNECT BUTTON -->
|
||||
<div layout="row" layout-align="space-between center" ng-if="!app.loginState">
|
||||
<md-button class="md-icon-button"
|
||||
label:aria-label="About"
|
||||
ng-click="app.showAbout()">
|
||||
<md-icon>info</md-icon>
|
||||
</md-button>
|
||||
<div>
|
||||
<md-button class="md-fab md-accent md-hue-2" type="submit"
|
||||
label:aria-label="Connect"
|
||||
ng-if="!app.loginState"
|
||||
ng-disabled="loginForm.$invalid"
|
||||
sg-ripple-click="loginContent">
|
||||
<md-icon>arrow_forward</md-icon>
|
||||
</md-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<sg-ripple class="md-default-theme md-accent md-bg"
|
||||
ng-class="{ 'md-warn': app.loginState == 'error' }"><!-- ripple background --></sg-ripple>
|
||||
<sg-ripple-content class="md-flex ng-hide"
|
||||
layout="column" layout-align="center center" layout-fill="layout-fill"
|
||||
ng-switch="app.loginState">
|
||||
|
||||
<!-- Authenticating -->
|
||||
<div layout="column" layout-align="center center"
|
||||
ng-switch-when="authenticating">
|
||||
<md-progress-circular class="md-hue-1"
|
||||
md-mode="indeterminate"
|
||||
md-diameter="32"><!-- mailbox loading progress --></md-progress-circular>
|
||||
<div class="md-default-theme md-accent md-hue-1 md-fg md-padding">
|
||||
<var:string label:value="Authenticating"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Logged in -->
|
||||
<div layout="column" layout-align="center center"
|
||||
ng-switch-when="logged">
|
||||
<md-icon class="md-accent md-hue-1 sg-icon--large">done</md-icon>
|
||||
<div class="md-default-theme md-accent md-hue-1 md-fg md-padding">
|
||||
<var:string label:value="Welcome"/> {{app.cn}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div layout="column" layout-align="center center"
|
||||
ng-switch-when="message">
|
||||
<md-icon class="md-accent md-hue-1 sg-icon--large">done</md-icon>
|
||||
<div class="md-default-theme md-accent md-hue-1 md-fg md-padding">
|
||||
{{app.errorMessage}}
|
||||
</div>
|
||||
<md-button
|
||||
ng-click="app.continueLogin()"
|
||||
sg-ripple-click="loginContent"><var:string label:value="Continue"/></md-button>
|
||||
</div>
|
||||
|
||||
<!-- Error -->
|
||||
<div layout="column" layout-align="center center"
|
||||
ng-switch-when="error">
|
||||
<md-icon class="md-accent md-hue-1 sg-icon--large">error</md-icon>
|
||||
<div class="md-default-theme md-accent md-hue-1 md-fg md-padding">
|
||||
{{app.errorMessage}}
|
||||
</div>
|
||||
<md-button
|
||||
ng-click="app.restoreLogin()"
|
||||
sg-ripple-click="loginContent"><var:string label:value="Retry"/></md-button>
|
||||
</div>
|
||||
|
||||
</sg-ripple-content>
|
||||
</form>
|
||||
</div>
|
||||
</var:if>
|
||||
</div>
|
||||
</div>
|
||||
</md-content>
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -170,6 +170,45 @@
|
||||
return d.promise;
|
||||
}, // login: function(data) { ...
|
||||
|
||||
loginName: function(data) {
|
||||
var d = $q.defer(),
|
||||
username = data.username,
|
||||
language;
|
||||
|
||||
if (data.language && data.language != 'WONoSelectionString') {
|
||||
language = data.language;
|
||||
}
|
||||
|
||||
$http({
|
||||
method: 'POST',
|
||||
url: '/SOGo/connectName?userName='+username,
|
||||
// data: JSON.stringify({userName: username}),
|
||||
data: {userName: username},
|
||||
// headers: {
|
||||
// //'Content-Type': undefined
|
||||
// //'Content-Type': "application/x-www-form-urlencoded"
|
||||
// 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8'
|
||||
// }
|
||||
}).then(function(response) {
|
||||
var data = response.data;
|
||||
// Make sure browser's cookies are enabled
|
||||
if (navigator && !navigator.cookieEnabled) {
|
||||
d.reject({error: l('cookiesNotEnabled')});
|
||||
}
|
||||
else {
|
||||
if(data.redirect) {
|
||||
//Redirection in case of openID
|
||||
d.resolve({ url: data.redirect });
|
||||
}
|
||||
}
|
||||
}, function(error) {
|
||||
var response, perr, data = error.data;
|
||||
|
||||
d.reject(response);
|
||||
});
|
||||
return d.promise;
|
||||
},
|
||||
|
||||
changePassword: function(userName, domain, newPassword, oldPassword, token) {
|
||||
var d = $q.defer(),
|
||||
xsrfCookie = $cookies.get('XSRF-TOKEN');
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -210,9 +210,7 @@
|
||||
}
|
||||
url = url.join('/');
|
||||
popupWindow = $window.open(url, wId,
|
||||
["width=680",
|
||||
"height=520",
|
||||
"resizable=1",
|
||||
["resizable=1",
|
||||
"scrollbars=1",
|
||||
"toolbar=0",
|
||||
"location=0",
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -20,6 +20,8 @@
|
||||
domain: null,
|
||||
rememberLogin: angular.isDefined($window.cookieUsername) && $window.cookieUsername.length > 0
|
||||
};
|
||||
if($window.loginHint)
|
||||
this.creds.username = $window.loginHint;
|
||||
// Send selected language only if user has changed it
|
||||
if (/\blanguage=/.test($window.location.search))
|
||||
this.creds.language = $window.language;
|
||||
@@ -157,6 +159,64 @@
|
||||
return false;
|
||||
};
|
||||
|
||||
this.loginName = function() {
|
||||
vm.loginState = 'authenticating';
|
||||
Authentication.loginName(vm.creds)
|
||||
.then(function(data) {
|
||||
vm.loginState = 'logged';
|
||||
vm.cn = data.cn;
|
||||
vm.url = data.url;
|
||||
|
||||
// Let the user see the succesfull message before reloading the page
|
||||
$timeout(function() {
|
||||
vm.continueLogin();
|
||||
}, 1000);
|
||||
}, function(msg) {
|
||||
vm.loginState = 'error';
|
||||
|
||||
if (msg.error) {
|
||||
vm.errorMessage = msg.error;
|
||||
}
|
||||
else if (msg.grace > 0) {
|
||||
// Password is expired, grace logins limit is not yet reached
|
||||
vm.loginState = 'passwordwillexpire';
|
||||
vm.cn = msg.cn;
|
||||
vm.url = msg.url;
|
||||
vm.errorMessage = l('You have %{0} logins remaining before your account is locked. Please change your password in the preference dialog.', msg.grace);
|
||||
}
|
||||
else if (msg.expire > 0) {
|
||||
// Password will soon expire
|
||||
var value, string;
|
||||
if (msg.expire > 86400) {
|
||||
value = Math.round(msg.expire/86400);
|
||||
string = l("days");
|
||||
}
|
||||
else if (msg.expire > 3600) {
|
||||
value = Math.round(msg.expire/3600);
|
||||
string = l("hours");
|
||||
}
|
||||
else if (msg.expire > 60) {
|
||||
value = Math.round(msg.expire/60);
|
||||
string = l("minutes");
|
||||
}
|
||||
else {
|
||||
value = msg.expire;
|
||||
string = l("seconds");
|
||||
}
|
||||
vm.loginState = 'passwordwillexpire';
|
||||
vm.cn = msg.cn;
|
||||
vm.url = msg.url;
|
||||
vm.errorMessage = l('Your password is going to expire in %{0} %{1}.', value, string);
|
||||
}
|
||||
else if (msg.passwordexpired) {
|
||||
vm.loginState = 'passwordchange';
|
||||
vm.url = msg.url;
|
||||
}
|
||||
|
||||
});
|
||||
return false;
|
||||
};
|
||||
|
||||
this.restoreLogin = function() {
|
||||
vm.showLogin = false;
|
||||
if ('SecretQuestion' === vm.passwordRecovery.passwordRecoveryMode) {
|
||||
|
||||
Reference in New Issue
Block a user