mirror of
https://github.com/Alinto/sogo.git
synced 2025-04-18 10:04:00 +03:00
feat(openID): first stable version
This commit is contained in:
parent
5cba10ccff
commit
458d39d48a
@ -437,8 +437,8 @@ Defaults to `300`.
|
||||
|
||||
|S |SOGoAuthenticationType
|
||||
|Parameter used to define the way by which users will be authenticated.
|
||||
For C.A.S., specify `cas`. For SAML2, specify `saml2`. For anything
|
||||
else, leave that value empty.
|
||||
For C.A.S., specify `cas`. For SAML2, specify `saml2`. For opendID Connect, specify `openid`.
|
||||
For anything else, leave that value empty.
|
||||
|
||||
|S |SOGoTrustProxyAuthentication
|
||||
|Parameter used to set whether HTTP username should be trusted.
|
||||
|
36
Main/SOGo.m
36
Main/SOGo.m
@ -288,16 +288,16 @@ static BOOL debugLeaks;
|
||||
|
||||
e = [urlStrings objectEnumerator];
|
||||
while (ok && (tmp = [e nextObject]))
|
||||
{
|
||||
value = [defaults stringForKey: tmp];
|
||||
if (value)
|
||||
[self _checkTableWithCM: cm tableURL: value andType: tmp];
|
||||
else
|
||||
{
|
||||
value = [defaults stringForKey: tmp];
|
||||
if (value)
|
||||
[self _checkTableWithCM: cm tableURL: value andType: tmp];
|
||||
else
|
||||
{
|
||||
[self errorWithFormat: @"No value specified for '%@'", tmp];
|
||||
ok = NO;
|
||||
}
|
||||
[self errorWithFormat: @"No value specified for '%@'", tmp];
|
||||
ok = NO;
|
||||
}
|
||||
}
|
||||
|
||||
if (combined)
|
||||
{
|
||||
@ -322,9 +322,23 @@ static BOOL debugLeaks;
|
||||
|
||||
// Create the email alarms table, if required
|
||||
if ([defaults enableEMailAlarms])
|
||||
{
|
||||
[[fm alarmsFolder] createFolderIfNotExists];
|
||||
}
|
||||
{
|
||||
[[fm alarmsFolder] createFolderIfNotExists];
|
||||
}
|
||||
|
||||
//Create mandatory openId table, if used
|
||||
if([[defaults authenticationType] isEqualToString: @"openid"])
|
||||
{
|
||||
value = [defaults stringForKey: @"OCSOpenIdURL"];
|
||||
if (value)
|
||||
[[fm openIdFolder] createFolderIfNotExists];
|
||||
else
|
||||
{
|
||||
[self errorWithFormat: @"No value specified for 'OCSOpenIdURL' for auth mode %@", [defaults authenticationType]];
|
||||
ok = NO;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return ok;
|
||||
|
@ -31,7 +31,7 @@
|
||||
*/
|
||||
|
||||
@class NSString, NSArray, NSURL, NSDictionary, NSException;
|
||||
@class GCSChannelManager, GCSAlarmsFolder, GCSAdminFolder, GCSFolder, GCSFolderType, GCSSessionsFolder;
|
||||
@class GCSChannelManager, GCSAlarmsFolder, GCSAdminFolder, GCSFolder, GCSFolderType, GCSSessionsFolder, GCSOpenIdFolder;
|
||||
|
||||
@interface GCSFolderManager : NSObject
|
||||
{
|
||||
@ -92,6 +92,9 @@
|
||||
/* admin */
|
||||
- (GCSAdminFolder *)adminFolder;
|
||||
|
||||
/* openid */
|
||||
- (GCSOpenIdFolder *) openIdFolder;
|
||||
|
||||
/* folder types */
|
||||
|
||||
- (GCSFolderType *)folderTypeWithName:(NSString *)_name;
|
||||
|
@ -38,6 +38,7 @@
|
||||
#import "EOAdaptorChannel+GCS.h"
|
||||
#import "GCSAlarmsFolder.h"
|
||||
#import "GCSAdminFolder.h"
|
||||
#import "GCSOpenIdFolder.h"
|
||||
#import "GCSFolder.h"
|
||||
#import "GCSFolderType.h"
|
||||
#import "GCSSessionsFolder.h"
|
||||
@ -504,6 +505,12 @@ static BOOL _singleStoreMode = NO;
|
||||
return [GCSAdminFolder adminFolderWithFolderManager: self];
|
||||
}
|
||||
|
||||
/* openId */
|
||||
- (GCSOpenIdFolder *) openIdFolder
|
||||
{
|
||||
return [GCSOpenIdFolder openIdFolderWithFolderManager: self];
|
||||
}
|
||||
|
||||
- (NSString *)generateSQLWhereForInternalNames:(NSArray *)_names
|
||||
exactMatch:(BOOL)_beExact orDirectSubfolderMatch:(BOOL)_directSubs
|
||||
{
|
||||
|
57
SOPE/GDLContentStore/GCSOpenIdFolder.h
Normal file
57
SOPE/GDLContentStore/GCSOpenIdFolder.h
Normal file
@ -0,0 +1,57 @@
|
||||
/* GCSAdminFolder.h - this file is part of $PROJECT_NAME_HERE$
|
||||
*
|
||||
* Copyright (C) 2023 Alinto
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef GCSOPENIDFOLDER_H
|
||||
#define GCSOPENIDOLDER_H
|
||||
|
||||
|
||||
@class NSCalendarDate;
|
||||
@class NSException;
|
||||
@class NSNumber;
|
||||
@class NSString;
|
||||
|
||||
@class GCSFolderManager;
|
||||
|
||||
@interface GCSOpenIdFolder : NSObject
|
||||
{
|
||||
GCSFolderManager *folderManager;
|
||||
}
|
||||
|
||||
+ (id)openIdFolderWithFolderManager:(GCSFolderManager *)newFolderManager;
|
||||
|
||||
- (void) setFolderManager: (GCSFolderManager *) newFolderManager;
|
||||
|
||||
/* operations */
|
||||
|
||||
- (void) createFolderIfNotExists;
|
||||
- (BOOL) canConnectStore;
|
||||
|
||||
- (NSString *) getRefreshToken: (NSString *) _user_session useOldSession: (BOOL) use_old_session;
|
||||
- (NSString *) getNewToken: (NSString *) _old_session;
|
||||
- (NSException *) writeOpenIdSession: (NSString *) _user_session
|
||||
withOldSession: (NSString *) _old_session
|
||||
withRefreshToken: (NSString *) _refresh_token
|
||||
withExpire: (NSNumber *) _expire
|
||||
withRefreshExpire: (NSNumber *) _refresh_expire;
|
||||
- (NSException *) deleteOpenIdSessionFor: (NSString *) _user_session;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* GCSALARMSFOLDER_H */
|
363
SOPE/GDLContentStore/GCSOpenIdFolder.m
Normal file
363
SOPE/GDLContentStore/GCSOpenIdFolder.m
Normal file
@ -0,0 +1,363 @@
|
||||
/* GCSAdminFolder.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2023 Alinto
|
||||
*
|
||||
* 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 <NGExtensions/NSObject+Logs.h>
|
||||
#import <NGExtensions/NSNull+misc.h>
|
||||
|
||||
#import <GDLAccess/EOAdaptorContext.h>
|
||||
#import <GDLAccess/EOAttribute.h>
|
||||
#import <GDLAccess/EOEntity.h>
|
||||
#import <GDLAccess/EOSQLQualifier.h>
|
||||
|
||||
#import "EOQualifier+GCS.h"
|
||||
#import "GCSChannelManager.h"
|
||||
#import "GCSFolderManager.h"
|
||||
#import "GCSSpecialQueries.h"
|
||||
#import "NSURL+GCS.h"
|
||||
|
||||
#import "GCSOpenIdFolder.h"
|
||||
|
||||
static NSString *openIdFolderURLString = nil;
|
||||
|
||||
#warning GCSOpenIdFolder should share a common ancestor with GCSFolder
|
||||
|
||||
@implementation GCSOpenIdFolder
|
||||
|
||||
+ (void) initialize
|
||||
{
|
||||
NSUserDefaults *ud;
|
||||
|
||||
if (!openIdFolderURLString)
|
||||
{
|
||||
ud = [NSUserDefaults standardUserDefaults];
|
||||
ASSIGN (openIdFolderURLString, [ud stringForKey: @"OCSOpenIdURL"]);
|
||||
}
|
||||
}
|
||||
|
||||
+ (id) openIdFolderWithFolderManager: (GCSFolderManager *) newFolderManager
|
||||
{
|
||||
GCSOpenIdFolder *newFolder;
|
||||
|
||||
if (openIdFolderURLString)
|
||||
{
|
||||
newFolder = [self new];
|
||||
[newFolder autorelease];
|
||||
[newFolder setFolderManager: newFolderManager];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self errorWithFormat: @"'OCSOpenIdURL' is not set"];
|
||||
newFolder = nil;
|
||||
}
|
||||
|
||||
return newFolder;
|
||||
}
|
||||
|
||||
- (void) setFolderManager: (GCSFolderManager *) newFolderManager
|
||||
{
|
||||
ASSIGN (folderManager, newFolderManager);
|
||||
}
|
||||
|
||||
/* accessors */
|
||||
|
||||
- (NSURL *) _location
|
||||
{
|
||||
NSURL *location;
|
||||
|
||||
if (openIdFolderURLString)
|
||||
location = [NSURL URLWithString: openIdFolderURLString];
|
||||
else
|
||||
{
|
||||
[self warnWithFormat: @"'OCSOpenIdURL' is not set"];
|
||||
location = nil;
|
||||
}
|
||||
|
||||
return location;
|
||||
}
|
||||
|
||||
- (GCSChannelManager *) _channelManager
|
||||
{
|
||||
return [folderManager channelManager];
|
||||
}
|
||||
|
||||
- (NSString *) _storeTableName
|
||||
{
|
||||
return [[self _location] gcsTableName];
|
||||
}
|
||||
|
||||
- (EOEntity *) _storeTableEntityForChannel: (EOAdaptorChannel *) tc
|
||||
{
|
||||
static EOEntity *entity = nil;
|
||||
EOAttribute *attribute;
|
||||
NSString *tableName;
|
||||
NSString *columns[] = {@"c_user_session", @"c_old_session", @"c_session_started", @"c_refresh_token", @"c_access_token_expires_in", @"c_refresh_token_expires_in", nil };
|
||||
NSString **column;
|
||||
NSMutableArray *keys;
|
||||
NSDictionary *types;
|
||||
|
||||
if (!entity)
|
||||
{
|
||||
entity = [EOEntity new];
|
||||
tableName = [self _storeTableName];
|
||||
[entity setName: tableName];
|
||||
[entity setExternalName: tableName];
|
||||
|
||||
types = [[tc specialQueries] openIdAttributeTypes];
|
||||
|
||||
column = columns;
|
||||
while (*column)
|
||||
{
|
||||
attribute = [EOAttribute new];
|
||||
[attribute setName: *column];
|
||||
[attribute setColumnName: *column];
|
||||
[attribute setExternalType: [types objectForKey: *column]];
|
||||
[entity addAttribute: attribute];
|
||||
[attribute release];
|
||||
column++;
|
||||
}
|
||||
|
||||
keys = [NSMutableArray arrayWithCapacity: 1];
|
||||
[keys addObject: [entity attributeNamed: @"c_user_session"]];
|
||||
[entity setPrimaryKeyAttributes: keys];
|
||||
|
||||
keys = [NSMutableArray arrayWithCapacity: 5];
|
||||
[keys addObject: [entity attributeNamed: @"c_old_session"]];
|
||||
[keys addObject: [entity attributeNamed: @"c_session_started"]];
|
||||
[keys addObject: [entity attributeNamed: @"c_refresh_token"]];
|
||||
[keys addObject: [entity attributeNamed: @"c_access_token_expires_in"]];
|
||||
[keys addObject: [entity attributeNamed: @"c_refresh_token_expires_in"]];
|
||||
[entity setClassProperties: keys];
|
||||
[entity setAttributesUsedForLocking: [NSArray array]];
|
||||
}
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
/* connection */
|
||||
|
||||
- (EOAdaptorChannel *) _acquireStoreChannel
|
||||
{
|
||||
return [[self _channelManager] acquireOpenChannelForURL: [self _location]];
|
||||
}
|
||||
|
||||
- (void) _releaseChannel: (EOAdaptorChannel *) _channel
|
||||
{
|
||||
[[self _channelManager] releaseChannel:_channel immediately: YES];
|
||||
}
|
||||
|
||||
- (BOOL) canConnectStore
|
||||
{
|
||||
return [[self _channelManager] canConnect:[self _location]];
|
||||
}
|
||||
|
||||
- (void) createFolderIfNotExists
|
||||
{
|
||||
EOAdaptorChannel *tc;
|
||||
NSString *sql, *tableName;
|
||||
GCSSpecialQueries *queries;
|
||||
|
||||
tc = [self _acquireStoreChannel];
|
||||
tableName = [self _storeTableName];
|
||||
|
||||
queries = [tc specialQueries];
|
||||
|
||||
sql = [NSString stringWithFormat: @"SELECT 1 FROM %@ WHERE 1 = 2", tableName];
|
||||
if ([tc evaluateExpressionX: sql])
|
||||
{
|
||||
sql = [queries createOpenIdFolderWithName: tableName];
|
||||
if (![tc evaluateExpressionX: sql])
|
||||
[self logWithFormat: @"openid folder table '%@' successfully created!", tableName];
|
||||
}
|
||||
else
|
||||
[tc cancelFetch];
|
||||
|
||||
[self _releaseChannel: tc];
|
||||
}
|
||||
|
||||
/* operations */
|
||||
|
||||
/* table has the following fields:
|
||||
c_user_session varchar(255) NOT NULL,
|
||||
c_session_started int(11) NOT NULL,
|
||||
c_refresh_token varchar(4096) DEFAULT '',
|
||||
c_access_token_expires_in int(11) NOT NULL,
|
||||
c_refresh_token_expires_in int(11) DEFAULT NULL,
|
||||
*/
|
||||
|
||||
- (NSDictionary *) recordForSession: (NSString *) _user_session useOldSession: (BOOL) use_old_session
|
||||
{
|
||||
EOAdaptorChannel *tc;
|
||||
EOAdaptorContext *context;
|
||||
NSException *error;
|
||||
NSArray *attrs;
|
||||
NSDictionary *record;
|
||||
EOEntity *entity;
|
||||
EOSQLQualifier *qualifier;
|
||||
|
||||
record = nil;
|
||||
|
||||
tc = [self _acquireStoreChannel];
|
||||
if (tc)
|
||||
{
|
||||
context = [tc adaptorContext];
|
||||
entity = [self _storeTableEntityForChannel: tc];
|
||||
if(!use_old_session)
|
||||
qualifier = [[EOSQLQualifier alloc] initWithEntity: entity
|
||||
qualifierFormat: @"c_user_session='%@'", _user_session];
|
||||
else
|
||||
qualifier = [[EOSQLQualifier alloc] initWithEntity: entity
|
||||
qualifierFormat: @"c_old_session='%@'", _user_session];
|
||||
[qualifier autorelease];
|
||||
|
||||
[context beginTransaction];
|
||||
error = [tc selectAttributesX: [entity attributesUsedForFetch]
|
||||
describedByQualifier: qualifier
|
||||
fetchOrder: nil
|
||||
lock: NO];
|
||||
if (error)
|
||||
[self errorWithFormat:@"%s: cannot execute fetch: %@", __PRETTY_FUNCTION__, error];
|
||||
else
|
||||
{
|
||||
attrs = [tc describeResults: NO];
|
||||
record = [tc fetchAttributes: attrs withZone: NULL];
|
||||
[tc cancelFetch];
|
||||
}
|
||||
[context rollbackTransaction];
|
||||
[self _releaseChannel: tc];
|
||||
}
|
||||
|
||||
return record;
|
||||
}
|
||||
|
||||
- (NSString *) getRefreshToken: (NSString *) _user_session
|
||||
{
|
||||
NSDictionary *r;
|
||||
|
||||
r = [self recordForSession: _user_session useOldSession: NO];
|
||||
if (r && [r objectForKey:@"c_refresh_token"])
|
||||
return [r objectForKey:@"c_refresh_token"];
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSString *) getNewToken: (NSString *) _old_session
|
||||
{
|
||||
NSDictionary *r;
|
||||
r = [self recordForSession: _old_session useOldSession: YES];
|
||||
if (r && [r objectForKey:@"c_user_session"])
|
||||
return [r objectForKey:@"c_user_session"];
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
- (NSException *) writeOpenIdSession: (NSString *) _user_session
|
||||
withOldSession: (NSString *) _old_session
|
||||
withRefreshToken: (NSString *) _refresh_token
|
||||
withExpire: (NSNumber *) _expire
|
||||
withRefreshExpire: (NSNumber *) _refresh_expire
|
||||
{
|
||||
NSDictionary *record, *newRecord;
|
||||
NSException *error;
|
||||
NSCalendarDate *nowDate;
|
||||
int now, nowExpire, nowRefreshExpire;
|
||||
EOAdaptorChannel *tc;
|
||||
EOAdaptorContext *context;
|
||||
EOEntity *entity;
|
||||
EOSQLQualifier *qualifier;
|
||||
|
||||
error = nil;
|
||||
tc = [self _acquireStoreChannel];
|
||||
if (tc)
|
||||
{
|
||||
context = [tc adaptorContext];
|
||||
|
||||
nowDate = [NSCalendarDate date];
|
||||
now = (nowDate ? (int)[nowDate timeIntervalSince1970] : 0);
|
||||
nowExpire = now + [_expire intValue];
|
||||
if(_refresh_expire)
|
||||
nowRefreshExpire = now + [_refresh_expire intValue];
|
||||
else
|
||||
nowRefreshExpire = -1;
|
||||
if(!_old_session)
|
||||
_old_session = @"";
|
||||
|
||||
newRecord = [NSDictionary dictionaryWithObjectsAndKeys: _user_session, @"c_user_session",
|
||||
_old_session, @"c_old_session",
|
||||
[NSNumber numberWithInt:now], @"c_session_started",
|
||||
_refresh_token, @"c_refresh_token",
|
||||
[NSNumber numberWithInt:nowExpire] , @"c_access_token_expires_in",
|
||||
[NSNumber numberWithInt:nowRefreshExpire] , @"c_refresh_token_expires_in",
|
||||
nil];
|
||||
record = [self recordForSession: _user_session useOldSession: NO];
|
||||
entity = [self _storeTableEntityForChannel: tc];
|
||||
[context beginTransaction];
|
||||
if (!record)
|
||||
{
|
||||
//If the session already exist no need to update it as it is unique
|
||||
error = [tc insertRowX: newRecord forEntity: entity];
|
||||
}
|
||||
|
||||
if (error)
|
||||
{
|
||||
[context rollbackTransaction];
|
||||
[self errorWithFormat:@"%s: cannot write record: %@", __PRETTY_FUNCTION__, error];
|
||||
}
|
||||
else
|
||||
[context commitTransaction];
|
||||
[self _releaseChannel: tc];
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
- (NSException *) deleteOpenIdSessionFor: (NSString *) _user_session
|
||||
{
|
||||
EOAdaptorChannel *tc;
|
||||
EOAdaptorContext *context;
|
||||
EOEntity *entity;
|
||||
EOSQLQualifier *qualifier;
|
||||
NSException *error;
|
||||
|
||||
error = nil;
|
||||
tc = [self _acquireStoreChannel];
|
||||
if (tc)
|
||||
{
|
||||
context = [tc adaptorContext];
|
||||
entity = [self _storeTableEntityForChannel: tc];
|
||||
qualifier = [[EOSQLQualifier alloc] initWithEntity: entity
|
||||
qualifierFormat: @"c_user_session='%@'",_user_session];
|
||||
[qualifier autorelease];
|
||||
[context beginTransaction];
|
||||
error = [tc deleteRowsDescribedByQualifierX: qualifier];
|
||||
if (error)
|
||||
{
|
||||
[context rollbackTransaction];
|
||||
[self errorWithFormat:@"%s: cannot delete record: %@", __PRETTY_FUNCTION__, error];
|
||||
}
|
||||
else
|
||||
[context commitTransaction];
|
||||
[self _releaseChannel: tc];
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
@end
|
@ -42,6 +42,9 @@
|
||||
- (NSString *)createAdminFolderWithName:(NSString *)tableName;
|
||||
- (NSDictionary *)adminAttributeTypes;
|
||||
|
||||
- (NSString *)createOpenIdFolderWithName:(NSString *)tableName;
|
||||
- (NSDictionary *)openIdAttributeTypes;
|
||||
|
||||
- (NSString *) updateCPathInFolderInfo:(NSString *)tableName
|
||||
withCPath2:(NSString *)c_path2;
|
||||
|
||||
|
@ -95,6 +95,21 @@
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSString *) createOpenIdFolderWithName: (NSString *) tableName
|
||||
{
|
||||
[self subclassResponsibility: _cmd];
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSDictionary *) openIdAttributeTypes
|
||||
{
|
||||
[self subclassResponsibility: _cmd];
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
- (NSString *) createFolderTableWithName: (NSString *) tableName
|
||||
{
|
||||
[self subclassResponsibility: _cmd];
|
||||
@ -195,6 +210,38 @@
|
||||
return types;
|
||||
}
|
||||
|
||||
- (NSString *) createOpenIdFolderWithName: (NSString *) tableName
|
||||
{
|
||||
static NSString *sqlFolderFormat
|
||||
= (@"CREATE TABLE %@ ("
|
||||
@" c_user_session VARCHAR(4096) NOT NULL,"
|
||||
@" c_old_session VARCHAR(4096) NULL,"
|
||||
@" c_session_started INT4 NOT NULL,"
|
||||
@" c_refresh_token VARCHAR(4096) NULL,"
|
||||
@" c_access_token_expires_in INT4 NOT NULL,"
|
||||
@" c_refresh_token_expires_in INT4 NULL)");
|
||||
|
||||
return [NSString stringWithFormat: sqlFolderFormat, tableName];
|
||||
}
|
||||
|
||||
- (NSDictionary *) openIdAttributeTypes
|
||||
{
|
||||
static NSMutableDictionary *types = nil;
|
||||
|
||||
if (!types)
|
||||
{
|
||||
types = [NSMutableDictionary new];
|
||||
[types setObject: @"varchar" forKey: @"c_user_session"];
|
||||
[types setObject: @"varchar" forKey: @"c_old_session"];
|
||||
[types setObject: @"int" forKey: @"c_session_started"];
|
||||
[types setObject: @"varchar" forKey: @"c_refresh_token"];
|
||||
[types setObject: @"int" forKey: @"c_access_token_expires_in"];
|
||||
[types setObject: @"int" forKey: @"c_refresh_token_expires_in"];
|
||||
}
|
||||
|
||||
return types;
|
||||
}
|
||||
|
||||
- (NSString *) createFolderTableWithName: (NSString *) tableName
|
||||
{
|
||||
static NSString *sqlFolderFormat
|
||||
@ -324,6 +371,38 @@
|
||||
return types;
|
||||
}
|
||||
|
||||
- (NSString *) createOpenIdFolderWithName: (NSString *) tableName
|
||||
{
|
||||
static NSString *sqlFolderFormat
|
||||
= (@"CREATE TABLE %@ ("
|
||||
@" c_user_session VARCHAR(4096) NOT NULL,"
|
||||
@" c_old_session VARCHAR(4096) NULL,"
|
||||
@" c_session_started INT4 NOT NULL,"
|
||||
@" c_refresh_token VARCHAR(4096) NULL,"
|
||||
@" c_access_token_expires_in INT4 NOT NULL,"
|
||||
@" c_refresh_token_expires_in INT4 NULL)");
|
||||
|
||||
return [NSString stringWithFormat: sqlFolderFormat, tableName];
|
||||
}
|
||||
|
||||
- (NSDictionary *) openIdAttributeTypes
|
||||
{
|
||||
static NSMutableDictionary *types = nil;
|
||||
|
||||
if (!types)
|
||||
{
|
||||
types = [NSMutableDictionary new];
|
||||
[types setObject: @"varchar" forKey: @"c_user_session"];
|
||||
[types setObject: @"varchar" forKey: @"c_old_session"];
|
||||
[types setObject: @"int" forKey: @"c_session_started"];
|
||||
[types setObject: @"varchar" forKey: @"c_refresh_token"];
|
||||
[types setObject: @"int" forKey: @"c_access_token_expires_in"];
|
||||
[types setObject: @"int" forKey: @"c_refresh_token_expires_in"];
|
||||
}
|
||||
|
||||
return types;
|
||||
}
|
||||
|
||||
- (NSString *) createFolderTableWithName: (NSString *) tableName
|
||||
{
|
||||
static NSString *sqlFolderFormat
|
||||
@ -453,6 +532,38 @@
|
||||
return types;
|
||||
}
|
||||
|
||||
- (NSString *) createOpenIdFolderWithName: (NSString *) tableName
|
||||
{
|
||||
static NSString *sqlFolderFormat
|
||||
= (@"CREATE TABLE %@ ("
|
||||
@" c_user_session VARCHAR2(4096) NOT NULL,"
|
||||
@" c_old_session VARCHAR2(4096) NULL,"
|
||||
@" c_session_started INTEGER NOT NULL,"
|
||||
@" c_refresh_token VARCHAR2(4096) NULL,"
|
||||
@" c_access_token_expires_in INTEGER NOT NULL,"
|
||||
@" c_refresh_token_expires_in INTEGER NULL)");
|
||||
|
||||
return [NSString stringWithFormat: sqlFolderFormat, tableName];
|
||||
}
|
||||
|
||||
- (NSDictionary *) openIdAttributeTypes
|
||||
{
|
||||
static NSMutableDictionary *types = nil;
|
||||
|
||||
if (!types)
|
||||
{
|
||||
types = [NSMutableDictionary new];
|
||||
[types setObject: @"varchar2" forKey: @"c_user_session"];
|
||||
[types setObject: @"varchar2" forKey: @"c_old_session"];
|
||||
[types setObject: @"integer" forKey: @"c_session_started"];
|
||||
[types setObject: @"varchar2" forKey: @"c_refresh_token"];
|
||||
[types setObject: @"integer" forKey: @"c_access_token_expires_in"];
|
||||
[types setObject: @"integer" forKey: @"c_refresh_token_expires_in"];
|
||||
}
|
||||
|
||||
return types;
|
||||
}
|
||||
|
||||
- (NSString *) createFolderTableWithName: (NSString *) tableName
|
||||
{
|
||||
static NSString *sqlFolderFormat
|
||||
|
@ -28,13 +28,14 @@ libGDLContentStore_HEADER_FILES += \
|
||||
GCSAdminFolder.h \
|
||||
GCSContext.h \
|
||||
GCSFieldInfo.h \
|
||||
GCSFolder.h \
|
||||
GCSFolder.h \
|
||||
GCSFolderManager.h \
|
||||
GCSFolderType.h \
|
||||
GCSChannelManager.h \
|
||||
GCSOpenIdFolder.h \
|
||||
GCSSessionsFolder.h \
|
||||
GCSSpecialQueries.h \
|
||||
GCSStringFormatter.h \
|
||||
GCSStringFormatter.h \
|
||||
|
||||
libGDLContentStore_OBJC_FILES += \
|
||||
NSURL+GCS.m \
|
||||
@ -45,13 +46,14 @@ libGDLContentStore_OBJC_FILES += \
|
||||
GCSAdminFolder.m \
|
||||
GCSContext.m \
|
||||
GCSFieldInfo.m \
|
||||
GCSFolder.m \
|
||||
GCSFolder.m \
|
||||
GCSFolderManager.m \
|
||||
GCSFolderType.m \
|
||||
GCSChannelManager.m \
|
||||
GCSOpenIdFolder.m \
|
||||
GCSSessionsFolder.m \
|
||||
GCSSpecialQueries.m \
|
||||
GCSStringFormatter.m \
|
||||
GCSStringFormatter.m \
|
||||
|
||||
# framework support
|
||||
|
||||
|
@ -165,6 +165,16 @@ CREATE TABLE sogo_admin (
|
||||
PRIMARY KEY (c_key)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
|
||||
|
||||
CREATE TABLE sogo_opend_id (
|
||||
c_user_session varchar(4096) NOT NULL,
|
||||
c_old_session varchar(4096) DEFAULT '',
|
||||
c_session_started int(11) NOT NULL,
|
||||
c_refresh_token varchar(4096) DEFAULT '',
|
||||
c_access_token_expires_in int(11) NOT NULL,
|
||||
c_refresh_token_expires_in int(11) DEFAULT NULL,
|
||||
PRIMARY KEY (c_user_session)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
|
||||
|
||||
CREATE TABLE sogo_user_profile (
|
||||
c_uid varchar(255) NOT NULL,
|
||||
c_defaults longtext,
|
||||
|
@ -152,7 +152,7 @@
|
||||
host = [NGInternetSocketAddress addressWithPort:0 onHost:[[self imap4URL] host]];
|
||||
|
||||
sd = [SOGoSystemDefaults sharedSystemDefaults];
|
||||
usesSSO = [[sd authenticationType] isEqualToString: @"cas"] || [[sd authenticationType] isEqualToString: @"saml2"];
|
||||
usesSSO = [sd isSsoUsed];
|
||||
|
||||
if (![[[self mailAccountFolder] nameInContainer] isEqualToString: @"0"] &&
|
||||
usesSSO &&
|
||||
|
@ -69,6 +69,7 @@ SOGo_HEADER_FILES = \
|
||||
SOGoAuthenticator.h \
|
||||
SOGoSession.h \
|
||||
SOGoCASSession.h \
|
||||
SOGoOpenIdSession.h \
|
||||
SOGoDAVAuthenticator.h \
|
||||
SOGoProxyAuthenticator.h \
|
||||
SOGoStaticAuthenticator.h \
|
||||
@ -161,6 +162,7 @@ SOGo_OBJC_FILES = \
|
||||
\
|
||||
SOGoSession.m \
|
||||
SOGoCASSession.m \
|
||||
SOGoOpenIdSession.m \
|
||||
SOGoDAVAuthenticator.m \
|
||||
SOGoProxyAuthenticator.m \
|
||||
SOGoStaticAuthenticator.m \
|
||||
|
@ -17,6 +17,7 @@ SOGo_LIBRARIES_DEPEND_UPON += \
|
||||
-Wl,--no-as-needed \
|
||||
-L../../SOPE/NGCards/$(GNUSTEP_OBJ_DIR)/ \
|
||||
-lmemcached \
|
||||
-lcurl \
|
||||
-lGDLAccess \
|
||||
-lNGObjWeb \
|
||||
-lNGCards \
|
||||
|
@ -119,6 +119,13 @@
|
||||
|
||||
- (void) removeCASSessionWithTicket: (NSString *) ticket;
|
||||
|
||||
// OpenId Support
|
||||
- (NSString *) openIdSessionFromServer: (NSString *) endpoint;
|
||||
- (void) setOpenIdSession: (NSString *) openIdSession
|
||||
forServer: (NSString *) endpoint;
|
||||
- (NSString *) userOpendIdSessionFromServer: (NSString *) endpoint;
|
||||
- (void) setUserOpendIdSessionFromServer: (NSString *) endpoint
|
||||
nextCheckAfter: (NSString *) nextCheck;
|
||||
//
|
||||
// SAML2 support
|
||||
//
|
||||
|
@ -683,6 +683,32 @@ static memcached_st *handle = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// OpenId support
|
||||
|
||||
- (NSString *) openIdSessionFromServer: (NSString *) endpoint
|
||||
{
|
||||
return [self valueForKey: [NSString stringWithFormat: @"openId:%@", endpoint]];
|
||||
}
|
||||
|
||||
- (void) setOpenIdSession: (NSString *) openIdSession
|
||||
forServer: (NSString *) endpoint
|
||||
{
|
||||
[self setValue: openIdSession
|
||||
forKey: [NSString stringWithFormat: @"openId:%@", endpoint]];
|
||||
}
|
||||
|
||||
- (NSString *) userOpendIdSessionFromServer: (NSString *) identifier
|
||||
{
|
||||
return [self valueForKey: [NSString stringWithFormat: @"userOpenId:%@", identifier]];
|
||||
}
|
||||
- (void) setUserOpendIdSessionFromServer: (NSString *) identifier
|
||||
nextCheckAfter: (NSString *) nextCheck
|
||||
{
|
||||
[self setValue: nextCheck
|
||||
forKey: [NSString stringWithFormat: @"userOpenId:%@", identifier]];
|
||||
}
|
||||
|
||||
//
|
||||
// SAML2 support
|
||||
//
|
||||
|
@ -266,6 +266,7 @@
|
||||
systemMessage: (BOOL) isSystemMessage
|
||||
{
|
||||
NSString *currentTo, *login, *password;
|
||||
NSString * smtpAuthMethod;
|
||||
NSDictionary *currentAcount;
|
||||
NSMutableArray *toErrors;
|
||||
NSEnumerator *addresses;
|
||||
@ -288,7 +289,9 @@
|
||||
currentAcount = [[user mailAccounts] objectAtIndex: userId];
|
||||
|
||||
//Check if we do an smtp authentication
|
||||
doSmtpAuth = [authenticationType isEqualToString: @"plain"] && ![authenticator isKindOfClass: [SOGoEmptyAuthenticator class]];
|
||||
smtpAuthMethod = authenticationType;
|
||||
doSmtpAuth = ([smtpAuthMethod isEqualToString: @"plain"] || [smtpAuthMethod isEqualToString: @"xoauth2"])
|
||||
&& ![authenticator isKindOfClass: [SOGoEmptyAuthenticator class]];
|
||||
if(!doSmtpAuth && userId > 0)
|
||||
{
|
||||
doSmtpAuth = [currentAcount objectForKey: @"smtpAuth"] ? [[currentAcount objectForKey: @"smtpAuth"] boolValue] : NO;
|
||||
@ -304,6 +307,7 @@
|
||||
{
|
||||
login = [currentAcount objectForKey: @"userName"];
|
||||
password = [currentAcount objectForKey: @"password"];
|
||||
smtpAuthMethod = "plain"; //Only support plain for auxiliary account
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -322,8 +326,9 @@
|
||||
if (isSystemMessage
|
||||
&& ![[[SOGoUserManager sharedUserManager] getEmailForUID: [[authenticator userInContext: woContext] loginInDomain]] isEqualToString: sender]
|
||||
&& smtpMasterUserEnabled) {
|
||||
if (![client plainAuthenticateUser: smtpMasterUserUsername
|
||||
withPassword: smtpMasterUserPassword]) {
|
||||
if (![client authenticateUser: smtpMasterUserUsername
|
||||
withPassword: smtpMasterUserPassword
|
||||
withMethod: smtpAuthMethod]) {
|
||||
result = [NSException exceptionWithHTTPStatus: 500
|
||||
reason: @"cannot send message:"
|
||||
@" (smtp) authentication failure"];
|
||||
@ -334,8 +339,9 @@
|
||||
{
|
||||
if ([login length] == 0
|
||||
|| [login isEqualToString: @"anonymous"]
|
||||
|| ![client plainAuthenticateUser: login
|
||||
withPassword: password])
|
||||
|| ![client authenticateUser: login
|
||||
withPassword: password
|
||||
withMethod: smtpAuthMethod])
|
||||
result = [NSException exceptionWithHTTPStatus: 500
|
||||
reason: @"cannot send message:"
|
||||
@" (smtp) authentication failure"];
|
||||
|
95
SoObjects/SOGo/SOGoOpenIdSession.h
Normal file
95
SoObjects/SOGo/SOGoOpenIdSession.h
Normal file
@ -0,0 +1,95 @@
|
||||
/* SOGoCASSession.h - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2000-2023 Alinto.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef SOGOOPENIDSESSION_H
|
||||
#define SOGOOPENIDSESSION_H
|
||||
|
||||
/* implementation of the OpenId Connect as required for a client/proxy:
|
||||
https://openid.net/developers/how-connect-works/ */
|
||||
|
||||
#import <NGObjWeb/WOResponse.h>
|
||||
|
||||
|
||||
@class NSString;
|
||||
@class NSMutableDictionary;
|
||||
@class NSURL;
|
||||
@class NSJSONSerialization;
|
||||
|
||||
|
||||
@interface SOGoOpenIdSession : NSObject
|
||||
{
|
||||
//For cache
|
||||
BOOL cacheUpdateNeeded;
|
||||
int userTokenInterval;
|
||||
|
||||
//From sogo.conf
|
||||
BOOL openIdSessionIsOK;
|
||||
NSString *openIdConfigUrl;
|
||||
NSString *openIdScope;
|
||||
NSString *openIdClient;
|
||||
NSString *openIdClientSecret;
|
||||
NSString *openIdEmailParam;
|
||||
BOOL openIdEnableRefreshToken;
|
||||
|
||||
//From request to well-known/configuration
|
||||
NSString *authorizationEndpoint;
|
||||
NSString *tokenEndpoint;
|
||||
NSString *introspectionEndpoint;
|
||||
NSString *userinfoEndpoint;
|
||||
NSString *endSessionEndpoint;
|
||||
NSString *revocationEndpoint;
|
||||
|
||||
//Access token
|
||||
NSString *accessToken;
|
||||
NSString *refreshToken;
|
||||
NSString *idToken;
|
||||
NSString *tokenType;
|
||||
NSNumber *expiresIn;
|
||||
NSNumber *refreshExpiresIn;
|
||||
}
|
||||
|
||||
+ (BOOL) checkUserConfig;
|
||||
+ (SOGoOpenIdSession *) OpenIdSession;
|
||||
+ (void) deleteValueForSessionKey: (NSString *) theSessionKey;
|
||||
|
||||
- (void) initialize;
|
||||
- (BOOL) sessionIsOK;
|
||||
- (WOResponse *) _performOpenIdRequest: (NSString *) endpoint
|
||||
method: (NSString *) method
|
||||
headers: (NSDictionary *) headers
|
||||
body: (NSData *) body;
|
||||
- (NSMutableDictionary *) fecthConfiguration;
|
||||
- (void) setAccessToken;
|
||||
- (NSString *) getRefreshToken;
|
||||
- (NSString *) getToken;
|
||||
- (NSString *) getCurrentToken;
|
||||
- (NSString *) loginUrl: (NSString *) oldLocation;
|
||||
- (NSMutableDictionary *) fetchToken: (NSString *) code redirect: (NSString *) oldLocation;
|
||||
|
||||
- (NSString *) logoutUrl;
|
||||
- (NSMutableDictionary *) fetchUserInfo;
|
||||
- (NSString *) _login;
|
||||
- (NSString *) login: (NSString *) identifier;
|
||||
|
||||
|
||||
@end
|
||||
|
||||
|
||||
#endif /* SOGOOPENIDSESSION_H */
|
663
SoObjects/SOGo/SOGoOpenIdSession.m
Normal file
663
SoObjects/SOGo/SOGoOpenIdSession.m
Normal file
@ -0,0 +1,663 @@
|
||||
/* SOGoCASSession.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2010-2014 Inverse inc.
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* 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 <NGObjWeb/WOHTTPConnection.h>
|
||||
#import <NGObjWeb/WORequest.h>
|
||||
#import <NGObjWeb/WOResponse.h>
|
||||
#import <NGExtensions/NSObject+Logs.h>
|
||||
|
||||
#import <GDLContentStore/GCSOpenIdFolder.h>
|
||||
#import <GDLContentStore/GCSFolderManager.h>
|
||||
|
||||
#import "NSDictionary+Utilities.h"
|
||||
#import "NSString+Utilities.h"
|
||||
#import "SOGoCache.h"
|
||||
#import "SOGoSystemDefaults.h"
|
||||
|
||||
#import "SOGoOpenIdSession.h"
|
||||
|
||||
static BOOL SOGoOpenIDDebugEnabled = YES;
|
||||
|
||||
@implementation SOGoOpenIdSession
|
||||
|
||||
/// Check if all required parameters to set up a OpenIdSession are in sogo.conf
|
||||
|
||||
+ (BOOL) checkUserConfig
|
||||
{
|
||||
SOGoSystemDefaults *sd;
|
||||
|
||||
if(nil == [[GCSFolderManager defaultFolderManager] openIdFolder])
|
||||
{
|
||||
[self errorWithFormat: @"Something wrong with the table defined by OCSOpenIdURL"];
|
||||
return NO;
|
||||
}
|
||||
sd = [SOGoSystemDefaults sharedSystemDefaults];
|
||||
return ([sd openIdConfigUrl] && [sd openIdScope] && [sd openIdClient] && [sd openIdClientSecret]);
|
||||
}
|
||||
|
||||
- (void) initialize
|
||||
{
|
||||
SOGoSystemDefaults *sd;
|
||||
|
||||
// //From sogo.conf
|
||||
// openIdConfigUrl = nil;
|
||||
// openIdScope = nil;
|
||||
// openIdClient = nil;
|
||||
// openIdClientSecret = nil;
|
||||
|
||||
// //From request to well-known/configuration
|
||||
// //SHoud be ste in sogo.cong in case of oauth
|
||||
// authorizationEndpoint = nil;
|
||||
// tokenEndpoint = nil;
|
||||
// introspectionEndpoint = nil;
|
||||
// userinfoEndpoint = nil;
|
||||
// endSessionEndpoint = nil;
|
||||
// revocationEndpoint = nil;
|
||||
|
||||
// //Access token
|
||||
// accessToken = nil;
|
||||
|
||||
sd = [SOGoSystemDefaults sharedSystemDefaults];
|
||||
SOGoOpenIDDebugEnabled = [sd openIdDebugEnabled];
|
||||
openIdSessionIsOK = NO;
|
||||
if ([[self class] checkUserConfig])
|
||||
{
|
||||
openIdConfigUrl = [sd openIdConfigUrl];
|
||||
openIdScope = [sd openIdScope];
|
||||
openIdClient = [sd openIdClient];
|
||||
openIdClientSecret = [sd openIdClientSecret];
|
||||
openIdEmailParam = [sd openIdEmailParam];
|
||||
openIdEnableRefreshToken = [sd openIdEnableRefreshToken];
|
||||
userTokenInterval = [sd openIdTokenCheckInterval];
|
||||
|
||||
[self _loadSessionFromCache];
|
||||
|
||||
if(cacheUpdateNeeded)
|
||||
{
|
||||
[self fecthConfiguration];
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
[self errorWithFormat: @"Missing parameters from sogo.conf"];
|
||||
}
|
||||
}
|
||||
|
||||
- (WOResponse *) _performOpenIdRequest: (NSString *) endpoint
|
||||
method: (NSString *) method
|
||||
headers: (NSDictionary *) headers
|
||||
body: (NSData *) body
|
||||
{
|
||||
NSURL *url;
|
||||
NSUInteger status;
|
||||
WORequest *request;
|
||||
WOResponse *response;
|
||||
WOHTTPConnection *httpConnection;
|
||||
|
||||
|
||||
url = [NSURL URLWithString: endpoint];
|
||||
if (url)
|
||||
{
|
||||
if(SOGoOpenIDDebugEnabled)
|
||||
{
|
||||
NSLog(@"OpenId perform request: %@ %@", method, [endpoint hostlessURL]);
|
||||
NSLog(@"OpenId perform request, headers %@", headers);
|
||||
if(body)
|
||||
NSLog(@"OpenId perform request: content %@", [[NSString alloc] initWithData:body encoding:NSUTF8StringEncoding]);
|
||||
}
|
||||
|
||||
httpConnection = [[WOHTTPConnection alloc] initWithURL: url];
|
||||
[httpConnection autorelease];
|
||||
|
||||
request = [[WORequest alloc] initWithMethod: method
|
||||
uri: [endpoint hostlessURL]
|
||||
httpVersion: @"HTTP/1.1"
|
||||
headers: headers content: body
|
||||
userInfo: nil];
|
||||
[request autorelease];
|
||||
[httpConnection sendRequest: request];
|
||||
response = [httpConnection readResponse];
|
||||
status = [response status];
|
||||
if(status >= 200 && status <500 && status != 404)
|
||||
return response;
|
||||
else if (status == 404)
|
||||
{
|
||||
[self errorWithFormat: @"OpenID endpoint not found (404): %@", endpoint];
|
||||
return nil;
|
||||
}
|
||||
else
|
||||
{
|
||||
[self errorWithFormat: @"OpenID server internal error during %@: %@", endpoint, response];
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
[self errorWithFormat: @"OpenID can't handle endpoint (not a url): '%@'", endpoint];
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSMutableDictionary *) fecthConfiguration
|
||||
{
|
||||
NSString *location, *content;
|
||||
WOResponse * response;
|
||||
NSUInteger status;
|
||||
NSMutableDictionary *result;
|
||||
NSDictionary *config;
|
||||
NSURL *url;
|
||||
|
||||
result = [NSMutableDictionary dictionary];
|
||||
[result setObject: self->openIdConfigUrl forKey: @"url"];
|
||||
|
||||
url = [NSURL URLWithString: self->openIdConfigUrl ];
|
||||
if (url)
|
||||
{
|
||||
response = [self _performOpenIdRequest: self->openIdConfigUrl
|
||||
method: @"GET"
|
||||
headers: nil
|
||||
body: nil];
|
||||
|
||||
if (response)
|
||||
{
|
||||
status = [response status];
|
||||
if(status >= 200 && status <300)
|
||||
{
|
||||
content = [response contentString];
|
||||
config = [content objectFromJSONString];
|
||||
self->authorizationEndpoint = [config objectForKey: @"authorization_endpoint"];
|
||||
self->tokenEndpoint = [config objectForKey: @"token_endpoint"];
|
||||
self->introspectionEndpoint = [config objectForKey: @"introspection_endpoint"];
|
||||
self->userinfoEndpoint = [config objectForKey: @"userinfo_endpoint"];
|
||||
self->endSessionEndpoint = [config objectForKey: @"end_session_endpoint"];
|
||||
self->revocationEndpoint = [config objectForKey: @"revocation_endpoint"];
|
||||
openIdSessionIsOK = YES;
|
||||
[self _saveSessionToCache];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self logWithFormat: @"Error during fetching the configuration (status %d), response: %@", status, response];
|
||||
}
|
||||
}
|
||||
else
|
||||
[result setObject: @"http-error" forKey: @"error"];
|
||||
}
|
||||
else
|
||||
[result setObject: @"invalid-url" forKey: @"error"];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
+ (SOGoOpenIdSession *) OpenIdSession
|
||||
{
|
||||
SOGoOpenIdSession *newSession;
|
||||
|
||||
newSession = [self new];
|
||||
[newSession autorelease];
|
||||
[newSession initialize];
|
||||
|
||||
return newSession;
|
||||
}
|
||||
|
||||
+ (SOGoOpenIdSession *) OpenIdSessionWithToken: (NSString *) token
|
||||
{
|
||||
SOGoOpenIdSession *newSession;
|
||||
|
||||
if (token)
|
||||
{
|
||||
newSession = [self new];
|
||||
[newSession autorelease];
|
||||
[newSession initialize];
|
||||
|
||||
[newSession setAccessToken: token];
|
||||
}
|
||||
else
|
||||
newSession = nil;
|
||||
|
||||
return newSession;
|
||||
}
|
||||
|
||||
- (BOOL) sessionIsOk
|
||||
{
|
||||
return self->openIdSessionIsOK;
|
||||
}
|
||||
|
||||
- (void) _loadSessionFromCache
|
||||
{
|
||||
SOGoCache *cache;
|
||||
NSString *jsonSession;
|
||||
NSDictionary *sessionDict;
|
||||
|
||||
cache = [SOGoCache sharedCache];
|
||||
jsonSession = [cache openIdSessionFromServer: self->openIdConfigUrl];
|
||||
if ([jsonSession length])
|
||||
{
|
||||
sessionDict = [jsonSession objectFromJSONString];
|
||||
ASSIGN (authorizationEndpoint, [sessionDict objectForKey: @"authorization_endpoint"]);
|
||||
ASSIGN (tokenEndpoint, [sessionDict objectForKey: @"token_endpoint"]);
|
||||
ASSIGN (introspectionEndpoint, [sessionDict objectForKey: @"introspection_endpoint"]);
|
||||
ASSIGN (userinfoEndpoint, [sessionDict objectForKey: @"userinfo_endpoint"]);
|
||||
ASSIGN (endSessionEndpoint, [sessionDict objectForKey: @"end_session_endpoint"]);
|
||||
ASSIGN (revocationEndpoint, [sessionDict objectForKey: @"revocation_endpoint"]);
|
||||
openIdSessionIsOK = YES;
|
||||
}
|
||||
else
|
||||
cacheUpdateNeeded = YES;
|
||||
}
|
||||
|
||||
- (void) _saveSessionToCache
|
||||
{
|
||||
SOGoCache *cache;
|
||||
NSString *jsonSession;
|
||||
NSMutableDictionary *sessionDict;
|
||||
|
||||
cache = [SOGoCache sharedCache];
|
||||
sessionDict = [NSMutableDictionary dictionary];
|
||||
[sessionDict setObject: authorizationEndpoint forKey: @"authorization_endpoint"];
|
||||
[sessionDict setObject: tokenEndpoint forKey: @"token_endpoint"];
|
||||
[sessionDict setObject: introspectionEndpoint forKey: @"introspection_endpoint"];
|
||||
[sessionDict setObject: userinfoEndpoint forKey: @"userinfo_endpoint"];
|
||||
[sessionDict setObject: endSessionEndpoint forKey: @"end_session_endpoint"];
|
||||
[sessionDict setObject: revocationEndpoint forKey: @"revocation_endpoint"];
|
||||
|
||||
jsonSession = [sessionDict jsonRepresentation];
|
||||
[cache setOpenIdSession: jsonSession
|
||||
forServer: self->openIdConfigUrl];
|
||||
}
|
||||
|
||||
|
||||
- (BOOL) _loadUserFromCache: (NSString *) email
|
||||
{
|
||||
/*
|
||||
Return NO if the user token must be check again, YES the user is valid for now
|
||||
*/
|
||||
SOGoCache *cache;
|
||||
NSString *identifier, *nextCheck;
|
||||
NSTimeInterval nextCheckTime, timeNow;
|
||||
|
||||
identifier = [self->openIdClient stringByAppendingString: email];
|
||||
|
||||
cache = [SOGoCache sharedCache];
|
||||
nextCheck = [cache userOpendIdSessionFromServer: identifier];
|
||||
if ([nextCheck length])
|
||||
{
|
||||
timeNow = [[NSDate date] timeIntervalSince1970];
|
||||
nextCheckTime = [nextCheck doubleValue];
|
||||
if(timeNow > nextCheckTime)
|
||||
return NO;
|
||||
else
|
||||
return YES;
|
||||
}
|
||||
else
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void) _saveUserToCache: (NSString *) email
|
||||
{
|
||||
SOGoCache *cache;
|
||||
NSString *identifier, *nextCheck;
|
||||
NSTimeInterval nextCheckTime;
|
||||
|
||||
identifier = [self->openIdClient stringByAppendingString: email];
|
||||
nextCheckTime = [[NSDate date] timeIntervalSince1970] + self->userTokenInterval;
|
||||
nextCheck = [NSString stringWithFormat:@"%f", nextCheckTime];
|
||||
|
||||
cache = [SOGoCache sharedCache];
|
||||
[cache setUserOpendIdSessionFromServer: identifier
|
||||
nextCheckAfter: nextCheck];
|
||||
}
|
||||
|
||||
- (NSString*) loginUrl: (NSString *) oldLocation
|
||||
{
|
||||
NSString* logUrl;
|
||||
logUrl = [self->authorizationEndpoint stringByAppendingFormat: @"?scope=%@", self->openIdScope];
|
||||
logUrl = [logUrl stringByAppendingString: @"&response_type=code"];
|
||||
logUrl = [logUrl stringByAppendingFormat: @"&client_id=%@", self->openIdClient];
|
||||
logUrl = [logUrl stringByAppendingFormat: @"&redirect_uri=%@", oldLocation];
|
||||
// logurl = [self->logurl stringByAppendingFormat: @"&state=%@", state];
|
||||
|
||||
return logUrl;
|
||||
}
|
||||
|
||||
- (NSString *) logoutUrl
|
||||
{
|
||||
return self->endSessionEndpoint;
|
||||
}
|
||||
|
||||
- (NSString*) getRefreshToken
|
||||
{
|
||||
return self->refreshToken;
|
||||
}
|
||||
|
||||
- (NSString*) getToken
|
||||
{
|
||||
return self->accessToken;
|
||||
}
|
||||
|
||||
- (void) setAccessToken: (NSString* ) token
|
||||
{
|
||||
self->accessToken = token;
|
||||
}
|
||||
|
||||
|
||||
- (NSString *) getCurrentToken
|
||||
{
|
||||
NSString *currentToken;
|
||||
|
||||
//See if the current token has been refreshed
|
||||
currentToken = [[[GCSFolderManager defaultFolderManager] openIdFolder] getNewToken: self->accessToken];
|
||||
|
||||
if(currentToken)
|
||||
{
|
||||
//be sure the old token has been cleaned
|
||||
[[[GCSFolderManager defaultFolderManager] openIdFolder] deleteOpenIdSessionFor: self->accessToken];
|
||||
return currentToken;
|
||||
}
|
||||
return self->accessToken;
|
||||
}
|
||||
|
||||
- (NSMutableDictionary *) fetchToken: (NSString * ) code redirect: (NSString *) oldLocation
|
||||
{
|
||||
NSString *location, *form, *content;
|
||||
WOResponse *response;
|
||||
NSUInteger status;
|
||||
NSMutableDictionary *result;
|
||||
NSDictionary *headers;
|
||||
NSDictionary *tokenRet;
|
||||
NSURL *url;
|
||||
|
||||
result = [NSMutableDictionary dictionary];
|
||||
[result setObject: @"ok" forKey: @"error"];
|
||||
|
||||
location = self->tokenEndpoint;
|
||||
url = [NSURL URLWithString: location];
|
||||
if (url)
|
||||
{
|
||||
form = @"grant_type=authorization_code";
|
||||
form = [form stringByAppendingFormat: @"&code=%@", code];
|
||||
form = [form stringByAppendingFormat: @"&redirect_uri=%@", [oldLocation stringByEscapingURL]];
|
||||
form = [form stringByAppendingFormat: @"&client_secret=%@", self->openIdClientSecret];
|
||||
form = [form stringByAppendingFormat: @"&client_id=%@", self->openIdClient];
|
||||
|
||||
headers = [NSDictionary dictionaryWithObject: @"application/x-www-form-urlencoded" forKey: @"content-type"];
|
||||
|
||||
response = [self _performOpenIdRequest: location
|
||||
method: @"POST"
|
||||
headers: headers
|
||||
body: [form dataUsingEncoding:NSUTF8StringEncoding]];
|
||||
|
||||
if (response)
|
||||
{
|
||||
status = [response status];
|
||||
if(status >= 200 && status <300)
|
||||
{
|
||||
content = [response contentString];
|
||||
tokenRet = [content objectFromJSONString];
|
||||
if (SOGoOpenIDDebugEnabled)
|
||||
{
|
||||
NSLog(@"fetch token response: %@", tokenRet);
|
||||
}
|
||||
self->accessToken = [tokenRet objectForKey: @"access_token"];
|
||||
self->refreshToken = [tokenRet objectForKey: @"refresh_token"];
|
||||
self->refreshExpiresIn = [tokenRet objectForKey: @"refresh_expires_in"];
|
||||
self->idToken = [tokenRet objectForKey: @"id_token"];
|
||||
self->tokenType = [tokenRet objectForKey: @"token_type"];
|
||||
self->expiresIn = [tokenRet objectForKey: @"expires_in"];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self logWithFormat: @"Error during fetching the token (status %d), response: %@", status, response];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
[result setObject: @"http-error" forKey: @"error"];
|
||||
}
|
||||
}
|
||||
else
|
||||
[result setObject: @"invalid-url" forKey: @"error"];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (NSMutableDictionary *) refreshToken: (NSString * ) userRefreshToken
|
||||
{
|
||||
NSString *location, *form, *content;
|
||||
WOResponse *response;
|
||||
NSUInteger status;
|
||||
NSMutableDictionary *result;
|
||||
NSDictionary *headers;
|
||||
NSDictionary *refreshTokenRet;
|
||||
NSURL *url;
|
||||
|
||||
result = [NSMutableDictionary dictionary];
|
||||
[result setObject: @"ok" forKey: @"error"];
|
||||
|
||||
if(!userRefreshToken || [userRefreshToken length] == 0)
|
||||
{
|
||||
[result setObject: @"invalid-token" forKey: @"error"];
|
||||
return result;
|
||||
}
|
||||
|
||||
location = self->tokenEndpoint;
|
||||
url = [NSURL URLWithString: location];
|
||||
if (url)
|
||||
{
|
||||
form = @"grant_type=refresh_token";
|
||||
form = [form stringByAppendingFormat: @"&scope=%@", self->openIdScope];
|
||||
form = [form stringByAppendingFormat: @"&refresh_token=%@", userRefreshToken];
|
||||
form = [form stringByAppendingFormat: @"&client_secret=%@", self->openIdClientSecret];
|
||||
form = [form stringByAppendingFormat: @"&client_id=%@", self->openIdClient];
|
||||
|
||||
headers = [NSDictionary dictionaryWithObject: @"application/x-www-form-urlencoded" forKey: @"content-type"];
|
||||
|
||||
response = [self _performOpenIdRequest: location
|
||||
method: @"POST"
|
||||
headers: headers
|
||||
body: [form dataUsingEncoding:NSUTF8StringEncoding]];
|
||||
|
||||
if (response)
|
||||
{
|
||||
status = [response status];
|
||||
if(status >= 200 && status <300)
|
||||
{
|
||||
content = [response contentString];
|
||||
refreshTokenRet = [content objectFromJSONString];
|
||||
if (SOGoOpenIDDebugEnabled)
|
||||
{
|
||||
NSLog(@"refresh token response: %@", refreshTokenRet);
|
||||
}
|
||||
self->accessToken = [refreshTokenRet objectForKey: @"access_token"];
|
||||
self->refreshToken = [refreshTokenRet objectForKey: @"refresh_token"];
|
||||
self->refreshExpiresIn = [refreshTokenRet objectForKey: @"refresh_expires_in"];
|
||||
self->tokenType = [refreshTokenRet objectForKey: @"token_type"];
|
||||
self->expiresIn = [refreshTokenRet objectForKey: @"expires_in"];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self logWithFormat: @"Error during refreshing the token (status %d), response: %@", status, response];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
[result setObject: @"http-error" forKey: @"error"];
|
||||
}
|
||||
}
|
||||
else
|
||||
[result setObject: @"invalid-url" forKey: @"error"];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (NSMutableDictionary *) fetchUserInfo
|
||||
{
|
||||
NSString *location, *auth, *content;
|
||||
WOResponse *response;
|
||||
NSUInteger status;
|
||||
NSMutableDictionary *result;
|
||||
NSDictionary *profile, *headers;
|
||||
NSURL *url;
|
||||
NSString *email;
|
||||
|
||||
result = [NSMutableDictionary dictionary];
|
||||
[result setObject: @"ok" forKey: @"error"];
|
||||
|
||||
location = self->userinfoEndpoint;
|
||||
url = [NSURL URLWithString: location];
|
||||
if (url)
|
||||
{
|
||||
auth = [NSString stringWithFormat: @"Bearer %@", self->accessToken];
|
||||
headers = [NSDictionary dictionaryWithObject: auth forKey: @"authorization"];
|
||||
|
||||
response = [self _performOpenIdRequest: location
|
||||
method: @"GET"
|
||||
headers: headers
|
||||
body: nil];
|
||||
if (response)
|
||||
{
|
||||
status = [response status];
|
||||
if(status >= 200 && status <300)
|
||||
{
|
||||
content = [response contentString];
|
||||
profile = [content objectFromJSONString];
|
||||
if(SOGoOpenIDDebugEnabled && profile)
|
||||
NSLog(@"OpenId fetch user info, profile is %@", profile);
|
||||
|
||||
/*profile = {"sub":"70a3e6a1-37cf-4cf6-b114-6973aabca86a",
|
||||
"email_verified":false,
|
||||
"address":{},
|
||||
"name":"Foo Bar",
|
||||
"preferred_username":"myuser",
|
||||
"given_name":"Foo",
|
||||
"family_name":"Bar",
|
||||
"email":"myuser@user.com"}*/
|
||||
if (email = [profile objectForKey: self->openIdEmailParam])
|
||||
{
|
||||
if(self->userTokenInterval > 0)
|
||||
[self _saveUserToCache: email];
|
||||
[result setObject: email forKey: @"login"];
|
||||
}
|
||||
else
|
||||
[result setObject: @"no mail found" forKey: @"error"];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self logWithFormat: @"Error during fetching the token (status %d), response: %@", status, response];
|
||||
[result setObject: @"http-error" forKey: @"error"];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
[result setObject: @"http-error" forKey: @"error"];
|
||||
}
|
||||
}
|
||||
else
|
||||
[result setObject: @"invalid-url" forKey: @"error"];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
+ (void) deleteValueForSessionKey: (NSString *) theSessionKey
|
||||
{
|
||||
GCSOpenIdFolder *folder;
|
||||
|
||||
folder = [[GCSFolderManager defaultFolderManager] openIdFolder];
|
||||
[folder deleteOpenIdSessionFor: theSessionKey];
|
||||
}
|
||||
|
||||
- (NSString *) _login
|
||||
{
|
||||
NSMutableDictionary *resultUserInfo, *resultResfreh;
|
||||
NSString* _oldAccessToken;
|
||||
|
||||
resultUserInfo = [self fetchUserInfo];
|
||||
if ([[resultUserInfo objectForKey: @"error"] isEqualToString: @"ok"])
|
||||
{
|
||||
//Save some infos as the refresh token to the database
|
||||
[[[GCSFolderManager defaultFolderManager] openIdFolder] writeOpenIdSession: self->accessToken
|
||||
withOldSession: nil
|
||||
withRefreshToken: self->refreshToken
|
||||
withExpire: self->expiresIn
|
||||
withRefreshExpire: self->refreshExpiresIn];
|
||||
return [resultUserInfo objectForKey: @"login"];
|
||||
}
|
||||
else if(openIdEnableRefreshToken)
|
||||
{
|
||||
//try to refresh
|
||||
if(self->accessToken)
|
||||
{
|
||||
self->refreshToken = [[[GCSFolderManager defaultFolderManager] openIdFolder] getRefreshToken: self->accessToken];
|
||||
//remove old session
|
||||
[[[GCSFolderManager defaultFolderManager] openIdFolder] deleteOpenIdSessionFor: self->accessToken];
|
||||
}
|
||||
if(self->refreshToken)
|
||||
{
|
||||
_oldAccessToken = self->accessToken;
|
||||
resultResfreh = [self refreshToken: self->refreshToken];
|
||||
if ([[resultResfreh objectForKey: @"error"] isEqualToString: @"ok"])
|
||||
{
|
||||
resultUserInfo = [self fetchUserInfo];
|
||||
if ([[resultUserInfo objectForKey: @"error"] isEqualToString: @"ok"])
|
||||
{
|
||||
[[[GCSFolderManager defaultFolderManager] openIdFolder] writeOpenIdSession: self->accessToken
|
||||
withOldSession: _oldAccessToken
|
||||
withRefreshToken: self->refreshToken
|
||||
withExpire: self->expiresIn
|
||||
withRefreshExpire: self->refreshExpiresIn];
|
||||
return [resultUserInfo objectForKey: @"login"];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//The acces token hasn't work, delete the session in database if needed
|
||||
if(self->accessToken)
|
||||
{
|
||||
[[[GCSFolderManager defaultFolderManager] openIdFolder] deleteOpenIdSessionFor: self->accessToken];
|
||||
}
|
||||
|
||||
[self errorWithFormat: @"Can't get user email from profile because: %@", [resultUserInfo objectForKey: @"error"]];
|
||||
}
|
||||
else
|
||||
{
|
||||
//remove old session
|
||||
[[[GCSFolderManager defaultFolderManager] openIdFolder] deleteOpenIdSessionFor: self->accessToken];
|
||||
}
|
||||
|
||||
return @"anonymous";
|
||||
}
|
||||
|
||||
- (BOOL) login: (NSString *) email
|
||||
{
|
||||
//Check if we need to fetch userinfo
|
||||
if(self->userTokenInterval > 0 && [self _loadUserFromCache: email])
|
||||
{
|
||||
return email;
|
||||
}
|
||||
else
|
||||
{
|
||||
return [self _login];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@end
|
@ -77,6 +77,7 @@ static const NSString *kDisableSharingCalendar = @"Calendar";
|
||||
|
||||
- (BOOL) uixDebugEnabled;
|
||||
- (BOOL) easDebugEnabled;
|
||||
- (BOOL) openIdDebugEnabled;
|
||||
- (BOOL) tnefDecoderDebugEnabled;
|
||||
- (BOOL) xsrfValidationEnabled;
|
||||
|
||||
@ -89,11 +90,21 @@ NSComparisonResult languageSort(id el1, id el2, void *context);
|
||||
- (NSString *) loginSuffix;
|
||||
|
||||
- (NSString *) authenticationType;
|
||||
- (BOOL) isSsoUsed;
|
||||
- (NSString *) davAuthenticationType;
|
||||
|
||||
- (NSString *) CASServiceURL;
|
||||
- (BOOL) CASLogoutEnabled;
|
||||
|
||||
- (NSString *) openIdConfigUrl;
|
||||
- (NSString *) openIdScope;
|
||||
- (NSString *) openIdClient;
|
||||
- (NSString *) openIdClientSecret;
|
||||
- (NSString *) openIdEmailParam;
|
||||
- (BOOL) openIdEnableRefreshToken;
|
||||
- (BOOL) openIdLogoutEnabled;
|
||||
- (int) openIdTokenCheckInterval;
|
||||
|
||||
- (NSString *) SAML2PrivateKeyLocation;
|
||||
- (NSString *) SAML2CertificateLocation;
|
||||
- (NSString *) SAML2IdpMetadataLocation;
|
||||
|
@ -512,6 +512,11 @@ _injectConfigurationFromFile (NSMutableDictionary *defaultsDict,
|
||||
return [self boolForKey: @"SOGoEASDebugEnabled"];
|
||||
}
|
||||
|
||||
- (BOOL) openIdDebugEnabled
|
||||
{
|
||||
return [self boolForKey: @"SOGoOpenIDDebugEnabled"];
|
||||
}
|
||||
|
||||
- (BOOL) tnefDecoderDebugEnabled
|
||||
{
|
||||
return [self boolForKey: @"SOGoTnefDecoderDebugEnabled"];
|
||||
@ -582,6 +587,14 @@ NSComparisonResult languageSort(id el1, id el2, void *context)
|
||||
return [[self stringForKey: @"SOGoAuthenticationType"] lowercaseString];
|
||||
}
|
||||
|
||||
- (BOOL) isSsoUsed
|
||||
{
|
||||
NSString* authType;
|
||||
authType = [self authenticationType];
|
||||
|
||||
return ([authType isEqualToString: @"cas"] || [authType isEqualToString: @"saml2"] || [authType isEqualToString: @"openid"]);
|
||||
}
|
||||
|
||||
- (NSString *) davAuthenticationType
|
||||
{
|
||||
return [[self stringForKey: @"SOGoDAVAuthenticationType"] lowercaseString];
|
||||
@ -597,6 +610,61 @@ NSComparisonResult languageSort(id el1, id el2, void *context)
|
||||
return [self boolForKey: @"SOGoCASLogoutEnabled"];
|
||||
}
|
||||
|
||||
/* OpenId Support */
|
||||
- (NSString *) openIdConfigUrl
|
||||
{
|
||||
return [self stringForKey: @"SOGoOpenIdConfigUrl"];
|
||||
}
|
||||
|
||||
- (NSString *) openIdScope
|
||||
{
|
||||
return [self stringForKey: @"SOGoOpenIdScope"];
|
||||
}
|
||||
|
||||
- (NSString *) openIdClient
|
||||
{
|
||||
return [self stringForKey: @"SOGoOpenIdClient"];
|
||||
}
|
||||
|
||||
- (NSString *) openIdClientSecret
|
||||
{
|
||||
return [self stringForKey: @"SOGoOpenIdClientSecret"];
|
||||
}
|
||||
|
||||
- (NSString *) openIdEmailParam
|
||||
{
|
||||
NSString *emailParam;
|
||||
emailParam = [self stringForKey: @"SOGoOpenIdEmailParam"];
|
||||
if(!emailParam)
|
||||
emailParam = @"email";
|
||||
return emailParam;
|
||||
}
|
||||
|
||||
- (BOOL) openIdLogoutEnabled
|
||||
{
|
||||
return [self boolForKey: @"SOGoOpenIdLogoutEnabled"];
|
||||
}
|
||||
|
||||
- (int) openIdTokenCheckInterval
|
||||
{
|
||||
|
||||
int v;
|
||||
|
||||
v = [self integerForKey: @"SOGoOpenIdTokenCheckInterval"];
|
||||
|
||||
if (!v)
|
||||
v = 0;
|
||||
if(v<0)
|
||||
v = 0;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
- (BOOL) openIdEnableRefreshToken
|
||||
{
|
||||
return [self boolForKey: @"SOGoOpenIdEnableRefreshToken"];
|
||||
}
|
||||
|
||||
/* SAML2 support */
|
||||
- (NSString *) SAML2PrivateKeyLocation
|
||||
{
|
||||
|
@ -58,6 +58,8 @@
|
||||
andPassword: (NSString *) password
|
||||
inContext: (WOContext *) context;
|
||||
|
||||
- (NSArray *)getCookiesIfNeeded: (WOContext *)_ctx;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* _SOGOWEBAUTHENTICATOR_H_ */
|
||||
|
@ -35,6 +35,7 @@
|
||||
|
||||
#import "SOGoCache.h"
|
||||
#import "SOGoCASSession.h"
|
||||
#import "SOGoOpenIdSession.h"
|
||||
#import "SOGoPermissions.h"
|
||||
#import "SOGoSession.h"
|
||||
#import "SOGoSystemDefaults.h"
|
||||
@ -133,7 +134,8 @@
|
||||
additionalInfo: (NSMutableDictionary **)_additionalInfo
|
||||
useCache: (BOOL) _useCache
|
||||
{
|
||||
SOGoCASSession *session;
|
||||
SOGoCASSession *casSession;
|
||||
SOGoOpenIdSession * openIdSession;
|
||||
SOGoSystemDefaults *sd;
|
||||
NSString *authenticationType;
|
||||
BOOL rc;
|
||||
@ -143,12 +145,20 @@
|
||||
authenticationType = [sd authenticationType];
|
||||
if ([authenticationType isEqualToString: @"cas"])
|
||||
{
|
||||
session = [SOGoCASSession CASSessionWithIdentifier: _pwd fromProxy: NO];
|
||||
if (session)
|
||||
rc = [[session login] isEqualToString: _login];
|
||||
casSession = [SOGoCASSession CASSessionWithIdentifier: _pwd fromProxy: NO];
|
||||
if (casSession)
|
||||
rc = [[casSession login] isEqualToString: _login];
|
||||
else
|
||||
rc = NO;
|
||||
}
|
||||
else if ([authenticationType isEqualToString: @"openid"])
|
||||
{
|
||||
openIdSession = [SOGoOpenIdSession OpenIdSessionWithToken: _pwd];
|
||||
if (openIdSession)
|
||||
rc = [[openIdSession login: _login] isEqualToString: _login];
|
||||
else
|
||||
rc = NO;
|
||||
}
|
||||
#if defined(SAML2_CONFIG)
|
||||
else if ([authenticationType isEqualToString: @"saml2"])
|
||||
{
|
||||
@ -310,6 +320,15 @@
|
||||
if ([password length] || renew)
|
||||
[session updateCache];
|
||||
}
|
||||
else if ([authType isEqualToString: @"openid"])
|
||||
{
|
||||
SOGoOpenIdSession* session;
|
||||
NSString* currentToken;
|
||||
//If the token has been refresh during the request, we need to use the new access_token
|
||||
//as the one from the cookie is no more valid
|
||||
session = [SOGoOpenIdSession OpenIdSessionWithToken: password];
|
||||
password = [session getCurrentToken];
|
||||
}
|
||||
#if defined(SAML2_CONFIG)
|
||||
else if ([authType isEqualToString: @"saml2"])
|
||||
{
|
||||
@ -436,4 +455,42 @@
|
||||
return authCookie;
|
||||
}
|
||||
|
||||
- (NSArray *) getCookiesIfNeeded: (WOContext *)_ctx
|
||||
{
|
||||
NSArray *listCookies = nil;
|
||||
SOGoSystemDefaults *sd;
|
||||
NSString *authType;
|
||||
|
||||
sd = [SOGoSystemDefaults sharedSystemDefaults];
|
||||
authType = [sd authenticationType];
|
||||
if([authType isEqualToString:@"openid"] && [sd openIdEnableRefreshToken])
|
||||
{
|
||||
NSString *currentPassword, *newPassword, *username;
|
||||
SOGoOpenIdSession *openIdSession;
|
||||
|
||||
WOCookie* newCookie;
|
||||
currentPassword = [self passwordInContext: _ctx];
|
||||
newPassword = [self imapPasswordInContext: _ctx forURL: nil forceRenew: NO];
|
||||
if(currentPassword && newPassword && ![newPassword isEqualToString: currentPassword])
|
||||
{
|
||||
openIdSession = [SOGoOpenIdSession OpenIdSessionWithToken: newPassword];
|
||||
if (openIdSession)
|
||||
username = [openIdSession login: @""]; //Force to refresh the name
|
||||
else
|
||||
username = [[self userInContext: _ctx] login];
|
||||
newCookie = [self cookieWithUsername: username
|
||||
andPassword: newPassword
|
||||
inContext: _ctx];
|
||||
listCookies = [[NSArray alloc] initWithObjects: newCookie, nil];
|
||||
[listCookies autorelease];
|
||||
}
|
||||
if(listCookies && [listCookies isKindOfClass:[NSArray class]] && [listCookies count] > 0)
|
||||
return listCookies;
|
||||
else
|
||||
return nil;
|
||||
}
|
||||
else
|
||||
return nil;
|
||||
}
|
||||
|
||||
@end /* SOGoWebAuthenticator */
|
||||
|
@ -33,6 +33,7 @@
|
||||
|
||||
#import <SOGo/NSString+Utilities.h>
|
||||
#import <SOGo/SOGoSession.h>
|
||||
#import <SOGo/SOGoOpenIdSession.h>
|
||||
|
||||
#import "SOGoTool.h"
|
||||
|
||||
@ -59,6 +60,73 @@
|
||||
"The expire-sessions action should be configured as a cronjob.\n");
|
||||
}
|
||||
|
||||
- (BOOL) expireUserOpenIdSessionOlderThan: (int) nbMinutes
|
||||
{
|
||||
BOOL rc;
|
||||
EOAdaptorChannel *channel;
|
||||
GCSChannelManager *cm;
|
||||
NSArray *attrs;
|
||||
NSDictionary *qresult;
|
||||
NSException *ex;
|
||||
NSString *sql, *sessionsFolderURL, *sessionID;
|
||||
NSURL *tableURL;
|
||||
NSUserDefaults *ud;
|
||||
|
||||
unsigned int now, oldest;
|
||||
|
||||
rc = YES;
|
||||
ud = [NSUserDefaults standardUserDefaults];
|
||||
now = [[NSCalendarDate calendarDate] timeIntervalSince1970];
|
||||
oldest = now - (nbMinutes * 60);
|
||||
sessionID = nil;
|
||||
|
||||
sessionsFolderURL = [ud stringForKey: @"OCSOpenIdURL"];
|
||||
if (!sessionsFolderURL)
|
||||
{
|
||||
if (verbose)
|
||||
NSLog(@"Couldn't read OCSOpenIdURL");
|
||||
return rc = NO;
|
||||
}
|
||||
|
||||
tableURL = [[NSURL alloc] initWithString: sessionsFolderURL];
|
||||
cm = [GCSChannelManager defaultChannelManager];
|
||||
channel = [cm acquireOpenChannelForURL: tableURL];
|
||||
if (!channel)
|
||||
{
|
||||
/* FIXME: nice error msg */
|
||||
NSLog(@"Can't aquire channel");
|
||||
return rc = NO;
|
||||
}
|
||||
|
||||
sql = [NSString stringWithFormat: @"SELECT c_user_session FROM %@ WHERE c_access_token_expires_in <= %d",
|
||||
[tableURL gcsTableName], oldest];
|
||||
ex = [channel evaluateExpressionX: sql];
|
||||
if (ex)
|
||||
{
|
||||
NSLog(@"%@", [ex reason]);
|
||||
[ex raise];
|
||||
return rc = NO;
|
||||
}
|
||||
|
||||
attrs = [channel describeResults: NO];
|
||||
while ((qresult = [channel fetchAttributes: attrs withZone: NULL]))
|
||||
{
|
||||
sessionID = [qresult objectForKey: @"c_user_session"];
|
||||
if (sessionID)
|
||||
{
|
||||
if (verbose)
|
||||
NSLog(@"Removing session %@", sessionID);
|
||||
[SOGoOpenIdSession deleteValueForSessionKey: sessionID];
|
||||
}
|
||||
}
|
||||
[cm releaseChannel: channel immediately: YES];
|
||||
|
||||
if (verbose && sessionID == nil)
|
||||
NSLog(@"No session to remove on openId");
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
- (BOOL) expireUserSessionOlderThan: (int) nbMinutes
|
||||
{
|
||||
BOOL rc;
|
||||
@ -67,7 +135,7 @@
|
||||
NSArray *attrs;
|
||||
NSDictionary *qresult;
|
||||
NSException *ex;
|
||||
NSString *sql, *sessionsFolderURL, *sessionID;
|
||||
NSString *sql, *sessionsFolderURL, *sessionID, *authType;
|
||||
NSURL *tableURL;
|
||||
NSUserDefaults *ud;
|
||||
|
||||
@ -123,6 +191,13 @@
|
||||
if (verbose && sessionID == nil)
|
||||
NSLog(@"No session to remove");
|
||||
|
||||
//doing openid session if needed
|
||||
authType = [ud stringForKey:@"SOGoAuthenticationType"];
|
||||
if([authType isEqualToString: @"openid"])
|
||||
{
|
||||
[self expireUserOpenIdSessionOlderThan: nbMinutes];
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -497,6 +497,15 @@
|
||||
return [[sd authenticationType] isEqualToString: @"cas"];
|
||||
}
|
||||
|
||||
- (BOOL) usesOpenIdAuthentication
|
||||
{
|
||||
SOGoSystemDefaults *sd;
|
||||
|
||||
sd = [SOGoSystemDefaults sharedSystemDefaults];
|
||||
|
||||
return [[sd authenticationType] isEqualToString: @"openid"];
|
||||
}
|
||||
|
||||
- (BOOL) usesSAML2Authenticationx
|
||||
{
|
||||
SOGoSystemDefaults *sd;
|
||||
@ -545,11 +554,13 @@
|
||||
sd = [SOGoSystemDefaults sharedSystemDefaults];
|
||||
authType = [sd authenticationType];
|
||||
if ([authType isEqualToString: @"cas"])
|
||||
canLogoff = [sd CASLogoutEnabled];
|
||||
canLogoff = [sd CASLogoutEnabled];
|
||||
else if ([authType isEqualToString: @"saml2"])
|
||||
canLogoff = [sd SAML2LogoutEnabled];
|
||||
canLogoff = [sd SAML2LogoutEnabled];
|
||||
else if ([authType isEqualToString: @"openid"])
|
||||
canLogoff = [sd openIdLogoutEnabled];
|
||||
else
|
||||
canLogoff = [[auth cookieNameInContext: context] length] > 0;
|
||||
canLogoff = [[auth cookieNameInContext: context] length] > 0;
|
||||
}
|
||||
else
|
||||
canLogoff = NO;
|
||||
|
@ -40,6 +40,7 @@
|
||||
#import <SOGo/SOGoBuild.h>
|
||||
#import <SOGo/SOGoCache.h>
|
||||
#import <SOGo/SOGoCASSession.h>
|
||||
#import <SOGo/SOGoOpenIdSession.h>
|
||||
#if defined(SAML2_CONFIG)
|
||||
#import <SOGo/SOGoSAML2Session.h>
|
||||
#endif /* SAML2_ENABLE */
|
||||
@ -478,42 +479,42 @@ static const NSString *kJwtKey = @"jwt";
|
||||
if ([login isEqualToString: @"anonymous"])
|
||||
login = nil;
|
||||
if (!login)
|
||||
{
|
||||
rq = [context request];
|
||||
ticket = [rq formValueForKey: @"ticket"];
|
||||
if ([ticket length])
|
||||
{
|
||||
rq = [context request];
|
||||
ticket = [rq formValueForKey: @"ticket"];
|
||||
if ([ticket length])
|
||||
{
|
||||
casSession = [SOGoCASSession CASSessionWithTicket: ticket
|
||||
fromProxy: NO];
|
||||
login = [casSession login];
|
||||
if ([login length])
|
||||
{
|
||||
auth = [[WOApplication application]
|
||||
authenticatorInContext: context];
|
||||
casCookie = [auth cookieWithUsername: login
|
||||
andPassword: [casSession identifier]
|
||||
inContext: context];
|
||||
[casSession updateCache];
|
||||
newLocation = [rq cookieValueForKey: @"cas-location"];
|
||||
/* login callback, we expire the "cas-location" cookie, created
|
||||
below */
|
||||
casLocationCookie = [self _authLocationCookie: YES
|
||||
withName: @"cas-location"];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* anonymous and no ticket, possibly a logout request from CAS
|
||||
* See: https://wiki.jasig.org/display/CASUM/Single+Sign+Out
|
||||
*/
|
||||
logoutRequest = [rq formValueForKey: @"logoutRequest"];
|
||||
if ([logoutRequest length])
|
||||
{
|
||||
[SOGoCASSession handleLogoutRequest: logoutRequest];
|
||||
return [self responseWithStatus: 200];
|
||||
}
|
||||
}
|
||||
casSession = [SOGoCASSession CASSessionWithTicket: ticket
|
||||
fromProxy: NO];
|
||||
login = [casSession login];
|
||||
if ([login length])
|
||||
{
|
||||
auth = [[WOApplication application]
|
||||
authenticatorInContext: context];
|
||||
casCookie = [auth cookieWithUsername: login
|
||||
andPassword: [casSession identifier]
|
||||
inContext: context];
|
||||
[casSession updateCache];
|
||||
newLocation = [rq cookieValueForKey: @"cas-location"];
|
||||
/* login callback, we expire the "cas-location" cookie, created
|
||||
below */
|
||||
casLocationCookie = [self _authLocationCookie: YES
|
||||
withName: @"cas-location"];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* anonymous and no ticket, possibly a logout request from CAS
|
||||
* See: https://wiki.jasig.org/display/CASUM/Single+Sign+Out
|
||||
*/
|
||||
logoutRequest = [rq formValueForKey: @"logoutRequest"];
|
||||
if ([logoutRequest length])
|
||||
{
|
||||
[SOGoCASSession handleLogoutRequest: logoutRequest];
|
||||
return [self responseWithStatus: 200];
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
ticket = nil;
|
||||
|
||||
@ -548,6 +549,115 @@ static const NSString *kJwtKey = @"jwt";
|
||||
return response;
|
||||
}
|
||||
|
||||
- (id <WOActionResults>) _openidDefaultAction
|
||||
{
|
||||
WOResponse *response;
|
||||
NSString *login, *redirectLocation, *serverUrl;
|
||||
NSString *sessionState, *code, *refreshTokenValue;
|
||||
NSURL *newLocation, *oldLocation;
|
||||
NSDictionary *formValues;
|
||||
SOGoUser *loggedInUser;
|
||||
WOCookie *openIdCookie, *openIdCookieLocation, *openIdRefreshCookie;
|
||||
WORequest *rq;
|
||||
SOGoWebAuthenticator *auth;
|
||||
SOGoOpenIdSession *openIdSession;
|
||||
id value;
|
||||
|
||||
openIdCookie = nil;
|
||||
openIdCookieLocation = nil;
|
||||
openIdRefreshCookie = nil;
|
||||
newLocation = nil;
|
||||
|
||||
openIdSession = [SOGoOpenIdSession OpenIdSession];
|
||||
|
||||
if(![openIdSession sessionIsOk])
|
||||
{
|
||||
return [NSException exceptionWithHTTPStatus: 502 /* Bad OpenId Configuration */
|
||||
reason: [NSString stringWithFormat: @"OpenId server not found or has unexpected behavior, contact your admin."]];
|
||||
}
|
||||
|
||||
login = [[context activeUser] login];
|
||||
rq = [context request];
|
||||
if ([login isEqualToString: @"anonymous"])
|
||||
login = nil;
|
||||
if (!login)
|
||||
{
|
||||
//You get here if you nerver been logged in or if you token is expired
|
||||
serverUrl = [[context serverURL] absoluteString];
|
||||
redirectLocation = [NSString stringWithFormat: @"%@/%@/", serverUrl, [rq applicationName]];
|
||||
NSLog(@"ServerUrl %@ and redirect: %@", serverUrl, redirectLocation);
|
||||
if((formValues = [rq formValues]) && [formValues objectForKey: @"code"])
|
||||
{
|
||||
//You get here if this is the callback of openid after you logged in
|
||||
|
||||
//NOT MANDATORY
|
||||
// value = [formValues objectForKey: @"session_state"];
|
||||
// if ([value isKindOfClass: [NSArray class]])
|
||||
// sessionState = [value lastObject];
|
||||
// else
|
||||
// sessionState = value;
|
||||
value = [formValues objectForKey: @"code"];
|
||||
if ([value isKindOfClass: [NSArray class]])
|
||||
code = [value lastObject];
|
||||
else
|
||||
code = value;
|
||||
[openIdSession fetchToken: code redirect: redirectLocation];
|
||||
login = [openIdSession login: @""];
|
||||
if ([login length])
|
||||
{
|
||||
auth = [[WOApplication application] authenticatorInContext: context];
|
||||
openIdCookie = [auth cookieWithUsername: login
|
||||
andPassword: [openIdSession getToken]
|
||||
inContext: context];
|
||||
}
|
||||
newLocation = [rq cookieValueForKey: @"openid-location"];
|
||||
openIdCookieLocation = [self _authLocationCookie: YES withName: @"openid-location"];
|
||||
}
|
||||
// else if((formValues = [rq formValues]) && [formValues objectForKey: @"action"])
|
||||
// {
|
||||
// value = [formValues objectForKey: @"action"];
|
||||
// if ([value isKindOfClass: [NSArray class]])
|
||||
// code = [value lastObject];
|
||||
// else
|
||||
// code = value;
|
||||
// if([code isEqualToString:@"redirect"])
|
||||
// {
|
||||
// //this action onlye serve to make a redirection to openId server after a GET request
|
||||
// newLocation = [openIdSession loginUrl: redirectLocation];
|
||||
// }
|
||||
//}
|
||||
else
|
||||
{
|
||||
// //You get here the first time you access sogo, it redirect to last location
|
||||
// if([[rq method] isEqualToString: @"POST"])
|
||||
// //To avoid making a redirection to openid server after a post request, we first redirect to a get method
|
||||
// newLocation = [NSString stringWithFormat: @"%@?action=redirect", redirectLocation];
|
||||
// else
|
||||
newLocation = [openIdSession loginUrl: redirectLocation];
|
||||
openIdCookieLocation = [self _authLocationCookie: NO withName: @"openid-location"];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!newLocation)
|
||||
{
|
||||
oldLocation = [[self clientObject] baseURLInContext: context];
|
||||
newLocation = [NSString stringWithFormat: @"%@%@", oldLocation, [login stringByEscapingURL]];
|
||||
}
|
||||
|
||||
loggedInUser = [SOGoUser userWithLogin: login];
|
||||
[self _checkAutoReloadWebCalendars: loggedInUser];
|
||||
}
|
||||
|
||||
response = [self redirectToLocation: newLocation];
|
||||
if (openIdCookie)
|
||||
[response addCookie: openIdCookie];
|
||||
if (openIdCookieLocation)
|
||||
[response addCookie: openIdCookieLocation];
|
||||
//[response setStatus: 303];
|
||||
return response;
|
||||
}
|
||||
|
||||
#if defined(SAML2_CONFIG)
|
||||
- (id <WOActionResults>) _saml2DefaultAction
|
||||
{
|
||||
@ -615,16 +725,13 @@ static const NSString *kJwtKey = @"jwt";
|
||||
[[SOGoUser getEncryptedUsernameIfNeeded:login request: [context request]] stringByEscapingURL]]];
|
||||
}
|
||||
else
|
||||
{
|
||||
oldLocation = [[context request] uri];
|
||||
if ([context clientObject]
|
||||
&& ![oldLocation hasSuffix: @"/"]
|
||||
&& ![oldLocation hasSuffix: @"/view"])
|
||||
response = [self redirectToLocation:
|
||||
[NSString stringWithFormat: @"%@/", oldLocation]];
|
||||
else
|
||||
response = self;
|
||||
}
|
||||
{
|
||||
oldLocation = [[context request] uri];
|
||||
if ([context clientObject] && ![oldLocation hasSuffix: @"/"] && ![oldLocation hasSuffix: @"/view"])
|
||||
response = [self redirectToLocation: [NSString stringWithFormat: @"%@/", oldLocation]];
|
||||
else
|
||||
response = self;
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
@ -638,6 +745,8 @@ static const NSString *kJwtKey = @"jwt";
|
||||
authenticationType];
|
||||
if ([authenticationType isEqualToString: @"cas"])
|
||||
result = [self _casDefaultAction];
|
||||
else if ([authenticationType isEqualToString: @"openid"])
|
||||
result = [self _openidDefaultAction];
|
||||
#if defined(SAML2_CONFIG)
|
||||
else if ([authenticationType isEqualToString: @"saml2"])
|
||||
result = [self _saml2DefaultAction];
|
||||
|
@ -32,6 +32,7 @@
|
||||
|
||||
#import <SOGo/SOGoCache.h>
|
||||
#import <SOGo/SOGoCASSession.h>
|
||||
#import <SOGo/SOGoOpenIdSession.h>
|
||||
#if defined(SAML2_CONFIG)
|
||||
#import <SOGo/SOGoSAML2Session.h>
|
||||
#endif
|
||||
@ -429,6 +430,14 @@
|
||||
redirectURL = [SOGoCASSession CASURLWithAction: @"logout"
|
||||
andParameters: nil];
|
||||
}
|
||||
else if ([[sd authenticationType] isEqualToString: @"openid"])
|
||||
{
|
||||
SOGoOpenIdSession* session;
|
||||
session = [SOGoOpenIdSession OpenIdSession];
|
||||
redirectURL = [session logoutUrl];
|
||||
//delete openid session in database
|
||||
|
||||
}
|
||||
#if defined(SAML2_CONFIG)
|
||||
else if ([[sd authenticationType] isEqualToString: @"saml2"])
|
||||
{
|
||||
|
2
UI/WebServerResources/js/vendor/punycode.js
vendored
2
UI/WebServerResources/js/vendor/punycode.js
vendored
@ -422,7 +422,7 @@ const punycode = {
|
||||
* @memberOf punycode
|
||||
* @type String
|
||||
*/
|
||||
'version': '2.3.1',
|
||||
'version': '2.1.0',
|
||||
/**
|
||||
* An object of methods to convert from JavaScript's internal character
|
||||
* representation (UCS-2) to Unicode code points, and back.
|
||||
|
Loading…
x
Reference in New Issue
Block a user