mirror of
https://github.com/inverse-inc/sogo.git
synced 2026-02-17 07:33:57 +00:00
feat(openID): second part with a lot of fixes and cleaning
This commit is contained in:
@@ -1559,6 +1559,81 @@ SOGoIMAPCASServiceName should be set to the actual imap service name
|
|||||||
expected by pam_cas, otherwise it will fail to authenticate incoming
|
expected by pam_cas, otherwise it will fail to authenticate incoming
|
||||||
connection properly.
|
connection properly.
|
||||||
|
|
||||||
|
|
||||||
|
Authenticating using OPENID
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
SOGo natively supports OPENID authentication. For activating OpenId authentication you need first to make sure that
|
||||||
|
_SOGoAuthenticationType_ is set to `openid`,
|
||||||
|
_SOGoXSRFValidationEnabled_ is set to `NO` and set the following parameters:
|
||||||
|
|
||||||
|
[cols="^4,46,50a"]
|
||||||
|
|=======================================================================
|
||||||
|
|S |OCSOpenIdURL
|
||||||
|
|Parameter used to set the database URL for openID session.
|
||||||
|
|
||||||
|
For MariaDB/MySQL, set the database URL to something like: mysql://sogo:sogo@127.0.0.1:3306/sogo/sogo_openid.
|
||||||
|
|
||||||
|
|
||||||
|
|S |SOGoOpenIdConfigUrl
|
||||||
|
|Parameter used to specify the endpoint of OpenID Provider Configuration, mandatory. For example:
|
||||||
|
https://myopenid.net/.well-known/openid-configuration
|
||||||
|
|
||||||
|
|S |SOGoOpenIdClient
|
||||||
|
|Name of your openid client, mandatory.
|
||||||
|
|
||||||
|
|S |SOGoOpenIdClientSecret
|
||||||
|
|Secret of your openid client, mandatory
|
||||||
|
|
||||||
|
|S |SOGoOpenIdScope
|
||||||
|
|Scope or your openid client, mandatory. List of words space separated like this:
|
||||||
|
"openid profile email"
|
||||||
|
|
||||||
|
|S |SOGoOpenIdEmailParam
|
||||||
|
|Name of the parameter from user profile that contains the mail/uid.
|
||||||
|
|
||||||
|
Defaults to `email` when unset.
|
||||||
|
|
||||||
|
|S |SOGoOpenIdEnableRefreshToken
|
||||||
|
|Set to `YES` to Enable the mechanism of refresh token if provided. You may have to configure
|
||||||
|
and/or add a value to your scope for it to work.
|
||||||
|
|
||||||
|
Defaults to `NO` when unset.
|
||||||
|
|
||||||
|
|S |SOGoOpenIdTokenCheckInterval
|
||||||
|
|Number of seconds before sogo check again the user's access token validity.
|
||||||
|
This is to prevent sogo to do too much request to the openid server.
|
||||||
|
|
||||||
|
Defaults to `0` when unset.
|
||||||
|
|
||||||
|
|S |SOGoOpenIdLogoutEnabled
|
||||||
|
|Allow user to end their openId with the webmail. Meaning that will disconnect them from
|
||||||
|
the others applicaitons as well.
|
||||||
|
|
||||||
|
Defaults to `NO` when unset.
|
||||||
|
|=======================================================================
|
||||||
|
|
||||||
|
|
||||||
|
The tricky part shows up for the imap and smtp sever. SOGo doesn't know the password
|
||||||
|
of the user and only have its access token. A new auth mechanism has been implemented,
|
||||||
|
the https://developers.google.com/gmail/imap/xoauth2-protocol#initial_client_response[xoauth2]
|
||||||
|
|
||||||
|
You can set it with the parameter _NGImap4AuthMechanism_ and/or _SOGoSMTPAuthenticationType_
|
||||||
|
|
||||||
|
*With dovecot:* +
|
||||||
|
Dovecot natively supports xoauth2 and can be figured as such: https://doc.dovecot.org/2.3/configuration_manual/authentication/oauth2/
|
||||||
|
|
||||||
|
*With cyrus:* +
|
||||||
|
Cyrus doesn't support xoauth2 mechanism and pluggins or homemade solutions must be found. +
|
||||||
|
_Please note, as Alinto uses dovecot, we didn't investigate cyrus' case. If one member of the community
|
||||||
|
finds a solution, we will be happy to update this documentation._
|
||||||
|
|
||||||
|
As you can see, a new database table is used for handling openid session. The table is automaticcaly created when _OCSOpenIdURL_ is set.
|
||||||
|
If the user quits the webmail without logging out or trough another application,
|
||||||
|
the session will stays in the table and be useless. That's why a new sogo-tool command has been added to clean this table.
|
||||||
|
You can put it in a cron to do that periodicly. +
|
||||||
|
See _<<sogo-tool-clean-openid-sessions,sogo-tool clean-openid-sessions>>_.
|
||||||
|
|
||||||
Authenticating using SAML2
|
Authenticating using SAML2
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
@@ -2113,8 +2188,7 @@ To disable TLS verification for localhost domains, add
|
|||||||
|
|
||||||
|D |SOGoSMTPAuthenticationType
|
|D |SOGoSMTPAuthenticationType
|
||||||
|Activate SMTP authentication and specifies which type is in use.
|
|Activate SMTP authentication and specifies which type is in use.
|
||||||
Current, only `PLAIN` is supported and other values will cause
|
Current, Are supported `PLAIN` and 'xoauth2' for openid.
|
||||||
the authentication to fail.
|
|
||||||
|
|
||||||
|D |SOGoSMTPMasterUserEnabled
|
|D |SOGoSMTPMasterUserEnabled
|
||||||
|Enable specific SMTP user account for system e-mails (notifications, reminders, ...). Default is `NO`.
|
|Enable specific SMTP user account for system e-mails (notifications, reminders, ...). Default is `NO`.
|
||||||
@@ -2297,6 +2371,7 @@ SASL mechanism. Using `AUTHENTICATE` instead of `LOGIN` is also necessary
|
|||||||
to enable UTF-8 characters in users' passwords. To enable simple use of
|
to enable UTF-8 characters in users' passwords. To enable simple use of
|
||||||
`AUTHENTICATE` for this purpose, set this setting to `plain`. Please note
|
`AUTHENTICATE` for this purpose, set this setting to `plain`. Please note
|
||||||
that this feature might be limited at this time.
|
that this feature might be limited at this time.
|
||||||
|
Now support `xoauth2` mechanism when using openid. Be sure you imap server undesrtands this mechanism.
|
||||||
|
|
||||||
|D |NGImap4ConnectionGroupIdPrefix
|
|D |NGImap4ConnectionGroupIdPrefix
|
||||||
|Prefix to prepend to names in IMAP ACL transactions, to indicate the
|
|Prefix to prepend to names in IMAP ACL transactions, to indicate the
|
||||||
@@ -3630,6 +3705,22 @@ sogo-tool checkup user1
|
|||||||
sogo-tool checkup -d user1
|
sogo-tool checkup -d user1
|
||||||
----
|
----
|
||||||
|
|
||||||
|
|
||||||
|
[[sogo-tool-clean-openid-sessions]]
|
||||||
|
|
||||||
|
sogo-tool clean-openid-sessions
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Obviously only usefull if you have set SOGo with openId authentication.
|
||||||
|
Will clean all expired openId sessions from the database.
|
||||||
|
|
||||||
|
sogo-tool clean-openid-sessions
|
||||||
|
|
||||||
|
Example:
|
||||||
|
----
|
||||||
|
sogo-tool clean-openid-sessions
|
||||||
|
----
|
||||||
|
|
||||||
sogo-tool cleanup
|
sogo-tool cleanup
|
||||||
~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|||||||
12
Main/SOGo.m
12
Main/SOGo.m
@@ -327,17 +327,9 @@ static BOOL debugLeaks;
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Create mandatory openId table, if used
|
//Create mandatory openId table, if used
|
||||||
if([[defaults authenticationType] isEqualToString: @"openid"])
|
if([defaults hasOpenIdType])
|
||||||
{
|
{
|
||||||
value = [defaults stringForKey: @"OCSOpenIdURL"];
|
[[fm openIdFolder] createFolderIfNotExists];
|
||||||
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
|
- (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
|
- (NGImap4Connection *) _createIMAP4Connection
|
||||||
{
|
{
|
||||||
NGImap4ConnectionManager *manager;
|
NGImap4ConnectionManager *manager;
|
||||||
NGImap4Connection *newConnection;
|
NGImap4Connection *newConnection;
|
||||||
NSString *password;
|
NSString *password, *domain;
|
||||||
NGInternetSocketAddress *host;
|
NGInternetSocketAddress *host;
|
||||||
SOGoSystemDefaults *sd;
|
SOGoSystemDefaults *sd;
|
||||||
BOOL usesSSO;
|
BOOL usesSSO;
|
||||||
@@ -152,12 +162,15 @@
|
|||||||
host = [NGInternetSocketAddress addressWithPort:0 onHost:[[self imap4URL] host]];
|
host = [NGInternetSocketAddress addressWithPort:0 onHost:[[self imap4URL] host]];
|
||||||
|
|
||||||
sd = [SOGoSystemDefaults sharedSystemDefaults];
|
sd = [SOGoSystemDefaults sharedSystemDefaults];
|
||||||
usesSSO = [sd isSsoUsed];
|
|
||||||
|
domain = [[[self context] activeUser] loginDomain];
|
||||||
|
usesSSO = [sd isSsoUsed: domain];
|
||||||
|
|
||||||
if (![[[self mailAccountFolder] nameInContainer] isEqualToString: @"0"] &&
|
if (![[[self mailAccountFolder] nameInContainer] isEqualToString: @"0"] &&
|
||||||
usesSSO &&
|
usesSSO &&
|
||||||
[host isLocalhost])
|
[host isLocalhost])
|
||||||
{
|
{
|
||||||
|
//
|
||||||
[self errorWithFormat: @"Trying to use localhost for additional IMAP account - aborting."];
|
[self errorWithFormat: @"Trying to use localhost for additional IMAP account - aborting."];
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
@@ -227,19 +240,17 @@
|
|||||||
login = [[[self context] activeUser] login];
|
login = [[[self context] activeUser] login];
|
||||||
|
|
||||||
if (!login)
|
if (!login)
|
||||||
login = [[[[self container] context] activeUser] login];
|
login = [[[[self container] context] activeUser] login];
|
||||||
|
|
||||||
cacheKey = [NSString stringWithFormat: @"%@+%@",
|
cacheKey = [NSString stringWithFormat: @"%@+%@", login, [[self mailAccountFolder] nameInContainer]];
|
||||||
login,
|
|
||||||
[[self mailAccountFolder] nameInContainer]];
|
|
||||||
imap4 = [sogoCache imap4ConnectionForKey: cacheKey];
|
imap4 = [sogoCache imap4ConnectionForKey: cacheKey];
|
||||||
if (!imap4)
|
if (!imap4)
|
||||||
{
|
{
|
||||||
imap4 = [self _createIMAP4Connection];
|
imap4 = [self _createIMAP4Connection];
|
||||||
[sogoCache registerIMAP4Connection: imap4
|
[sogoCache registerIMAP4Connection: imap4
|
||||||
forKey: cacheKey];
|
forKey: cacheKey];
|
||||||
}
|
}
|
||||||
[imap4 retain];
|
[imap4 retain];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connection broken, try to reconnect
|
// Connection broken, try to reconnect
|
||||||
|
|||||||
@@ -95,6 +95,7 @@
|
|||||||
return keysWithFormat;
|
return keysWithFormat;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSComparisonResult) caseInsensitiveDisplayNameCompare: (NSDictionary *) theDictionary
|
- (NSComparisonResult) caseInsensitiveDisplayNameCompare: (NSDictionary *) theDictionary
|
||||||
{
|
{
|
||||||
return [[self objectForKey: @"cn"] caseInsensitiveCompare: [theDictionary objectForKey: @"cn"]];
|
return [[self objectForKey: @"cn"] caseInsensitiveCompare: [theDictionary objectForKey: @"cn"]];
|
||||||
|
|||||||
@@ -80,7 +80,10 @@ static int cssEscapingCount;
|
|||||||
{
|
{
|
||||||
hostR = [self rangeOfString: @"://"];
|
hostR = [self rangeOfString: @"://"];
|
||||||
locationR = [[self substringFromIndex: (hostR.location + hostR.length)] rangeOfString: @"/"];
|
locationR = [[self substringFromIndex: (hostR.location + hostR.length)] rangeOfString: @"/"];
|
||||||
newURL = [self substringFromIndex: (hostR.location + hostR.length + locationR.location)];
|
if(locationR.location != NSNotFound)
|
||||||
|
newURL = [self substringFromIndex: (hostR.location + hostR.length + locationR.location)];
|
||||||
|
else
|
||||||
|
newURL = @"";
|
||||||
}
|
}
|
||||||
|
|
||||||
return newURL;
|
return newURL;
|
||||||
|
|||||||
@@ -141,6 +141,7 @@
|
|||||||
return password;
|
return password;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* create SOGoUser */
|
/* create SOGoUser */
|
||||||
|
|
||||||
- (SOGoUser *) userInContext: (WOContext *)_ctx
|
- (SOGoUser *) userInContext: (WOContext *)_ctx
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ extern NSString *SOGoDefaultsSourceUnmutableSource;
|
|||||||
|
|
||||||
- (void) setBool: (BOOL) value forKey: (NSString *) key;
|
- (void) setBool: (BOOL) value forKey: (NSString *) key;
|
||||||
- (BOOL) boolForKey: (NSString *) key;
|
- (BOOL) boolForKey: (NSString *) key;
|
||||||
|
- (BOOL) boolForKey: (NSString *) key andDict: (NSDictionary*) _dict;
|
||||||
|
|
||||||
- (void) setFloat: (float) value forKey: (NSString *) key;
|
- (void) setFloat: (float) value forKey: (NSString *) key;
|
||||||
- (float) floatForKey: (NSString *) key;
|
- (float) floatForKey: (NSString *) key;
|
||||||
|
|||||||
@@ -164,6 +164,29 @@ static Class NSStringKlass = Nil;
|
|||||||
return value;
|
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
|
- (void) setFloat: (float) value
|
||||||
forKey: (NSString *) key
|
forKey: (NSString *) key
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
@class NSArray;
|
@class NSArray;
|
||||||
@class NSException;
|
@class NSException;
|
||||||
@class NSString;
|
@class NSString;
|
||||||
|
@class NSDictionary;
|
||||||
|
|
||||||
@class WOContext;
|
@class WOContext;
|
||||||
@class SOGoDomainDefaults;
|
@class SOGoDomainDefaults;
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
#import "NSString+Utilities.h"
|
#import "NSString+Utilities.h"
|
||||||
#import "SOGoStaticAuthenticator.h"
|
#import "SOGoStaticAuthenticator.h"
|
||||||
#import "SOGoEmptyAuthenticator.h"
|
#import "SOGoEmptyAuthenticator.h"
|
||||||
|
#import "SOGoWebAuthenticator.h"
|
||||||
#import "SOGoSystemDefaults.h"
|
#import "SOGoSystemDefaults.h"
|
||||||
#import "SOGoUser.h"
|
#import "SOGoUser.h"
|
||||||
#import "SOGoUserManager.h"
|
#import "SOGoUserManager.h"
|
||||||
@@ -265,7 +266,7 @@
|
|||||||
inContext: (WOContext *) woContext
|
inContext: (WOContext *) woContext
|
||||||
systemMessage: (BOOL) isSystemMessage
|
systemMessage: (BOOL) isSystemMessage
|
||||||
{
|
{
|
||||||
NSString *currentTo, *login, *password;
|
NSString *currentTo, *login, *password, *encryption, *protocol, *server;
|
||||||
NSString * smtpAuthMethod;
|
NSString * smtpAuthMethod;
|
||||||
NSDictionary *currentAcount;
|
NSDictionary *currentAcount;
|
||||||
NSMutableArray *toErrors;
|
NSMutableArray *toErrors;
|
||||||
@@ -274,6 +275,7 @@
|
|||||||
NSException *result;
|
NSException *result;
|
||||||
NSURL * smtpUrl;
|
NSURL * smtpUrl;
|
||||||
SOGoUser* user;
|
SOGoUser* user;
|
||||||
|
SOGoSystemDefaults *sd;
|
||||||
BOOL doSmtpAuth;
|
BOOL doSmtpAuth;
|
||||||
|
|
||||||
result = nil;
|
result = nil;
|
||||||
@@ -297,6 +299,22 @@
|
|||||||
doSmtpAuth = [currentAcount objectForKey: @"smtpAuth"] ? [[currentAcount objectForKey: @"smtpAuth"] boolValue] : NO;
|
doSmtpAuth = [currentAcount objectForKey: @"smtpAuth"] ? [[currentAcount objectForKey: @"smtpAuth"] boolValue] : NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sd = [SOGoSystemDefaults sharedSystemDefaults];
|
||||||
|
if(userId == 0 && [sd doesLoginTypeByDomain] && [authenticator isKindOfClass: [SOGoWebAuthenticator class]])
|
||||||
|
{
|
||||||
|
//Check if the authentication depends on the domain, only for webmail requets not for dav...
|
||||||
|
NSString *username, *_domain;
|
||||||
|
NSRange r;
|
||||||
|
|
||||||
|
username = [currentAcount objectForKey: @"userName"];
|
||||||
|
r = [username rangeOfString: @"@"];
|
||||||
|
if (r.location != NSNotFound)
|
||||||
|
{
|
||||||
|
_domain = [username substringFromIndex: r.location+1];
|
||||||
|
smtpAuthMethod = [sd getSmtpAuthMechForDomain: _domain];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
NS_DURING
|
NS_DURING
|
||||||
{
|
{
|
||||||
[client connect];
|
[client connect];
|
||||||
@@ -319,7 +337,18 @@
|
|||||||
getExternalLoginForUID: [[authenticator userInContext: woContext] loginInDomain]
|
getExternalLoginForUID: [[authenticator userInContext: woContext] loginInDomain]
|
||||||
inDomain: [[authenticator userInContext: woContext] domain]];
|
inDomain: [[authenticator userInContext: woContext] domain]];
|
||||||
|
|
||||||
password = [authenticator passwordInContext: woContext];
|
encryption = [currentAcount objectForKey: @"encryption"];
|
||||||
|
protocol = @"imap";
|
||||||
|
if ([encryption isEqualToString: @"ssl"] || [encryption isEqualToString: @"tls"])
|
||||||
|
protocol = @"imaps";
|
||||||
|
server = [NSString stringWithFormat: @"%@://%@", protocol, [currentAcount objectForKey: @"serverName"]];
|
||||||
|
if([authenticator isKindOfClass: [SOGoWebAuthenticator class]])
|
||||||
|
password = [authenticator smtpPasswordInContext: woContext forURL: server];
|
||||||
|
else
|
||||||
|
{
|
||||||
|
password = [authenticator passwordInContext: woContext];
|
||||||
|
smtpAuthMethod = @"plain"; //is a dav or activesync request, only support plain method
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
https://openid.net/developers/how-connect-works/ */
|
https://openid.net/developers/how-connect-works/ */
|
||||||
|
|
||||||
#import <NGObjWeb/WOResponse.h>
|
#import <NGObjWeb/WOResponse.h>
|
||||||
|
#import <SOGo/SOGoObject.h>
|
||||||
|
|
||||||
|
|
||||||
@class NSString;
|
@class NSString;
|
||||||
@@ -33,7 +34,7 @@
|
|||||||
@class NSJSONSerialization;
|
@class NSJSONSerialization;
|
||||||
|
|
||||||
|
|
||||||
@interface SOGoOpenIdSession : NSObject
|
@interface SOGoOpenIdSession : SOGoObject
|
||||||
{
|
{
|
||||||
//For cache
|
//For cache
|
||||||
BOOL cacheUpdateNeeded;
|
BOOL cacheUpdateNeeded;
|
||||||
@@ -47,6 +48,10 @@
|
|||||||
NSString *openIdClientSecret;
|
NSString *openIdClientSecret;
|
||||||
NSString *openIdEmailParam;
|
NSString *openIdEmailParam;
|
||||||
BOOL openIdEnableRefreshToken;
|
BOOL openIdEnableRefreshToken;
|
||||||
|
BOOL sendDomainInfo;
|
||||||
|
|
||||||
|
NSString *forDomain;
|
||||||
|
|
||||||
|
|
||||||
//From request to well-known/configuration
|
//From request to well-known/configuration
|
||||||
NSString *authorizationEndpoint;
|
NSString *authorizationEndpoint;
|
||||||
@@ -66,16 +71,20 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
+ (BOOL) checkUserConfig;
|
+ (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) deleteValueForSessionKey: (NSString *) theSessionKey;
|
||||||
|
|
||||||
- (void) initialize;
|
- (void) initialize;
|
||||||
|
- (void) initializeWithConfig: (NSDictionary *) _config;
|
||||||
- (BOOL) sessionIsOK;
|
- (BOOL) sessionIsOK;
|
||||||
- (WOResponse *) _performOpenIdRequest: (NSString *) endpoint
|
- (WOResponse *) _performOpenIdRequest: (NSString *) endpoint
|
||||||
method: (NSString *) method
|
method: (NSString *) method
|
||||||
headers: (NSDictionary *) headers
|
headers: (NSDictionary *) headers
|
||||||
body: (NSData *) body;
|
body: (NSData *) body;
|
||||||
- (NSMutableDictionary *) fecthConfiguration;
|
- (NSMutableDictionary *) fecthConfiguration: (NSString *) _domain;
|
||||||
- (void) setAccessToken;
|
- (void) setAccessToken;
|
||||||
- (NSString *) getRefreshToken;
|
- (NSString *) getRefreshToken;
|
||||||
- (NSString *) getToken;
|
- (NSString *) getToken;
|
||||||
|
|||||||
@@ -23,6 +23,9 @@
|
|||||||
#import <NGObjWeb/WOResponse.h>
|
#import <NGObjWeb/WOResponse.h>
|
||||||
#import <NGExtensions/NSObject+Logs.h>
|
#import <NGExtensions/NSObject+Logs.h>
|
||||||
|
|
||||||
|
#import <SOGo/SOGoUser.h>
|
||||||
|
|
||||||
|
|
||||||
#import <GDLContentStore/GCSOpenIdFolder.h>
|
#import <GDLContentStore/GCSOpenIdFolder.h>
|
||||||
#import <GDLContentStore/GCSFolderManager.h>
|
#import <GDLContentStore/GCSFolderManager.h>
|
||||||
|
|
||||||
@@ -52,32 +55,73 @@ static BOOL SOGoOpenIDDebugEnabled = YES;
|
|||||||
return ([sd openIdConfigUrl] && [sd openIdScope] && [sd openIdClient] && [sd openIdClientSecret]);
|
return ([sd openIdConfigUrl] && [sd openIdScope] && [sd openIdClient] && [sd openIdClientSecret]);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) initialize
|
- (void) initializeWithConfig: (NSDictionary *) _config
|
||||||
{
|
{
|
||||||
SOGoSystemDefaults *sd;
|
SOGoSystemDefaults *sd;
|
||||||
|
id refreshTokenBool, domainInfo;
|
||||||
|
|
||||||
// //From sogo.conf
|
if([_config objectForKey: @"SOGoOpenIdConfigUrl"] &&
|
||||||
// openIdConfigUrl = nil;
|
[_config objectForKey: @"SOGoOpenIdScope"] &&
|
||||||
// openIdScope = nil;
|
[_config objectForKey: @"SOGoOpenIdClient"] &&
|
||||||
// openIdClient = nil;
|
[_config objectForKey: @"SOGoOpenIdClientSecret"])
|
||||||
// openIdClientSecret = nil;
|
{
|
||||||
|
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
|
openIdEnableRefreshToken = NO;
|
||||||
// //SHoud be ste in sogo.cong in case of oauth
|
refreshTokenBool = [_config objectForKey: @"SOGoOpenIdEnableRefreshToken"];
|
||||||
// authorizationEndpoint = nil;
|
if (refreshTokenBool && [refreshTokenBool respondsToSelector: @selector (boolValue)])
|
||||||
// tokenEndpoint = nil;
|
openIdEnableRefreshToken = [refreshTokenBool boolValue];
|
||||||
// introspectionEndpoint = nil;
|
|
||||||
// userinfoEndpoint = nil;
|
|
||||||
// endSessionEndpoint = nil;
|
|
||||||
// revocationEndpoint = nil;
|
|
||||||
|
|
||||||
// //Access token
|
sendDomainInfo = NO;
|
||||||
// accessToken = nil;
|
domainInfo = [_config objectForKey: @"SOGoOpenIdSendDomainInfo"];
|
||||||
|
if (domainInfo && [domainInfo respondsToSelector: @selector (boolValue)])
|
||||||
|
sendDomainInfo = [domainInfo boolValue];
|
||||||
|
|
||||||
|
|
||||||
|
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];
|
sd = [SOGoSystemDefaults sharedSystemDefaults];
|
||||||
SOGoOpenIDDebugEnabled = [sd openIdDebugEnabled];
|
SOGoOpenIDDebugEnabled = [sd openIdDebugEnabled];
|
||||||
openIdSessionIsOK = NO;
|
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];
|
openIdConfigUrl = [sd openIdConfigUrl];
|
||||||
openIdScope = [sd openIdScope];
|
openIdScope = [sd openIdScope];
|
||||||
@@ -86,12 +130,14 @@ static BOOL SOGoOpenIDDebugEnabled = YES;
|
|||||||
openIdEmailParam = [sd openIdEmailParam];
|
openIdEmailParam = [sd openIdEmailParam];
|
||||||
openIdEnableRefreshToken = [sd openIdEnableRefreshToken];
|
openIdEnableRefreshToken = [sd openIdEnableRefreshToken];
|
||||||
userTokenInterval = [sd openIdTokenCheckInterval];
|
userTokenInterval = [sd openIdTokenCheckInterval];
|
||||||
|
sendDomainInfo = [sd openIdSendDomainInfo];
|
||||||
|
forDomain = _domain;
|
||||||
|
|
||||||
[self _loadSessionFromCache];
|
[self _loadSessionFromCache: _domain];
|
||||||
|
|
||||||
if(cacheUpdateNeeded)
|
if(cacheUpdateNeeded)
|
||||||
{
|
{
|
||||||
[self fecthConfiguration];
|
[self fecthConfiguration: _domain];
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -101,6 +147,7 @@ static BOOL SOGoOpenIDDebugEnabled = YES;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (WOResponse *) _performOpenIdRequest: (NSString *) endpoint
|
- (WOResponse *) _performOpenIdRequest: (NSString *) endpoint
|
||||||
method: (NSString *) method
|
method: (NSString *) method
|
||||||
headers: (NSDictionary *) headers
|
headers: (NSDictionary *) headers
|
||||||
@@ -112,16 +159,16 @@ static BOOL SOGoOpenIDDebugEnabled = YES;
|
|||||||
WOResponse *response;
|
WOResponse *response;
|
||||||
WOHTTPConnection *httpConnection;
|
WOHTTPConnection *httpConnection;
|
||||||
|
|
||||||
|
|
||||||
url = [NSURL URLWithString: endpoint];
|
url = [NSURL URLWithString: endpoint];
|
||||||
if (url)
|
if (url)
|
||||||
{
|
{
|
||||||
if(SOGoOpenIDDebugEnabled)
|
if(SOGoOpenIDDebugEnabled)
|
||||||
{
|
{
|
||||||
NSLog(@"OpenId perform request: %@ %@", method, [endpoint hostlessURL]);
|
NSLog(@"OpenId perform request: %@ %@", method, endpoint);
|
||||||
NSLog(@"OpenId perform request, headers %@", headers);
|
NSLog(@"OpenId perform request, headers %@", headers);
|
||||||
if(body)
|
// if(body)
|
||||||
NSLog(@"OpenId perform request: content %@", [[NSString alloc] initWithData:body encoding:NSUTF8StringEncoding]);
|
// NSLog(@"OpenId perform request: content %@", [[NSString alloc] initWithData:body encoding:NSUTF8StringEncoding]);
|
||||||
}
|
}
|
||||||
|
|
||||||
httpConnection = [[WOHTTPConnection alloc] initWithURL: url];
|
httpConnection = [[WOHTTPConnection alloc] initWithURL: url];
|
||||||
@@ -156,13 +203,13 @@ static BOOL SOGoOpenIDDebugEnabled = YES;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSMutableDictionary *) fecthConfiguration
|
- (NSMutableDictionary *) fecthConfiguration: (NSString*) _domain
|
||||||
{
|
{
|
||||||
NSString *location, *content;
|
NSString *content;
|
||||||
WOResponse * response;
|
WOResponse * response;
|
||||||
NSUInteger status;
|
NSUInteger status;
|
||||||
NSMutableDictionary *result;
|
NSMutableDictionary *result;
|
||||||
NSDictionary *config;
|
NSDictionary *config, *headers;
|
||||||
NSURL *url;
|
NSURL *url;
|
||||||
|
|
||||||
result = [NSMutableDictionary dictionary];
|
result = [NSMutableDictionary dictionary];
|
||||||
@@ -171,9 +218,14 @@ static BOOL SOGoOpenIDDebugEnabled = YES;
|
|||||||
url = [NSURL URLWithString: self->openIdConfigUrl ];
|
url = [NSURL URLWithString: self->openIdConfigUrl ];
|
||||||
if (url)
|
if (url)
|
||||||
{
|
{
|
||||||
|
if(self->sendDomainInfo && _domain != nil && [_domain length] > 0)
|
||||||
|
headers = [NSDictionary dictionaryWithObject: _domain forKey: @"sogo-user-domain"];
|
||||||
|
else
|
||||||
|
headers = nil;
|
||||||
|
|
||||||
response = [self _performOpenIdRequest: self->openIdConfigUrl
|
response = [self _performOpenIdRequest: self->openIdConfigUrl
|
||||||
method: @"GET"
|
method: @"GET"
|
||||||
headers: nil
|
headers: headers
|
||||||
body: nil];
|
body: nil];
|
||||||
|
|
||||||
if (response)
|
if (response)
|
||||||
@@ -185,12 +237,17 @@ static BOOL SOGoOpenIDDebugEnabled = YES;
|
|||||||
config = [content objectFromJSONString];
|
config = [content objectFromJSONString];
|
||||||
self->authorizationEndpoint = [config objectForKey: @"authorization_endpoint"];
|
self->authorizationEndpoint = [config objectForKey: @"authorization_endpoint"];
|
||||||
self->tokenEndpoint = [config objectForKey: @"token_endpoint"];
|
self->tokenEndpoint = [config objectForKey: @"token_endpoint"];
|
||||||
self->introspectionEndpoint = [config objectForKey: @"introspection_endpoint"];
|
|
||||||
self->userinfoEndpoint = [config objectForKey: @"userinfo_endpoint"];
|
self->userinfoEndpoint = [config objectForKey: @"userinfo_endpoint"];
|
||||||
self->endSessionEndpoint = [config objectForKey: @"end_session_endpoint"];
|
self->endSessionEndpoint = [config objectForKey: @"end_session_endpoint"];
|
||||||
self->revocationEndpoint = [config objectForKey: @"revocation_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;
|
openIdSessionIsOK = YES;
|
||||||
[self _saveSessionToCache];
|
[self _saveSessionToCache: _domain];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -206,18 +263,29 @@ static BOOL SOGoOpenIDDebugEnabled = YES;
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (SOGoOpenIdSession *) OpenIdSession
|
+ (SOGoOpenIdSession *) OpenIdSession: (NSString *) _domain
|
||||||
{
|
{
|
||||||
SOGoOpenIdSession *newSession;
|
SOGoOpenIdSession *newSession;
|
||||||
|
|
||||||
newSession = [self new];
|
newSession = [self new];
|
||||||
[newSession autorelease];
|
[newSession autorelease];
|
||||||
[newSession initialize];
|
[newSession initialize: _domain];
|
||||||
|
|
||||||
return newSession;
|
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;
|
SOGoOpenIdSession *newSession;
|
||||||
|
|
||||||
@@ -225,7 +293,7 @@ static BOOL SOGoOpenIDDebugEnabled = YES;
|
|||||||
{
|
{
|
||||||
newSession = [self new];
|
newSession = [self new];
|
||||||
[newSession autorelease];
|
[newSession autorelease];
|
||||||
[newSession initialize];
|
[newSession initialize: _domain];
|
||||||
|
|
||||||
[newSession setAccessToken: token];
|
[newSession setAccessToken: token];
|
||||||
}
|
}
|
||||||
@@ -235,52 +303,90 @@ static BOOL SOGoOpenIDDebugEnabled = YES;
|
|||||||
return newSession;
|
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
|
- (BOOL) sessionIsOk
|
||||||
{
|
{
|
||||||
return self->openIdSessionIsOK;
|
return self->openIdSessionIsOK;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) _loadSessionFromCache
|
- (void) _loadSessionFromCache: (NSString*) _domain
|
||||||
{
|
{
|
||||||
SOGoCache *cache;
|
SOGoCache *cache;
|
||||||
NSString *jsonSession;
|
NSString *jsonSession, *cacheKey;
|
||||||
NSDictionary *sessionDict;
|
NSDictionary *sessionDict;
|
||||||
|
|
||||||
|
if(_domain != nil && [_domain length] > 0)
|
||||||
|
cacheKey = [self->openIdConfigUrl stringByAppendingFormat: @":%@", _domain];
|
||||||
|
else
|
||||||
|
cacheKey = self->openIdConfigUrl;
|
||||||
|
|
||||||
cache = [SOGoCache sharedCache];
|
cache = [SOGoCache sharedCache];
|
||||||
jsonSession = [cache openIdSessionFromServer: self->openIdConfigUrl];
|
jsonSession = [cache openIdSessionFromServer: cacheKey];
|
||||||
if ([jsonSession length])
|
if ([jsonSession length])
|
||||||
{
|
{
|
||||||
sessionDict = [jsonSession objectFromJSONString];
|
sessionDict = [jsonSession objectFromJSONString];
|
||||||
ASSIGN (authorizationEndpoint, [sessionDict objectForKey: @"authorization_endpoint"]);
|
ASSIGN (authorizationEndpoint, [sessionDict objectForKey: @"authorization_endpoint"]);
|
||||||
ASSIGN (tokenEndpoint, [sessionDict objectForKey: @"token_endpoint"]);
|
ASSIGN (tokenEndpoint, [sessionDict objectForKey: @"token_endpoint"]);
|
||||||
ASSIGN (introspectionEndpoint, [sessionDict objectForKey: @"introspection_endpoint"]);
|
|
||||||
ASSIGN (userinfoEndpoint, [sessionDict objectForKey: @"userinfo_endpoint"]);
|
ASSIGN (userinfoEndpoint, [sessionDict objectForKey: @"userinfo_endpoint"]);
|
||||||
ASSIGN (endSessionEndpoint, [sessionDict objectForKey: @"end_session_endpoint"]);
|
ASSIGN (endSessionEndpoint, [sessionDict objectForKey: @"end_session_endpoint"]);
|
||||||
ASSIGN (revocationEndpoint, [sessionDict objectForKey: @"revocation_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;
|
openIdSessionIsOK = YES;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
cacheUpdateNeeded = YES;
|
cacheUpdateNeeded = YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) _saveSessionToCache
|
- (void) _saveSessionToCache: (NSString*) _domain
|
||||||
{
|
{
|
||||||
SOGoCache *cache;
|
SOGoCache *cache;
|
||||||
NSString *jsonSession;
|
NSString *jsonSession, *cacheKey;
|
||||||
NSMutableDictionary *sessionDict;
|
NSMutableDictionary *sessionDict;
|
||||||
|
|
||||||
cache = [SOGoCache sharedCache];
|
cache = [SOGoCache sharedCache];
|
||||||
sessionDict = [NSMutableDictionary dictionary];
|
sessionDict = [NSMutableDictionary dictionary];
|
||||||
[sessionDict setObject: authorizationEndpoint forKey: @"authorization_endpoint"];
|
[sessionDict setObject: authorizationEndpoint forKey: @"authorization_endpoint"];
|
||||||
[sessionDict setObject: tokenEndpoint forKey: @"token_endpoint"];
|
[sessionDict setObject: tokenEndpoint forKey: @"token_endpoint"];
|
||||||
[sessionDict setObject: introspectionEndpoint forKey: @"introspection_endpoint"];
|
|
||||||
[sessionDict setObject: userinfoEndpoint forKey: @"userinfo_endpoint"];
|
[sessionDict setObject: userinfoEndpoint forKey: @"userinfo_endpoint"];
|
||||||
[sessionDict setObject: endSessionEndpoint forKey: @"end_session_endpoint"];
|
[sessionDict setObject: endSessionEndpoint forKey: @"end_session_endpoint"];
|
||||||
[sessionDict setObject: revocationEndpoint forKey: @"revocation_endpoint"];
|
|
||||||
|
//Optionnals?
|
||||||
|
if(introspectionEndpoint)
|
||||||
|
[sessionDict setObject: introspectionEndpoint forKey: @"introspection_endpoint"];
|
||||||
|
if(revocationEndpoint)
|
||||||
|
[sessionDict setObject: revocationEndpoint forKey: @"revocation_endpoint"];
|
||||||
|
|
||||||
jsonSession = [sessionDict jsonRepresentation];
|
jsonSession = [sessionDict jsonRepresentation];
|
||||||
|
|
||||||
|
if(_domain != nil && [_domain length] > 0)
|
||||||
|
cacheKey = [self->openIdConfigUrl stringByAppendingFormat: @":%@", _domain];
|
||||||
|
else
|
||||||
|
cacheKey = self->openIdConfigUrl;
|
||||||
|
|
||||||
[cache setOpenIdSession: jsonSession
|
[cache setOpenIdSession: jsonSession
|
||||||
forServer: self->openIdConfigUrl];
|
forServer: cacheKey];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -332,6 +438,8 @@ static BOOL SOGoOpenIDDebugEnabled = YES;
|
|||||||
logUrl = [logUrl stringByAppendingString: @"&response_type=code"];
|
logUrl = [logUrl stringByAppendingString: @"&response_type=code"];
|
||||||
logUrl = [logUrl stringByAppendingFormat: @"&client_id=%@", self->openIdClient];
|
logUrl = [logUrl stringByAppendingFormat: @"&client_id=%@", self->openIdClient];
|
||||||
logUrl = [logUrl stringByAppendingFormat: @"&redirect_uri=%@", oldLocation];
|
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];
|
// logurl = [self->logurl stringByAppendingFormat: @"&state=%@", state];
|
||||||
|
|
||||||
return logUrl;
|
return logUrl;
|
||||||
@@ -397,7 +505,11 @@ static BOOL SOGoOpenIDDebugEnabled = YES;
|
|||||||
form = [form stringByAppendingFormat: @"&client_secret=%@", self->openIdClientSecret];
|
form = [form stringByAppendingFormat: @"&client_secret=%@", self->openIdClientSecret];
|
||||||
form = [form stringByAppendingFormat: @"&client_id=%@", self->openIdClient];
|
form = [form stringByAppendingFormat: @"&client_id=%@", self->openIdClient];
|
||||||
|
|
||||||
headers = [NSDictionary dictionaryWithObject: @"application/x-www-form-urlencoded" forKey: @"content-type"];
|
if(self->sendDomainInfo && 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
|
response = [self _performOpenIdRequest: location
|
||||||
method: @"POST"
|
method: @"POST"
|
||||||
@@ -467,7 +579,11 @@ static BOOL SOGoOpenIDDebugEnabled = YES;
|
|||||||
form = [form stringByAppendingFormat: @"&client_secret=%@", self->openIdClientSecret];
|
form = [form stringByAppendingFormat: @"&client_secret=%@", self->openIdClientSecret];
|
||||||
form = [form stringByAppendingFormat: @"&client_id=%@", self->openIdClient];
|
form = [form stringByAppendingFormat: @"&client_id=%@", self->openIdClient];
|
||||||
|
|
||||||
headers = [NSDictionary dictionaryWithObject: @"application/x-www-form-urlencoded" forKey: @"content-type"];
|
if(self->sendDomainInfo && 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
|
response = [self _performOpenIdRequest: location
|
||||||
method: @"POST"
|
method: @"POST"
|
||||||
@@ -525,7 +641,13 @@ static BOOL SOGoOpenIDDebugEnabled = YES;
|
|||||||
if (url)
|
if (url)
|
||||||
{
|
{
|
||||||
auth = [NSString stringWithFormat: @"Bearer %@", self->accessToken];
|
auth = [NSString stringWithFormat: @"Bearer %@", self->accessToken];
|
||||||
headers = [NSDictionary dictionaryWithObject: auth forKey: @"authorization"];
|
if(self->sendDomainInfo && 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 dictionaryWithObjectsAndKeys: @"application/x-www-form-urlencoded", @"content-type",
|
||||||
|
auth, @"authorization", nil];
|
||||||
|
|
||||||
response = [self _performOpenIdRequest: location
|
response = [self _performOpenIdRequest: location
|
||||||
method: @"GET"
|
method: @"GET"
|
||||||
@@ -646,7 +768,7 @@ static BOOL SOGoOpenIDDebugEnabled = YES;
|
|||||||
return @"anonymous";
|
return @"anonymous";
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL) login: (NSString *) email
|
- (NSString *) login: (NSString *) email
|
||||||
{
|
{
|
||||||
//Check if we need to fetch userinfo
|
//Check if we need to fetch userinfo
|
||||||
if(self->userTokenInterval > 0 && [self _loadUserFromCache: email])
|
if(self->userTokenInterval > 0 && [self _loadUserFromCache: email])
|
||||||
|
|||||||
@@ -247,12 +247,20 @@
|
|||||||
usingKey: theKey];
|
usingKey: theKey];
|
||||||
|
|
||||||
r = [decodedValue rangeOfString: @":"];
|
r = [decodedValue rangeOfString: @":"];
|
||||||
*theLogin = [decodedValue substringToIndex: r.location];
|
if (r.location != NSNotFound)
|
||||||
*thePassword = [decodedValue substringFromIndex: r.location+1];
|
{
|
||||||
|
*theLogin = [decodedValue substringToIndex: r.location];
|
||||||
|
*thePassword = [decodedValue substringFromIndex: r.location+1];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*theLogin = nil;
|
||||||
|
*thePassword = nil;
|
||||||
|
}
|
||||||
*theDomain = nil;
|
*theDomain = nil;
|
||||||
|
|
||||||
sd = [SOGoSystemDefaults sharedSystemDefaults];
|
sd = [SOGoSystemDefaults sharedSystemDefaults];
|
||||||
if ([sd enableDomainBasedUID])
|
if (*theLogin &&[sd enableDomainBasedUID])
|
||||||
{
|
{
|
||||||
r = [*theLogin rangeOfString: @"@" options: NSBackwardsSearch];
|
r = [*theLogin rangeOfString: @"@" options: NSBackwardsSearch];
|
||||||
if (r.location != NSNotFound)
|
if (r.location != NSNotFound)
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
#import <SOGo/NSDictionary+Utilities.h>
|
#import <SOGo/NSDictionary+Utilities.h>
|
||||||
#import <SOGo/NSString+Utilities.h>
|
#import <SOGo/NSString+Utilities.h>
|
||||||
#import <SOGo/SOGoDomainDefaults.h>
|
#import <SOGo/SOGoDomainDefaults.h>
|
||||||
|
#import <SOGo/SOGoSystemDefaults.h>
|
||||||
#import <SOGo/SOGoUser.h>
|
#import <SOGo/SOGoUser.h>
|
||||||
#import <SOGo/SOGoTextTemplateFile.h>
|
#import <SOGo/SOGoTextTemplateFile.h>
|
||||||
|
|
||||||
@@ -697,8 +698,11 @@ static NSString *sieveScriptName = @"sogo";
|
|||||||
NSDictionary *result;
|
NSDictionary *result;
|
||||||
NSString *login, *authname, *password;
|
NSString *login, *authname, *password;
|
||||||
SOGoDomainDefaults *dd;
|
SOGoDomainDefaults *dd;
|
||||||
|
SOGoSystemDefaults *sd;
|
||||||
NGSieveClient *client;
|
NGSieveClient *client;
|
||||||
NSString *sieveServer, *sieveScheme, *sieveQuery, *imapServer;
|
NSString *sieveServer, *sieveScheme, *sieveQuery, *imapServer;
|
||||||
|
NSString *imapAuthMech, *userDomain;
|
||||||
|
NSRange r;
|
||||||
NSURL *url, *cUrl;
|
NSURL *url, *cUrl;
|
||||||
int sievePort;
|
int sievePort;
|
||||||
BOOL connected;
|
BOOL connected;
|
||||||
@@ -773,7 +777,20 @@ static NSString *sieveScriptName = @"sogo";
|
|||||||
url = [NSURL URLWithString: [NSString stringWithFormat: @"%@://%@:%d%@",
|
url = [NSURL URLWithString: [NSString stringWithFormat: @"%@://%@:%d%@",
|
||||||
sieveScheme, sieveServer, sievePort, sieveQuery]];
|
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) {
|
if (!client) {
|
||||||
[self errorWithFormat: @"Sieve connection failed on %@", [url description]];
|
[self errorWithFormat: @"Sieve connection failed on %@", [url description]];
|
||||||
|
|||||||
@@ -36,6 +36,11 @@ static const NSString *kDisableSharingCalendar = @"Calendar";
|
|||||||
+ (SOGoSystemDefaults *) sharedSystemDefaults;
|
+ (SOGoSystemDefaults *) sharedSystemDefaults;
|
||||||
|
|
||||||
- (NSArray *) domainIds;
|
- (NSArray *) domainIds;
|
||||||
|
- (BOOL) doesLoginTypeByDomain;
|
||||||
|
- (NSString *) getLoginTypeForDomain: (NSString*) _domain;
|
||||||
|
- (NSString *) getLoginConfigForDomain: (NSDictionary*) _domain;
|
||||||
|
- (NSString *) getImapAuthMechForDomain: (NSString*) _domain;
|
||||||
|
- (NSString *) getSmtpAuthMechForDomain: (NSString*) _domain;
|
||||||
- (BOOL) forbidUnknownDomainsAuth;
|
- (BOOL) forbidUnknownDomainsAuth;
|
||||||
- (NSArray *) domainsAllowed;
|
- (NSArray *) domainsAllowed;
|
||||||
- (BOOL) enableDomainBasedUID;
|
- (BOOL) enableDomainBasedUID;
|
||||||
@@ -90,7 +95,7 @@ NSComparisonResult languageSort(id el1, id el2, void *context);
|
|||||||
- (NSString *) loginSuffix;
|
- (NSString *) loginSuffix;
|
||||||
|
|
||||||
- (NSString *) authenticationType;
|
- (NSString *) authenticationType;
|
||||||
- (BOOL) isSsoUsed;
|
- (BOOL) isSsoUsed: (NSString *) domain;
|
||||||
- (NSString *) davAuthenticationType;
|
- (NSString *) davAuthenticationType;
|
||||||
|
|
||||||
- (NSString *) CASServiceURL;
|
- (NSString *) CASServiceURL;
|
||||||
@@ -102,8 +107,9 @@ NSComparisonResult languageSort(id el1, id el2, void *context);
|
|||||||
- (NSString *) openIdClientSecret;
|
- (NSString *) openIdClientSecret;
|
||||||
- (NSString *) openIdEmailParam;
|
- (NSString *) openIdEmailParam;
|
||||||
- (BOOL) openIdEnableRefreshToken;
|
- (BOOL) openIdEnableRefreshToken;
|
||||||
- (BOOL) openIdLogoutEnabled;
|
- (BOOL) openIdLogoutEnabled: (NSString *) _domain;
|
||||||
- (int) openIdTokenCheckInterval;
|
- (int) openIdTokenCheckInterval;
|
||||||
|
- (BOOL) openIdSendDomainInfo;
|
||||||
|
|
||||||
- (NSString *) SAML2PrivateKeyLocation;
|
- (NSString *) SAML2PrivateKeyLocation;
|
||||||
- (NSString *) SAML2CertificateLocation;
|
- (NSString *) SAML2CertificateLocation;
|
||||||
|
|||||||
@@ -262,6 +262,143 @@ _injectConfigurationFromFile (NSMutableDictionary *defaultsDict,
|
|||||||
return [domains allKeys];
|
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
|
- (BOOL) enableDomainBasedUID
|
||||||
{
|
{
|
||||||
return [self boolForKey: @"SOGoEnableDomainBasedUID"];
|
return [self boolForKey: @"SOGoEnableDomainBasedUID"];
|
||||||
@@ -587,11 +724,13 @@ NSComparisonResult languageSort(id el1, id el2, void *context)
|
|||||||
return [[self stringForKey: @"SOGoAuthenticationType"] lowercaseString];
|
return [[self stringForKey: @"SOGoAuthenticationType"] lowercaseString];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL) isSsoUsed
|
- (BOOL) isSsoUsed: (NSString *) domain
|
||||||
{
|
{
|
||||||
NSString* authType;
|
NSString* authType;
|
||||||
authType = [self authenticationType];
|
|
||||||
|
|
||||||
|
authType = [self getLoginTypeForDomain: domain];
|
||||||
|
if(!authType)
|
||||||
|
authType = [self authenticationType];
|
||||||
return ([authType isEqualToString: @"cas"] || [authType isEqualToString: @"saml2"] || [authType isEqualToString: @"openid"]);
|
return ([authType isEqualToString: @"cas"] || [authType isEqualToString: @"saml2"] || [authType isEqualToString: @"openid"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -640,11 +779,28 @@ NSComparisonResult languageSort(id el1, id el2, void *context)
|
|||||||
return emailParam;
|
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"];
|
return [self boolForKey: @"SOGoOpenIdLogoutEnabled"];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (BOOL) openIdSendDomainInfo
|
||||||
|
{
|
||||||
|
return [self boolForKey: @"SOGoOpenIdSendDomainInfo"];
|
||||||
|
}
|
||||||
|
|
||||||
- (int) openIdTokenCheckInterval
|
- (int) openIdTokenCheckInterval
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|||||||
@@ -91,6 +91,7 @@
|
|||||||
|
|
||||||
/* properties */
|
/* properties */
|
||||||
- (NSString *) domain;
|
- (NSString *) domain;
|
||||||
|
- (NSString *) loginDomain;
|
||||||
- (id <SOGoSource>) authenticationSource;
|
- (id <SOGoSource>) authenticationSource;
|
||||||
|
|
||||||
- (NSArray *) allEmails;
|
- (NSArray *) allEmails;
|
||||||
|
|||||||
@@ -301,6 +301,19 @@ static const NSString *kEncryptedUserNamePrefix = @"uenc";
|
|||||||
return [self _fetchFieldForUser: @"c_domain"];
|
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
|
- (id <SOGoSource>) authenticationSource
|
||||||
{
|
{
|
||||||
NSString *sourceID;
|
NSString *sourceID;
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
#import "SOGoConstants.h"
|
#import "SOGoConstants.h"
|
||||||
|
|
||||||
@class NSString;
|
@class NSString;
|
||||||
|
@class NSMutableDictionary;
|
||||||
|
|
||||||
@class WOContext;
|
@class WOContext;
|
||||||
@class WOCookie;
|
@class WOCookie;
|
||||||
|
|||||||
@@ -138,11 +138,33 @@
|
|||||||
SOGoOpenIdSession * openIdSession;
|
SOGoOpenIdSession * openIdSession;
|
||||||
SOGoSystemDefaults *sd;
|
SOGoSystemDefaults *sd;
|
||||||
NSString *authenticationType;
|
NSString *authenticationType;
|
||||||
|
NSString* loginDomain;
|
||||||
BOOL rc;
|
BOOL rc;
|
||||||
|
|
||||||
sd = [SOGoSystemDefaults sharedSystemDefaults];
|
sd = [SOGoSystemDefaults sharedSystemDefaults];
|
||||||
|
|
||||||
|
//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([sd doesLoginTypeByDomain])
|
||||||
|
authenticationType = [sd getLoginTypeForDomain: loginDomain];
|
||||||
|
else
|
||||||
|
authenticationType = [sd authenticationType];
|
||||||
|
|
||||||
authenticationType = [sd authenticationType];
|
|
||||||
if ([authenticationType isEqualToString: @"cas"])
|
if ([authenticationType isEqualToString: @"cas"])
|
||||||
{
|
{
|
||||||
casSession = [SOGoCASSession CASSessionWithIdentifier: _pwd fromProxy: NO];
|
casSession = [SOGoCASSession CASSessionWithIdentifier: _pwd fromProxy: NO];
|
||||||
@@ -153,7 +175,7 @@
|
|||||||
}
|
}
|
||||||
else if ([authenticationType isEqualToString: @"openid"])
|
else if ([authenticationType isEqualToString: @"openid"])
|
||||||
{
|
{
|
||||||
openIdSession = [SOGoOpenIdSession OpenIdSessionWithToken: _pwd];
|
openIdSession = [SOGoOpenIdSession OpenIdSessionWithToken: _pwd domain: loginDomain];
|
||||||
if (openIdSession)
|
if (openIdSession)
|
||||||
rc = [[openIdSession login: _login] isEqualToString: _login];
|
rc = [[openIdSession login: _login] isEqualToString: _login];
|
||||||
else
|
else
|
||||||
@@ -180,7 +202,6 @@
|
|||||||
grace: _grace
|
grace: _grace
|
||||||
additionalInfo: _additionalInfo
|
additionalInfo: _additionalInfo
|
||||||
useCache: _useCache];
|
useCache: _useCache];
|
||||||
|
|
||||||
//[self logWithFormat: @"Checked login with ppolicy enabled: %d %d %d", *_perr, *_expire, *_grace];
|
//[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
|
// It's important to return the real value here. The callee will handle
|
||||||
@@ -259,7 +280,8 @@
|
|||||||
login: &login
|
login: &login
|
||||||
domain: &domain
|
domain: &domain
|
||||||
password: &pwd];
|
password: &pwd];
|
||||||
|
|
||||||
|
|
||||||
if (![self checkLogin: login
|
if (![self checkLogin: login
|
||||||
password: pwd
|
password: pwd
|
||||||
domain: &domain
|
domain: &domain
|
||||||
@@ -282,32 +304,42 @@
|
|||||||
{
|
{
|
||||||
NSString *authType, *password;
|
NSString *authType, *password;
|
||||||
SOGoSystemDefaults *sd;
|
SOGoSystemDefaults *sd;
|
||||||
|
SOGoUser *user;
|
||||||
|
NSRange r;
|
||||||
|
NSString *loginDomain, *login;
|
||||||
|
|
||||||
password = [self passwordInContext: context];
|
password = [self passwordInContext: context];
|
||||||
if ([password length])
|
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];
|
sd = [SOGoSystemDefaults sharedSystemDefaults];
|
||||||
authType = [sd authenticationType];
|
if([sd doesLoginTypeByDomain])
|
||||||
|
authType = [sd getLoginTypeForDomain: loginDomain];
|
||||||
|
else
|
||||||
|
authType = [sd authenticationType];
|
||||||
|
|
||||||
if ([authType isEqualToString: @"cas"])
|
if ([authType isEqualToString: @"cas"])
|
||||||
{
|
{
|
||||||
SOGoCASSession *session;
|
SOGoCASSession *session;
|
||||||
SOGoUser *user;
|
|
||||||
NSString *service, *scheme;
|
NSString *service, *scheme;
|
||||||
|
|
||||||
session = [SOGoCASSession CASSessionWithIdentifier: password
|
session = [SOGoCASSession CASSessionWithIdentifier: password
|
||||||
fromProxy: NO];
|
fromProxy: NO];
|
||||||
|
|
||||||
user = [self userInContext: context];
|
|
||||||
// Try configured CAS service name first
|
// Try configured CAS service name first
|
||||||
service = [[user domainDefaults] imapCASServiceName];
|
service = [[user domainDefaults] imapCASServiceName];
|
||||||
if (!service)
|
if (!service)
|
||||||
{
|
{
|
||||||
// We must NOT assume the scheme exists
|
// We must NOT assume the scheme exists
|
||||||
scheme = [server scheme];
|
scheme = [server scheme];
|
||||||
|
|
||||||
if (!scheme)
|
if (!scheme)
|
||||||
scheme = @"imap";
|
scheme = @"imap";
|
||||||
|
|
||||||
service = [NSString stringWithFormat: @"%@://%@",
|
service = [NSString stringWithFormat: @"%@://%@",
|
||||||
scheme, [server host]];
|
scheme, [server host]];
|
||||||
}
|
}
|
||||||
@@ -316,17 +348,16 @@
|
|||||||
[session invalidateTicketForService: service];
|
[session invalidateTicketForService: service];
|
||||||
|
|
||||||
password = [session ticketForService: service];
|
password = [session ticketForService: service];
|
||||||
|
|
||||||
if ([password length] || renew)
|
if ([password length] || renew)
|
||||||
[session updateCache];
|
[session updateCache];
|
||||||
}
|
}
|
||||||
else if ([authType isEqualToString: @"openid"])
|
else if ([authType isEqualToString: @"openid"])
|
||||||
{
|
{
|
||||||
SOGoOpenIdSession* session;
|
SOGoOpenIdSession* session;
|
||||||
NSString* currentToken;
|
|
||||||
//If the token has been refresh during the request, we need to use the new access_token
|
//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
|
//as the one from the cookie is no more valid
|
||||||
session = [SOGoOpenIdSession OpenIdSessionWithToken: password];
|
session = [SOGoOpenIdSession OpenIdSessionWithToken: password domain: loginDomain];
|
||||||
password = [session getCurrentToken];
|
password = [session getCurrentToken];
|
||||||
}
|
}
|
||||||
#if defined(SAML2_CONFIG)
|
#if defined(SAML2_CONFIG)
|
||||||
@@ -351,6 +382,16 @@
|
|||||||
return password;
|
return password;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (NSString *) smtpPasswordInContext: (WOContext *) context
|
||||||
|
forURL: (NSURL *) server
|
||||||
|
{
|
||||||
|
NSString *password;
|
||||||
|
|
||||||
|
password = [self imapPasswordInContext: context forURL: server forceRenew:NO];
|
||||||
|
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
|
||||||
/* create SOGoUser */
|
/* create SOGoUser */
|
||||||
|
|
||||||
- (SOGoUser *) userWithLogin: (NSString *) login
|
- (SOGoUser *) userWithLogin: (NSString *) login
|
||||||
@@ -459,21 +500,36 @@
|
|||||||
{
|
{
|
||||||
NSArray *listCookies = nil;
|
NSArray *listCookies = nil;
|
||||||
SOGoSystemDefaults *sd;
|
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];
|
sd = [SOGoSystemDefaults sharedSystemDefaults];
|
||||||
authType = [sd authenticationType];
|
if(loginDomain && [sd doesLoginTypeByDomain])
|
||||||
|
authType = [sd getLoginTypeForDomain: loginDomain];
|
||||||
|
else
|
||||||
|
authType = [sd authenticationType];
|
||||||
if([authType isEqualToString:@"openid"] && [sd openIdEnableRefreshToken])
|
if([authType isEqualToString:@"openid"] && [sd openIdEnableRefreshToken])
|
||||||
{
|
{
|
||||||
NSString *currentPassword, *newPassword, *username;
|
NSString *currentPassword, *newPassword;
|
||||||
SOGoOpenIdSession *openIdSession;
|
SOGoOpenIdSession *openIdSession;
|
||||||
|
|
||||||
WOCookie* newCookie;
|
WOCookie* newCookie;
|
||||||
|
|
||||||
|
|
||||||
currentPassword = [self passwordInContext: _ctx];
|
currentPassword = [self passwordInContext: _ctx];
|
||||||
newPassword = [self imapPasswordInContext: _ctx forURL: nil forceRenew: NO];
|
newPassword = [self imapPasswordInContext: _ctx forURL: nil forceRenew: NO];
|
||||||
if(currentPassword && newPassword && ![newPassword isEqualToString: currentPassword])
|
if(currentPassword && newPassword && ![newPassword isEqualToString: currentPassword])
|
||||||
{
|
{
|
||||||
openIdSession = [SOGoOpenIdSession OpenIdSessionWithToken: newPassword];
|
|
||||||
|
openIdSession = [SOGoOpenIdSession OpenIdSessionWithToken: newPassword domain: loginDomain];
|
||||||
if (openIdSession)
|
if (openIdSession)
|
||||||
username = [openIdSession login: @""]; //Force to refresh the name
|
username = [openIdSession login: @""]; //Force to refresh the name
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ $(SOGO_TOOL)_OBJC_FILES += \
|
|||||||
SOGoToolRemoveDoubles.m \
|
SOGoToolRemoveDoubles.m \
|
||||||
SOGoToolRenameUser.m \
|
SOGoToolRenameUser.m \
|
||||||
SOGoToolCheckupUser.m \
|
SOGoToolCheckupUser.m \
|
||||||
|
SOGoToolCleanOpenIdSessions.m \
|
||||||
SOGoToolCleanupUser.m \
|
SOGoToolCleanupUser.m \
|
||||||
SOGoToolRestore.m \
|
SOGoToolRestore.m \
|
||||||
SOGoToolCreateFolder.m \
|
SOGoToolCreateFolder.m \
|
||||||
|
|||||||
137
Tools/SOGoToolCleanOpenIdSessions.m
Normal file
137
Tools/SOGoToolCleanOpenIdSessions.m
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
/* SOGoToolCleanOpenIdSessions.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 SOGoToolCleanOpenIdSessions : SOGoTool
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation SOGoToolCleanOpenIdSessions
|
||||||
|
|
||||||
|
+ (NSString *) command
|
||||||
|
{
|
||||||
|
return @"clean-openid-sessions";
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (NSString *) description
|
||||||
|
{
|
||||||
|
return @"clean user openid sessions that are expired";
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) usage
|
||||||
|
{
|
||||||
|
fprintf (stderr, "clean-openid-sessions\n\n"
|
||||||
|
"\n"
|
||||||
|
"The clean-openid-sessions action should be configured as a cronjob.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL) cleanExpiredOpenIdSession
|
||||||
|
{
|
||||||
|
BOOL rc;
|
||||||
|
EOAdaptorChannel *channel;
|
||||||
|
GCSChannelManager *cm;
|
||||||
|
NSArray *attrs;
|
||||||
|
NSDictionary *qresult;
|
||||||
|
NSException *ex;
|
||||||
|
NSString *sql, *sessionsFolderURL, *sessionID;
|
||||||
|
NSURL *tableURL;
|
||||||
|
NSUserDefaults *ud;
|
||||||
|
|
||||||
|
unsigned int now;
|
||||||
|
|
||||||
|
rc = YES;
|
||||||
|
ud = [NSUserDefaults standardUserDefaults];
|
||||||
|
now = [[NSCalendarDate calendarDate] timeIntervalSince1970];
|
||||||
|
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 acquire channel");
|
||||||
|
return rc = NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
sql = [NSString stringWithFormat: @"SELECT c_user_session FROM %@ WHERE c_access_token_expires_in <= %d AND c_refresh_token_expires_in <= %d",
|
||||||
|
[tableURL gcsTableName], now, now];
|
||||||
|
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 table");
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL) run
|
||||||
|
{
|
||||||
|
BOOL rc;
|
||||||
|
|
||||||
|
rc = [self cleanExpiredOpenIdSession];
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
@@ -60,73 +60,6 @@
|
|||||||
"The expire-sessions action should be configured as a cronjob.\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) expireUserSessionOlderThan: (int) nbMinutes
|
||||||
{
|
{
|
||||||
BOOL rc;
|
BOOL rc;
|
||||||
@@ -135,7 +68,7 @@
|
|||||||
NSArray *attrs;
|
NSArray *attrs;
|
||||||
NSDictionary *qresult;
|
NSDictionary *qresult;
|
||||||
NSException *ex;
|
NSException *ex;
|
||||||
NSString *sql, *sessionsFolderURL, *sessionID, *authType;
|
NSString *sql, *sessionsFolderURL, *sessionID;
|
||||||
NSURL *tableURL;
|
NSURL *tableURL;
|
||||||
NSUserDefaults *ud;
|
NSUserDefaults *ud;
|
||||||
|
|
||||||
@@ -191,13 +124,6 @@
|
|||||||
if (verbose && sessionID == nil)
|
if (verbose && sessionID == nil)
|
||||||
NSLog(@"No session to remove");
|
NSLog(@"No session to remove");
|
||||||
|
|
||||||
//doing openid session if needed
|
|
||||||
authType = [ud stringForKey:@"SOGoAuthenticationType"];
|
|
||||||
if([authType isEqualToString: @"openid"])
|
|
||||||
{
|
|
||||||
[self expireUserOpenIdSessionOlderThan: nbMinutes];
|
|
||||||
}
|
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -488,14 +488,6 @@
|
|||||||
&& [user isSuperUser]);
|
&& [user isSuperUser]);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL) usesCASAuthentication
|
|
||||||
{
|
|
||||||
SOGoSystemDefaults *sd;
|
|
||||||
|
|
||||||
sd = [SOGoSystemDefaults sharedSystemDefaults];
|
|
||||||
|
|
||||||
return [[sd authenticationType] isEqualToString: @"cas"];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL) usesOpenIdAuthentication
|
- (BOOL) usesOpenIdAuthentication
|
||||||
{
|
{
|
||||||
@@ -546,19 +538,32 @@
|
|||||||
BOOL canLogoff;
|
BOOL canLogoff;
|
||||||
id auth;
|
id auth;
|
||||||
SOGoSystemDefaults *sd;
|
SOGoSystemDefaults *sd;
|
||||||
NSString *authType;
|
NSString *authType, *login, *loginDomain;
|
||||||
|
NSRange r;
|
||||||
|
|
||||||
auth = [[self clientObject] authenticatorInContext: context];
|
auth = [[self clientObject] authenticatorInContext: context];
|
||||||
if ([auth respondsToSelector: @selector (cookieNameInContext:)])
|
if ([auth respondsToSelector: @selector (cookieNameInContext:)])
|
||||||
{
|
{
|
||||||
|
|
||||||
sd = [SOGoSystemDefaults sharedSystemDefaults];
|
sd = [SOGoSystemDefaults sharedSystemDefaults];
|
||||||
authType = [sd authenticationType];
|
|
||||||
|
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"])
|
if ([authType isEqualToString: @"cas"])
|
||||||
canLogoff = [sd CASLogoutEnabled];
|
canLogoff = [sd CASLogoutEnabled];
|
||||||
else if ([authType isEqualToString: @"saml2"])
|
else if ([authType isEqualToString: @"saml2"])
|
||||||
canLogoff = [sd SAML2LogoutEnabled];
|
canLogoff = [sd SAML2LogoutEnabled];
|
||||||
else if ([authType isEqualToString: @"openid"])
|
else if ([authType isEqualToString: @"openid"])
|
||||||
canLogoff = [sd openIdLogoutEnabled];
|
canLogoff = [sd openIdLogoutEnabled: loginDomain];
|
||||||
else
|
else
|
||||||
canLogoff = [[auth cookieNameInContext: context] length] > 0;
|
canLogoff = [[auth cookieNameInContext: context] length] > 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -146,6 +146,7 @@ static const NSString *kJwtKey = @"jwt";
|
|||||||
|
|
||||||
- (WOCookie *) _authLocationCookie: (BOOL) cookieReset
|
- (WOCookie *) _authLocationCookie: (BOOL) cookieReset
|
||||||
withName: (NSString *) cookieName
|
withName: (NSString *) cookieName
|
||||||
|
withValue: (NSString *) _value
|
||||||
{
|
{
|
||||||
WOCookie *locationCookie;
|
WOCookie *locationCookie;
|
||||||
NSString *appName;
|
NSString *appName;
|
||||||
@@ -153,7 +154,10 @@ static const NSString *kJwtKey = @"jwt";
|
|||||||
NSCalendarDate *date;
|
NSCalendarDate *date;
|
||||||
|
|
||||||
rq = [context request];
|
rq = [context request];
|
||||||
locationCookie = [WOCookie cookieWithName: cookieName value: [rq uri]];
|
if(_value)
|
||||||
|
locationCookie = [WOCookie cookieWithName: cookieName value: _value];
|
||||||
|
else
|
||||||
|
locationCookie = [WOCookie cookieWithName: cookieName value: [rq uri]];
|
||||||
appName = [rq applicationName];
|
appName = [rq applicationName];
|
||||||
[locationCookie setPath: [NSString stringWithFormat: @"/%@/", appName]];
|
[locationCookie setPath: [NSString stringWithFormat: @"/%@/", appName]];
|
||||||
if (cookieReset)
|
if (cookieReset)
|
||||||
@@ -166,6 +170,28 @@ static const NSString *kJwtKey = @"jwt";
|
|||||||
return locationCookie;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
@@ -426,6 +452,7 @@ static const NSString *kJwtKey = @"jwt";
|
|||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSDictionary *) _casRedirectKeys
|
- (NSDictionary *) _casRedirectKeys
|
||||||
{
|
{
|
||||||
NSDictionary *redirectKeys;
|
NSDictionary *redirectKeys;
|
||||||
@@ -499,7 +526,8 @@ static const NSString *kJwtKey = @"jwt";
|
|||||||
/* login callback, we expire the "cas-location" cookie, created
|
/* login callback, we expire the "cas-location" cookie, created
|
||||||
below */
|
below */
|
||||||
casLocationCookie = [self _authLocationCookie: YES
|
casLocationCookie = [self _authLocationCookie: YES
|
||||||
withName: @"cas-location"];
|
withName: @"cas-location"
|
||||||
|
withValue: nil];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -538,7 +566,8 @@ static const NSString *kJwtKey = @"jwt";
|
|||||||
newLocation = [SOGoCASSession CASURLWithAction: @"login"
|
newLocation = [SOGoCASSession CASURLWithAction: @"login"
|
||||||
andParameters: [self _casRedirectKeys]];
|
andParameters: [self _casRedirectKeys]];
|
||||||
casLocationCookie = [self _authLocationCookie: NO
|
casLocationCookie = [self _authLocationCookie: NO
|
||||||
withName: @"cas-location"];
|
withName: @"cas-location"
|
||||||
|
withValue: nil];
|
||||||
}
|
}
|
||||||
response = [self redirectToLocation: newLocation];
|
response = [self redirectToLocation: newLocation];
|
||||||
if (casCookie)
|
if (casCookie)
|
||||||
@@ -549,7 +578,7 @@ static const NSString *kJwtKey = @"jwt";
|
|||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id <WOActionResults>) _openidDefaultAction
|
- (id <WOActionResults>) _openidDefaultAction: (NSString *) _domain
|
||||||
{
|
{
|
||||||
WOResponse *response;
|
WOResponse *response;
|
||||||
NSString *login, *redirectLocation, *serverUrl;
|
NSString *login, *redirectLocation, *serverUrl;
|
||||||
@@ -557,7 +586,7 @@ static const NSString *kJwtKey = @"jwt";
|
|||||||
NSURL *newLocation, *oldLocation;
|
NSURL *newLocation, *oldLocation;
|
||||||
NSDictionary *formValues;
|
NSDictionary *formValues;
|
||||||
SOGoUser *loggedInUser;
|
SOGoUser *loggedInUser;
|
||||||
WOCookie *openIdCookie, *openIdCookieLocation, *openIdRefreshCookie;
|
WOCookie *openIdCookie, *openIdCookieLocation, *openIdRefreshCookie, *domainCookie;
|
||||||
WORequest *rq;
|
WORequest *rq;
|
||||||
SOGoWebAuthenticator *auth;
|
SOGoWebAuthenticator *auth;
|
||||||
SOGoOpenIdSession *openIdSession;
|
SOGoOpenIdSession *openIdSession;
|
||||||
@@ -566,9 +595,16 @@ static const NSString *kJwtKey = @"jwt";
|
|||||||
openIdCookie = nil;
|
openIdCookie = nil;
|
||||||
openIdCookieLocation = nil;
|
openIdCookieLocation = nil;
|
||||||
openIdRefreshCookie = nil;
|
openIdRefreshCookie = nil;
|
||||||
|
domainCookie = nil;
|
||||||
newLocation = 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])
|
if(![openIdSession sessionIsOk])
|
||||||
{
|
{
|
||||||
@@ -577,7 +613,6 @@ static const NSString *kJwtKey = @"jwt";
|
|||||||
}
|
}
|
||||||
|
|
||||||
login = [[context activeUser] login];
|
login = [[context activeUser] login];
|
||||||
rq = [context request];
|
|
||||||
if ([login isEqualToString: @"anonymous"])
|
if ([login isEqualToString: @"anonymous"])
|
||||||
login = nil;
|
login = nil;
|
||||||
if (!login)
|
if (!login)
|
||||||
@@ -585,7 +620,6 @@ static const NSString *kJwtKey = @"jwt";
|
|||||||
//You get here if you nerver been logged in or if you token is expired
|
//You get here if you nerver been logged in or if you token is expired
|
||||||
serverUrl = [[context serverURL] absoluteString];
|
serverUrl = [[context serverURL] absoluteString];
|
||||||
redirectLocation = [NSString stringWithFormat: @"%@/%@/", serverUrl, [rq applicationName]];
|
redirectLocation = [NSString stringWithFormat: @"%@/%@/", serverUrl, [rq applicationName]];
|
||||||
NSLog(@"ServerUrl %@ and redirect: %@", serverUrl, redirectLocation);
|
|
||||||
if((formValues = [rq formValues]) && [formValues objectForKey: @"code"])
|
if((formValues = [rq formValues]) && [formValues objectForKey: @"code"])
|
||||||
{
|
{
|
||||||
//You get here if this is the callback of openid after you logged in
|
//You get here if this is the callback of openid after you logged in
|
||||||
@@ -596,6 +630,7 @@ static const NSString *kJwtKey = @"jwt";
|
|||||||
// sessionState = [value lastObject];
|
// sessionState = [value lastObject];
|
||||||
// else
|
// else
|
||||||
// sessionState = value;
|
// sessionState = value;
|
||||||
|
|
||||||
value = [formValues objectForKey: @"code"];
|
value = [formValues objectForKey: @"code"];
|
||||||
if ([value isKindOfClass: [NSArray class]])
|
if ([value isKindOfClass: [NSArray class]])
|
||||||
code = [value lastObject];
|
code = [value lastObject];
|
||||||
@@ -611,7 +646,8 @@ static const NSString *kJwtKey = @"jwt";
|
|||||||
inContext: context];
|
inContext: context];
|
||||||
}
|
}
|
||||||
newLocation = [rq cookieValueForKey: @"openid-location"];
|
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"])
|
// else if((formValues = [rq formValues]) && [formValues objectForKey: @"action"])
|
||||||
// {
|
// {
|
||||||
@@ -633,8 +669,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
|
// //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];
|
// newLocation = [NSString stringWithFormat: @"%@?action=redirect", redirectLocation];
|
||||||
// else
|
// else
|
||||||
newLocation = [openIdSession loginUrl: redirectLocation];
|
if(_domain != nil && [_domain length] > 0)
|
||||||
openIdCookieLocation = [self _authLocationCookie: NO withName: @"openid-location"];
|
{
|
||||||
|
//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" withValue: nil];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -654,10 +695,14 @@ static const NSString *kJwtKey = @"jwt";
|
|||||||
[response addCookie: openIdCookie];
|
[response addCookie: openIdCookie];
|
||||||
if (openIdCookieLocation)
|
if (openIdCookieLocation)
|
||||||
[response addCookie: openIdCookieLocation];
|
[response addCookie: openIdCookieLocation];
|
||||||
|
if(domainCookie)
|
||||||
|
[response addCookie: domainCookie];
|
||||||
//[response setStatus: 303];
|
//[response setStatus: 303];
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#if defined(SAML2_CONFIG)
|
#if defined(SAML2_CONFIG)
|
||||||
- (id <WOActionResults>) _saml2DefaultAction
|
- (id <WOActionResults>) _saml2DefaultAction
|
||||||
{
|
{
|
||||||
@@ -681,7 +726,8 @@ static const NSString *kJwtKey = @"jwt";
|
|||||||
newLocation = [rq cookieValueForKey: @"saml2-location"];
|
newLocation = [rq cookieValueForKey: @"saml2-location"];
|
||||||
if (newLocation)
|
if (newLocation)
|
||||||
saml2LocationCookie = [self _authLocationCookie: YES
|
saml2LocationCookie = [self _authLocationCookie: YES
|
||||||
withName: @"saml2-location"];
|
withName: @"saml2-location"
|
||||||
|
withValue: nil];
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
oldLocation = [[self clientObject] baseURLInContext: context];
|
oldLocation = [[self clientObject] baseURLInContext: context];
|
||||||
@@ -696,7 +742,8 @@ static const NSString *kJwtKey = @"jwt";
|
|||||||
{
|
{
|
||||||
newLocation = [SOGoSAML2Session authenticationURLInContext: context];
|
newLocation = [SOGoSAML2Session authenticationURLInContext: context];
|
||||||
saml2LocationCookie = [self _authLocationCookie: NO
|
saml2LocationCookie = [self _authLocationCookie: NO
|
||||||
withName: @"saml2-location"];
|
withName: @"saml2-location"
|
||||||
|
withValue: nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
response = [self redirectToLocation: newLocation];
|
response = [self redirectToLocation: newLocation];
|
||||||
@@ -717,13 +764,11 @@ static const NSString *kJwtKey = @"jwt";
|
|||||||
login = nil;
|
login = nil;
|
||||||
|
|
||||||
if (login)
|
if (login)
|
||||||
{
|
{
|
||||||
oldLocation = [[self clientObject] baseURLInContext: context];
|
oldLocation = [[self clientObject] baseURLInContext: context];
|
||||||
response
|
response = [self redirectToLocation: [NSString stringWithFormat: @"%@%@", oldLocation,
|
||||||
= [self redirectToLocation: [NSString stringWithFormat: @"%@%@",
|
[[SOGoUser getEncryptedUsernameIfNeeded:login request: [context request]] stringByEscapingURL]]];
|
||||||
oldLocation,
|
}
|
||||||
[[SOGoUser getEncryptedUsernameIfNeeded:login request: [context request]] stringByEscapingURL]]];
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
oldLocation = [[context request] uri];
|
oldLocation = [[context request] uri];
|
||||||
@@ -736,23 +781,174 @@ static const NSString *kJwtKey = @"jwt";
|
|||||||
return response;
|
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
|
- (id <WOActionResults>) defaultAction
|
||||||
{
|
{
|
||||||
NSString *authenticationType;
|
NSString *authenticationType, *loginDomain, *type, *_domain;
|
||||||
|
SOGoSystemDefaults* sd;
|
||||||
id <WOActionResults> result;
|
id <WOActionResults> result;
|
||||||
|
|
||||||
authenticationType = [[SOGoSystemDefaults sharedSystemDefaults]
|
loginDomain = nil;
|
||||||
authenticationType];
|
sd = [SOGoSystemDefaults sharedSystemDefaults];
|
||||||
if ([authenticationType isEqualToString: @"cas"])
|
if([sd doesLoginTypeByDomain])
|
||||||
result = [self _casDefaultAction];
|
{
|
||||||
else if ([authenticationType isEqualToString: @"openid"])
|
NSString *login;
|
||||||
result = [self _openidDefaultAction];
|
//In this mode sogo will ask the mail of the user before doing any authentication
|
||||||
#if defined(SAML2_CONFIG)
|
//Check if a user is already logged in
|
||||||
else if ([authenticationType isEqualToString: @"saml2"])
|
|
||||||
result = [self _saml2DefaultAction];
|
_domain = [[context request] cookieValueForKey: @"sogo-user-domain"]; //_domain can still be nil aftert his
|
||||||
#endif /* SAML2_CONFIG */
|
if(_domain != nil)
|
||||||
else
|
{
|
||||||
result = [self _standardDefaultAction];
|
//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: loginDomain];
|
||||||
|
#if defined(SAML2_CONFIG)
|
||||||
|
else if ([authenticationType isEqualToString: @"saml2"])
|
||||||
|
result = [self _saml2DefaultAction];
|
||||||
|
#endif /* SAML2_CONFIG */
|
||||||
|
else
|
||||||
|
result = [self _standardDefaultAction];
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -782,6 +978,57 @@ static const NSString *kJwtKey = @"jwt";
|
|||||||
return ([[self loginDomains] count] > 0);
|
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
|
- (BOOL) hasPasswordRecovery
|
||||||
{
|
{
|
||||||
return [[SOGoSystemDefaults sharedSystemDefaults] isPasswordRecoveryEnabled];
|
return [[SOGoSystemDefaults sharedSystemDefaults] isPasswordRecoveryEnabled];
|
||||||
@@ -1145,8 +1392,7 @@ static const NSString *kJwtKey = @"jwt";
|
|||||||
message = [[request contentAsString] objectFromJSONString];
|
message = [[request contentAsString] objectFromJSONString];
|
||||||
username = [message objectForKey: @"userName"];
|
username = [message objectForKey: @"userName"];
|
||||||
domain = [message objectForKey: @"domain"];
|
domain = [message objectForKey: @"domain"];
|
||||||
if ([[SOGoSystemDefaults sharedSystemDefaults]
|
if ([[SOGoSystemDefaults sharedSystemDefaults] isPasswordRecoveryEnabled]) {
|
||||||
isPasswordRecoveryEnabled]) {
|
|
||||||
// If no domain, try to retrieve domain from username
|
// If no domain, try to retrieve domain from username
|
||||||
if (nil != domain && domain != [NSNull null]) {
|
if (nil != domain && domain != [NSNull null]) {
|
||||||
domainName = domain;
|
domainName = domain;
|
||||||
|
|||||||
@@ -420,26 +420,38 @@
|
|||||||
|
|
||||||
- (NSString *) _logoutRedirectURL
|
- (NSString *) _logoutRedirectURL
|
||||||
{
|
{
|
||||||
NSString *redirectURL;
|
NSString *redirectURL, *login, *loginDomain, *authType;
|
||||||
SOGoSystemDefaults *sd;
|
SOGoSystemDefaults *sd;
|
||||||
id container;
|
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];
|
sd = [SOGoSystemDefaults sharedSystemDefaults];
|
||||||
if ([[sd authenticationType] isEqualToString: @"cas"])
|
if(loginDomain && [sd doesLoginTypeByDomain])
|
||||||
{
|
authType = [sd getLoginTypeForDomain: loginDomain];
|
||||||
redirectURL = [SOGoCASSession CASURLWithAction: @"logout"
|
else
|
||||||
andParameters: nil];
|
authType = [sd authenticationType];
|
||||||
}
|
|
||||||
else if ([[sd authenticationType] isEqualToString: @"openid"])
|
if ([authType isEqualToString: @"cas"])
|
||||||
|
{
|
||||||
|
redirectURL = [SOGoCASSession CASURLWithAction: @"logout"
|
||||||
|
andParameters: nil];
|
||||||
|
}
|
||||||
|
else if ([authType isEqualToString: @"openid"])
|
||||||
{
|
{
|
||||||
SOGoOpenIdSession* session;
|
SOGoOpenIdSession* session;
|
||||||
session = [SOGoOpenIdSession OpenIdSession];
|
session = [SOGoOpenIdSession OpenIdSession: loginDomain];
|
||||||
redirectURL = [session logoutUrl];
|
redirectURL = [session logoutUrl];
|
||||||
//delete openid session in database
|
|
||||||
|
|
||||||
}
|
}
|
||||||
#if defined(SAML2_CONFIG)
|
#if defined(SAML2_CONFIG)
|
||||||
else if ([[sd authenticationType] isEqualToString: @"saml2"])
|
else if ([authType isEqualToString: @"saml2"])
|
||||||
{
|
{
|
||||||
NSString *username, *password, *domain, *value;
|
NSString *username, *password, *domain, *value;
|
||||||
SOGoSAML2Session *saml2Session;
|
SOGoSAML2Session *saml2Session;
|
||||||
|
|||||||
@@ -168,6 +168,16 @@
|
|||||||
pageName = "SOGoRootPage";
|
pageName = "SOGoRootPage";
|
||||||
actionName = "connect";
|
actionName = "connect";
|
||||||
};
|
};
|
||||||
|
connectName = {
|
||||||
|
protectedBy = "<public>";
|
||||||
|
pageName = "SOGoRootPage";
|
||||||
|
actionName = "connectName";
|
||||||
|
};
|
||||||
|
openid_redirect = {
|
||||||
|
protectedBy = "<public>";
|
||||||
|
pageName = "SOGoRootPage";
|
||||||
|
actionName = "openIdRedirect";
|
||||||
|
};
|
||||||
changePassword = {
|
changePassword = {
|
||||||
protectedBy = "<public>";
|
protectedBy = "<public>";
|
||||||
pageName = "SOGoRootPage";
|
pageName = "SOGoRootPage";
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
var cookieUsername = <var:string var:value="cookieUsername.doubleQuotedString" const:escapeHTML="NO"/>;
|
var cookieUsername = <var:string var:value="cookieUsername.doubleQuotedString" const:escapeHTML="NO"/>;
|
||||||
var language = '<var:string var:value="language" const:escapeHTML="NO"/>';
|
var language = '<var:string var:value="language" const:escapeHTML="NO"/>';
|
||||||
|
var loginHint = '<var:string var:value="getLoginHint" const:escapeHTML="NO"/>'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
@@ -44,116 +45,121 @@
|
|||||||
</div>
|
</div>
|
||||||
</var:if>
|
</var:if>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="sg-login md-default-theme md-bg md-accent" flex-gt-md="50">
|
<div class="sg-login md-default-theme md-bg md-accent" flex-gt-md="50">
|
||||||
<div id="login" class="sg-login-content md-padding">
|
<var:if condition="doFullLogin">
|
||||||
<form name="loginForm" layout="column"
|
<div id="login" class="sg-login-content md-padding">
|
||||||
ng-cloak="ng-cloak"
|
<form name="loginForm" layout="column"
|
||||||
ng-submit="app.login()">
|
ng-cloak="ng-cloak"
|
||||||
<var:if condition="hasLoginSuffix">
|
ng-submit="app.login()">
|
||||||
<input type="hidden" ng-model="app.creds.loginSuffix" var:value="loginSuffix"/>
|
<var:if condition="hasLoginSuffix">
|
||||||
</var:if>
|
<input type="hidden" ng-model="app.creds.loginSuffix" var:value="loginSuffix"/>
|
||||||
<div ng-if="!app.loginState">
|
</var:if>
|
||||||
<md-input-container class="md-block">
|
<div ng-if="!app.loginState">
|
||||||
<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>
|
|
||||||
<input id="passwordField" type="password" ng-model="app.creds.password" ng-required="true"/>
|
|
||||||
<md-icon id="password-visibility-icon" ng-click="app.changePasswordVisibility()">visibility</md-icon>
|
|
||||||
</md-input-container>
|
|
||||||
|
|
||||||
|
|
||||||
<!-- LANGUAGES SELECT -->
|
<md-input-container class="md-block">
|
||||||
<div layout="row" layout-align="start end">
|
<label><var:string label:value="Username"/></label>
|
||||||
<md-icon>language</md-icon>
|
<md-icon>person</md-icon>
|
||||||
<md-input-container class="md-flex">
|
<input autocorrect="off" autocapitalize="off" type="text" ng-model="app.creds.username" ng-required="true" ng-change="app.usernameChanged()" ng-blur="app.retrievePasswordRecoveryEnabled()" />
|
||||||
<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>
|
</md-input-container>
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- DOMAINS SELECT -->
|
<md-input-container class="md-block">
|
||||||
<var:if condition="hasLoginDomains">
|
<label><var:string label:value="Password"/></label>
|
||||||
|
<md-icon>vpn_key</md-icon>
|
||||||
|
<input id="passwordField" type="password" ng-model="app.creds.password" ng-required="true"/>
|
||||||
|
<md-icon id="password-visibility-icon" ng-click="app.changePasswordVisibility()">visibility</md-icon>
|
||||||
|
</md-input-container>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- LANGUAGES SELECT -->
|
||||||
<div layout="row" layout-align="start end">
|
<div layout="row" layout-align="start end">
|
||||||
<md-icon>domain</md-icon>
|
<md-icon>language</md-icon>
|
||||||
<md-input-container class="md-flex">
|
<md-input-container class="md-flex">
|
||||||
<md-select class="md-flex" ng-model="app.creds.domain" label:placeholder="choose" ng-change="app.retrievePasswordRecoveryEnabled()">
|
<label><var:string label:value="choose"/></label>
|
||||||
<var:foreach list="loginDomains" item="item">
|
<md-select ng-model="app.creds.language"
|
||||||
<md-option var:value="item">
|
var:placeholder="localizedLanguage"
|
||||||
<var:string value="item"/>
|
ng-change="app.changeLanguage($event)">
|
||||||
</md-option>
|
<var:foreach list="languages" item="item">
|
||||||
</var:foreach>
|
<md-option var:value="item">
|
||||||
</md-select>
|
<var:string value="languageText"/>
|
||||||
</md-input-container>
|
</md-option>
|
||||||
</div>
|
</var:foreach>
|
||||||
</var:if>
|
</md-select>
|
||||||
|
</md-input-container>
|
||||||
<div layout="row" layout-align="center center">
|
|
||||||
<md-switch class="md-accent md-hue-2"
|
|
||||||
ng-model="app.creds.rememberLogin"
|
|
||||||
label:arial-label="Remember username">
|
|
||||||
<var:string label:value="Remember username"/>
|
|
||||||
</md-switch>
|
|
||||||
</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>
|
</div>
|
||||||
</var:if>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Password recovery -->
|
<!-- DOMAINS SELECT -->
|
||||||
<div layout="row" layout-align="center center" ng-if="app.passwordRecovery.passwordRecoveryEnabled">
|
<var:if condition="hasLoginDomains">
|
||||||
<div ng-if="app.showLogin">
|
<div layout="row" layout-align="start end">
|
||||||
<a href="#" ng-click="app.passwordRecoveryInfo()" sg-ripple-click="loginContent" class="password-lost-link"><var:string label:value="Password lost"/></a>
|
<md-icon>domain</md-icon>
|
||||||
|
<md-input-container class="md-flex">
|
||||||
|
<md-select class="md-flex" ng-model="app.creds.domain" label:placeholder="choose" ng-change="app.retrievePasswordRecoveryEnabled()">
|
||||||
|
<var:foreach list="loginDomains" item="item">
|
||||||
|
<md-option var:value="item">
|
||||||
|
<var:string value="item"/>
|
||||||
|
</md-option>
|
||||||
|
</var:foreach>
|
||||||
|
</md-select>
|
||||||
|
</md-input-container>
|
||||||
|
</div>
|
||||||
|
</var:if>
|
||||||
|
|
||||||
|
<div layout="row" layout-align="center center">
|
||||||
|
<md-switch class="md-accent md-hue-2"
|
||||||
|
ng-model="app.creds.rememberLogin"
|
||||||
|
label:arial-label="Remember username">
|
||||||
|
<var:string label:value="Remember username"/>
|
||||||
|
</md-switch>
|
||||||
|
</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>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- CONNECT BUTTON -->
|
<!-- Password recovery -->
|
||||||
<div layout="row" layout-align="space-between center" ng-if="!app.loginState">
|
<div layout="row" layout-align="center center" ng-if="app.passwordRecovery.passwordRecoveryEnabled">
|
||||||
<md-button class="md-icon-button"
|
<div ng-if="app.showLogin">
|
||||||
label:aria-label="About"
|
<a href="#" ng-click="app.passwordRecoveryInfo()" sg-ripple-click="loginContent" class="password-lost-link"><var:string label:value="Password lost"/></a>
|
||||||
ng-click="app.showAbout()">
|
</div>
|
||||||
<md-icon>info</md-icon>
|
</div>
|
||||||
</md-button>
|
|
||||||
<div>
|
<!-- CONNECT BUTTON -->
|
||||||
<md-button class="md-fab md-accent md-hue-2" type="submit"
|
<div layout="row" layout-align="space-between center" ng-if="!app.loginState">
|
||||||
label:aria-label="Connect"
|
<md-button class="md-icon-button"
|
||||||
ng-if="!app.loginState"
|
label:aria-label="About"
|
||||||
ng-disabled="loginForm.$invalid"
|
ng-click="app.showAbout()">
|
||||||
sg-ripple-click="loginContent">
|
<md-icon>info</md-icon>
|
||||||
<md-icon>arrow_forward</md-icon>
|
|
||||||
</md-button>
|
</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>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<sg-ripple class="md-default-theme md-accent md-bg"
|
<sg-ripple class="md-default-theme md-accent md-bg"
|
||||||
ng-class="{ 'md-warn': app.loginState == 'error' }"><!-- ripple background --></sg-ripple>
|
ng-class="{ 'md-warn': app.loginState == 'error' }"><!-- ripple background --></sg-ripple>
|
||||||
<sg-ripple-content class="md-flex ng-hide"
|
<sg-ripple-content class="md-flex ng-hide"
|
||||||
layout="column" layout-align="center center" layout-fill="layout-fill"
|
layout="column" layout-align="center center" layout-fill="layout-fill"
|
||||||
ng-switch="app.loginState">
|
ng-switch="app.loginState">
|
||||||
|
|
||||||
<!-- Authenticating -->
|
<!-- Authenticating -->
|
||||||
<div layout="column" layout-align="center center"
|
<div layout="column" layout-align="center center"
|
||||||
ng-switch-when="authenticating">
|
ng-switch-when="authenticating">
|
||||||
<md-progress-circular class="md-hue-1"
|
<md-progress-circular class="md-hue-1"
|
||||||
md-mode="indeterminate"
|
md-mode="indeterminate"
|
||||||
md-diameter="32"><!-- mailbox loading progress --></md-progress-circular>
|
md-diameter="32"><!-- mailbox loading progress --></md-progress-circular>
|
||||||
<div class="md-default-theme md-accent md-hue-1 md-fg md-padding">
|
<div class="md-default-theme md-accent md-hue-1 md-fg md-padding">
|
||||||
<var:string label:value="Authenticating"/>
|
<var:string label:value="Authenticating"/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<var:if condition="isTotpEnabled">
|
<var:if condition="isTotpEnabled">
|
||||||
<!-- TOTP Code -->
|
<!-- TOTP Code -->
|
||||||
@@ -163,7 +169,7 @@
|
|||||||
<md-input-container class="md-block">
|
<md-input-container class="md-block">
|
||||||
<label><var:string label:value="Verification Code"/></label>
|
<label><var:string label:value="Verification Code"/></label>
|
||||||
<md-icon>lock</md-icon>
|
<md-icon>lock</md-icon>
|
||||||
<input type="text" inputmode="numeric" autocomplete="off"
|
<input type="text" inputmode="numeric" autocomplete="off"
|
||||||
ng-pattern="app.verificationCodePattern"
|
ng-pattern="app.verificationCodePattern"
|
||||||
ng-model="app.creds.verificationCode"
|
ng-model="app.creds.verificationCode"
|
||||||
ng-required="app.loginState == 'totpcode'"
|
ng-required="app.loginState == 'totpcode'"
|
||||||
@@ -258,126 +264,235 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Password policy: Grace period -->
|
<!-- Password policy: Grace period -->
|
||||||
<div layout="row" layout-align="center center" layout-fill="layout-fill"
|
<div layout="row" layout-align="center center" layout-fill="layout-fill"
|
||||||
ng-switch-when="passwordwillexpire">
|
ng-switch-when="passwordwillexpire">
|
||||||
<div layout="column" layout-align="center center" flex-xs="flex-xs" flex-gt-xs="50">
|
<div layout="column" layout-align="center center" flex-xs="flex-xs" flex-gt-xs="50">
|
||||||
<md-icon class="md-accent md-hue-1 sg-icon--large">warning</md-icon>
|
<md-icon class="md-accent md-hue-1 sg-icon--large">warning</md-icon>
|
||||||
<div class="md-default-theme md-accent md-hue-1 md-fg md-padding" ng-if="app.cn">
|
<div class="md-default-theme md-accent md-hue-1 md-fg md-padding" ng-if="app.cn">
|
||||||
<var:string label:value="Welcome"/> {{app.cn}}
|
<var:string label:value="Welcome"/> {{app.cn}}
|
||||||
</div>
|
|
||||||
<div class="md-padding" layout="row" layout-align="start center">
|
|
||||||
<md-icon>priority_high</md-icon>
|
|
||||||
<div class="md-padding">{{app.errorMessage}}</div>
|
|
||||||
</div>
|
|
||||||
<div layout="row" layout-align="end center">
|
|
||||||
<md-button
|
|
||||||
ng-click="app.loginState = 'passwordexpired'"><var:string label:value="Change your Password"/></md-button>
|
|
||||||
<md-button
|
|
||||||
ng-click="app.continueLogin()"
|
|
||||||
sg-ripple-click="loginContent"><var:string label:value="Continue"/></md-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Password recovery -->
|
|
||||||
<var:if condition="hasPasswordRecovery">
|
|
||||||
<div layout="column" layout-align="center center"
|
|
||||||
ng-switch-when="passwordrecovery">
|
|
||||||
<md-icon class="md-accent md-hue-1 sg-icon--large">vpn_key</md-icon>
|
|
||||||
<div flex="100">
|
|
||||||
<div layout="row" layout-xs="column" class="md-padding" layout-align="center center">
|
|
||||||
<div ng-if="app.passwordRecovery.showLoader">
|
|
||||||
<md-progress-circular class="md-hue-1"
|
|
||||||
md-mode="indeterminate"
|
|
||||||
md-diameter="32"><!-- password recovery progress --></md-progress-circular>
|
|
||||||
</div>
|
|
||||||
<div ng-if="'SecretQuestion' === app.passwordRecovery.passwordRecoveryMode">
|
|
||||||
<div ng-if="!app.passwordRecovery.showLoader">
|
|
||||||
{{ app.passwordRecovery.passwordRecoveryQuestion }}
|
|
||||||
<md-input-container class="md-block">
|
|
||||||
<label><var:string label:value="Answer"/></label>
|
|
||||||
<input autocorrect="off" autocapitalize="off" type="text" ng-model="app.passwordRecovery.passwordRecoveryQuestionAnswer" />
|
|
||||||
</md-input-container>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div ng-if="'SecondaryEmail' === app.passwordRecovery.passwordRecoveryMode">
|
|
||||||
<div ng-if="!app.passwordRecovery.showLoader">
|
|
||||||
{{ app.passwordRecovery.passwordRecoverySecondaryEmailText }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div layout="row" layout-align="end center" ng-if="!app.passwordRecovery.showLoader">
|
<div class="md-padding" layout="row" layout-align="start center">
|
||||||
<md-button ng-click="app.passwordRecoveryAbort()" type="button" >
|
<md-icon>priority_high</md-icon>
|
||||||
<var:string label:value="Back"/>
|
<div class="md-padding">{{app.errorMessage}}</div>
|
||||||
</md-button>
|
|
||||||
<div ng-if="'SecretQuestion' === app.passwordRecovery.passwordRecoveryMode">
|
|
||||||
<md-button ng-click="app.passwordRecoveryCheck()" type="button" >
|
|
||||||
<var:string label:value="Next"/>
|
|
||||||
</md-button>
|
|
||||||
</div>
|
|
||||||
<div ng-if="'SecondaryEmail' === app.passwordRecovery.passwordRecoveryMode">
|
|
||||||
<md-button ng-click="app.passwordRecoveryEmail()" type="button" >
|
|
||||||
<var:string label:value="Next"/>
|
|
||||||
</md-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div layout="column" layout-align="center center"
|
|
||||||
ng-switch-when="sendrecoverymail">
|
|
||||||
<md-icon class="md-accent md-hue-1 sg-icon--large">local_shipping</md-icon>
|
|
||||||
<div flex="100">
|
|
||||||
<div layout="row" layout-xs="column" class="md-padding">
|
|
||||||
<div ng-if="'SecondaryEmail' === app.passwordRecovery.passwordRecoveryMode">
|
|
||||||
<var:string label:value="A password reset link has been sent, please check your recovery e-mail mailbox and click on the link"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div layout="row" layout-align="end center">
|
<div layout="row" layout-align="end center">
|
||||||
<md-button ng-click="app.passwordRecoveryAbort()" type="button" >
|
<md-button
|
||||||
<var:string label:value="Back"/>
|
ng-click="app.loginState = 'passwordexpired'"><var:string label:value="Change your Password"/></md-button>
|
||||||
</md-button>
|
<md-button
|
||||||
|
ng-click="app.continueLogin()"
|
||||||
|
sg-ripple-click="loginContent"><var:string label:value="Continue"/></md-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</var:if>
|
|
||||||
|
|
||||||
<!-- Logged in -->
|
<!-- Password recovery -->
|
||||||
<div layout="column" layout-align="center center"
|
<var:if condition="hasPasswordRecovery">
|
||||||
ng-switch-when="logged">
|
<div layout="column" layout-align="center center"
|
||||||
<md-icon class="md-accent md-hue-1 sg-icon--large">done</md-icon>
|
ng-switch-when="passwordrecovery">
|
||||||
<div class="md-default-theme md-accent md-hue-1 md-fg md-padding">
|
<md-icon class="md-accent md-hue-1 sg-icon--large">vpn_key</md-icon>
|
||||||
<var:string label:value="Welcome"/> {{app.cn}}
|
<div flex="100">
|
||||||
|
<div layout="row" layout-xs="column" class="md-padding" layout-align="center center">
|
||||||
|
<div ng-if="app.passwordRecovery.showLoader">
|
||||||
|
<md-progress-circular class="md-hue-1"
|
||||||
|
md-mode="indeterminate"
|
||||||
|
md-diameter="32"><!-- password recovery progress --></md-progress-circular>
|
||||||
|
</div>
|
||||||
|
<div ng-if="'SecretQuestion' === app.passwordRecovery.passwordRecoveryMode">
|
||||||
|
<div ng-if="!app.passwordRecovery.showLoader">
|
||||||
|
{{ app.passwordRecovery.passwordRecoveryQuestion }}
|
||||||
|
<md-input-container class="md-block">
|
||||||
|
<label><var:string label:value="Answer"/></label>
|
||||||
|
<input autocorrect="off" autocapitalize="off" type="text" ng-model="app.passwordRecovery.passwordRecoveryQuestionAnswer" />
|
||||||
|
</md-input-container>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div ng-if="'SecondaryEmail' === app.passwordRecovery.passwordRecoveryMode">
|
||||||
|
<div ng-if="!app.passwordRecovery.showLoader">
|
||||||
|
{{ app.passwordRecovery.passwordRecoverySecondaryEmailText }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div layout="row" layout-align="end center" ng-if="!app.passwordRecovery.showLoader">
|
||||||
|
<md-button ng-click="app.passwordRecoveryAbort()" type="button" >
|
||||||
|
<var:string label:value="Back"/>
|
||||||
|
</md-button>
|
||||||
|
<div ng-if="'SecretQuestion' === app.passwordRecovery.passwordRecoveryMode">
|
||||||
|
<md-button ng-click="app.passwordRecoveryCheck()" type="button" >
|
||||||
|
<var:string label:value="Next"/>
|
||||||
|
</md-button>
|
||||||
|
</div>
|
||||||
|
<div ng-if="'SecondaryEmail' === app.passwordRecovery.passwordRecoveryMode">
|
||||||
|
<md-button ng-click="app.passwordRecoveryEmail()" type="button" >
|
||||||
|
<var:string label:value="Next"/>
|
||||||
|
</md-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div layout="column" layout-align="center center"
|
||||||
|
ng-switch-when="sendrecoverymail">
|
||||||
|
<md-icon class="md-accent md-hue-1 sg-icon--large">local_shipping</md-icon>
|
||||||
|
<div flex="100">
|
||||||
|
<div layout="row" layout-xs="column" class="md-padding">
|
||||||
|
<div ng-if="'SecondaryEmail' === app.passwordRecovery.passwordRecoveryMode">
|
||||||
|
<var:string label:value="A password reset link has been sent, please check your recovery e-mail mailbox and click on the link"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div layout="row" layout-align="end center">
|
||||||
|
<md-button ng-click="app.passwordRecoveryAbort()" type="button" >
|
||||||
|
<var:string label:value="Back"/>
|
||||||
|
</md-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</var:if>
|
||||||
|
|
||||||
|
<!-- 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>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div layout="column" layout-align="center center"
|
<div layout="column" layout-align="center center"
|
||||||
ng-switch-when="message">
|
ng-switch-when="message">
|
||||||
<md-icon class="md-accent md-hue-1 sg-icon--large">done</md-icon>
|
<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">
|
<div class="md-default-theme md-accent md-hue-1 md-fg md-padding">
|
||||||
{{app.errorMessage}}
|
{{app.errorMessage}}
|
||||||
|
</div>
|
||||||
|
<md-button
|
||||||
|
ng-click="app.continueLogin()"
|
||||||
|
sg-ripple-click="loginContent"><var:string label:value="Continue"/></md-button>
|
||||||
</div>
|
</div>
|
||||||
<md-button
|
|
||||||
ng-click="app.continueLogin()"
|
|
||||||
sg-ripple-click="loginContent"><var:string label:value="Continue"/></md-button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Error -->
|
<!-- Error -->
|
||||||
<div layout="column" layout-align="center center"
|
<div layout="column" layout-align="center center"
|
||||||
ng-switch-when="error">
|
ng-switch-when="error">
|
||||||
<md-icon class="md-accent md-hue-1 sg-icon--large">error</md-icon>
|
<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">
|
<div class="md-default-theme md-accent md-hue-1 md-fg md-padding">
|
||||||
{{app.errorMessage}}
|
{{app.errorMessage}}
|
||||||
|
</div>
|
||||||
|
<md-button
|
||||||
|
ng-click="app.restoreLogin()"
|
||||||
|
sg-ripple-click="loginContent"><var:string label:value="Retry"/></md-button>
|
||||||
</div>
|
</div>
|
||||||
<md-button
|
|
||||||
ng-click="app.restoreLogin()"
|
|
||||||
sg-ripple-click="loginContent"><var:string label:value="Retry"/></md-button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</sg-ripple-content>
|
</sg-ripple-content>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
</div>
|
</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>
|
||||||
</div>
|
</div>
|
||||||
</md-content>
|
</md-content>
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -178,6 +178,45 @@
|
|||||||
return d.promise;
|
return d.promise;
|
||||||
}, // login: function(data) { ...
|
}, // 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) {
|
changePassword: function(userName, domain, newPassword, oldPassword, token) {
|
||||||
var d = $q.defer(),
|
var d = $q.defer(),
|
||||||
xsrfCookie = $cookies.get('XSRF-TOKEN');
|
xsrfCookie = $cookies.get('XSRF-TOKEN');
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -210,9 +210,7 @@
|
|||||||
}
|
}
|
||||||
url = url.join('/');
|
url = url.join('/');
|
||||||
popupWindow = $window.open(url, wId,
|
popupWindow = $window.open(url, wId,
|
||||||
["width=680",
|
["resizable=1",
|
||||||
"height=520",
|
|
||||||
"resizable=1",
|
|
||||||
"scrollbars=1",
|
"scrollbars=1",
|
||||||
"toolbar=0",
|
"toolbar=0",
|
||||||
"location=0",
|
"location=0",
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -20,6 +20,8 @@
|
|||||||
domain: null,
|
domain: null,
|
||||||
rememberLogin: angular.isDefined($window.cookieUsername) && $window.cookieUsername.length > 0
|
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
|
// Send selected language only if user has changed it
|
||||||
if (/\blanguage=/.test($window.location.search))
|
if (/\blanguage=/.test($window.location.search))
|
||||||
this.creds.language = $window.language;
|
this.creds.language = $window.language;
|
||||||
@@ -164,6 +166,64 @@
|
|||||||
return false;
|
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 () {
|
this.restoreLogin = function () {
|
||||||
if ('SecretQuestion' === vm.passwordRecovery.passwordRecoveryMode) {
|
if ('SecretQuestion' === vm.passwordRecovery.passwordRecoveryMode) {
|
||||||
rippleDo('loginContent');
|
rippleDo('loginContent');
|
||||||
|
|||||||
Reference in New Issue
Block a user