/* 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 #import #import #import #import #import #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