2017-02-21 00:57:46 +01:00
|
|
|
/* SOGoToolManageACL.m - this file is part of SOGo
|
|
|
|
*
|
|
|
|
* Copyright (C) 2017 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/NSAutoreleasePool.h>
|
|
|
|
#import <Foundation/NSDictionary.h>
|
|
|
|
#import <Foundation/NSEnumerator.h>
|
|
|
|
#import <Foundation/NSString.h>
|
|
|
|
|
|
|
|
#import <GDLAccess/EOAdaptorChannel.h>
|
|
|
|
|
|
|
|
#import <GDLContentStore/EOQualifier+GCS.h>
|
|
|
|
#import <GDLContentStore/GCSChannelManager.h>
|
|
|
|
#import <GDLContentStore/GCSFolderManager.h>
|
|
|
|
#import <GDLContentStore/GCSFolder.h>
|
|
|
|
#import <GDLContentStore/NSURL+GCS.h>
|
|
|
|
|
|
|
|
#import <SOGo/SOGoCache.h>
|
|
|
|
#import <SOGo/SOGoUserManager.h>
|
|
|
|
#import <SOGo/NSArray+Utilities.h>
|
|
|
|
#import <SOGo/NSString+Utilities.h>
|
2017-05-17 15:44:34 +02:00
|
|
|
#import <SOGo/SOGoGCSFolder.h>
|
|
|
|
#import <SOGo/SOGoParentFolder.h>
|
2017-02-21 00:57:46 +01:00
|
|
|
#import <SOGo/SOGoUser.h>
|
2017-05-17 15:44:34 +02:00
|
|
|
#import <SOGo/SOGoUserFolder.h>
|
2017-02-21 00:57:46 +01:00
|
|
|
#import <SOGo/SOGoSystemDefaults.h>
|
|
|
|
|
|
|
|
#import <NGCards/iCalCalendar.h>
|
|
|
|
#import <NGCards/NGVCard.h>
|
|
|
|
|
2017-05-17 15:44:34 +02:00
|
|
|
#import <NGObjWeb/WOContext+SoObjects.h>
|
|
|
|
|
2017-02-21 00:57:46 +01:00
|
|
|
#import "SOGoTool.h"
|
|
|
|
|
|
|
|
typedef enum
|
|
|
|
{
|
|
|
|
ManageACLUnknown = -1,
|
|
|
|
ManageACLGet = 0,
|
|
|
|
ManageACLAdd = 1,
|
|
|
|
ManageACLRemove = 2,
|
2017-05-17 15:44:34 +02:00
|
|
|
ManageACLSubscribe = 3,
|
|
|
|
ManageACLUnsubscribe = 3,
|
2017-02-21 00:57:46 +01:00
|
|
|
} SOGoManageACLCommand;
|
|
|
|
|
|
|
|
@interface SOGoToolManageACL : SOGoTool
|
|
|
|
{
|
|
|
|
SOGoManageACLCommand command;
|
|
|
|
NSString *owner;
|
|
|
|
NSString *folder;
|
|
|
|
NSString *user;
|
|
|
|
NSArray *rights;
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation SOGoToolManageACL
|
|
|
|
|
|
|
|
+ (NSString *) command
|
|
|
|
{
|
|
|
|
return @"manage-acl";
|
|
|
|
}
|
|
|
|
|
|
|
|
+ (NSString *) description
|
|
|
|
{
|
|
|
|
return @"manage user's ACL";
|
|
|
|
}
|
|
|
|
|
|
|
|
- (id) init
|
|
|
|
{
|
|
|
|
if ((self = [super init]))
|
|
|
|
{
|
|
|
|
command = ManageACLUnknown;
|
|
|
|
owner = nil;
|
|
|
|
folder = nil;
|
|
|
|
user = nil;
|
|
|
|
rights = nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) dealloc
|
|
|
|
{
|
|
|
|
[owner release];
|
|
|
|
[folder release];
|
|
|
|
[user release];
|
|
|
|
[rights release];
|
|
|
|
[super dealloc];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) usage
|
|
|
|
{
|
2017-05-17 15:44:34 +02:00
|
|
|
fprintf (stderr, "manage-acl get|add|remove|subscribe|unsubscribe owner folder user <rights>\n\n"
|
|
|
|
" get get ACL information of folder for user\n"
|
|
|
|
" add add ACL information of folder for user\n"
|
|
|
|
" remove remove all ACL information of folder for user\n"
|
|
|
|
" subscribe subscribe user to owner's folder\n"
|
|
|
|
" unsubscribe unsubscribe user to owner's folder\n"
|
|
|
|
" owner the user owning the folder\n"
|
2017-09-12 11:39:47 +02:00
|
|
|
" folder the folder - Calendar/<ID> or Contacts/<ID>\n"
|
2017-05-17 15:44:34 +02:00
|
|
|
" user the user to get/set rights for - 'ALL', '<default>', 'anonymous' are supported\n"
|
|
|
|
" rights rights to add\n\n"
|
2017-02-22 17:00:01 +01:00
|
|
|
"Example: sogo-tool manage-acl get jdoe Calendar/personal\n\n"
|
2017-02-21 00:57:46 +01:00
|
|
|
"Note: You can add only one access right at the time. To set them all at once,\n"
|
|
|
|
" invoke 'remove' first to remove them all.\n\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL) parseArguments
|
|
|
|
{
|
|
|
|
NSString *s;
|
|
|
|
BOOL rc;
|
|
|
|
int max;
|
|
|
|
|
|
|
|
max = [arguments count];
|
|
|
|
if (max > 3 && max < 6)
|
|
|
|
{
|
|
|
|
s = [arguments objectAtIndex: 0];
|
|
|
|
if ([s isEqualToString: @"get"])
|
|
|
|
command = ManageACLGet;
|
|
|
|
else if ([s isEqualToString: @"add"])
|
|
|
|
{
|
|
|
|
command = ManageACLAdd;
|
|
|
|
rights = RETAIN([[arguments objectAtIndex: 4] objectFromJSONString]);
|
|
|
|
}
|
|
|
|
else if ([s isEqualToString: @"remove"])
|
|
|
|
command = ManageACLRemove;
|
2017-05-17 15:44:34 +02:00
|
|
|
else if ([s isEqualToString: @"subscribe"])
|
|
|
|
command = ManageACLSubscribe;
|
|
|
|
else if ([s isEqualToString: @"unsubscribe"])
|
|
|
|
command = ManageACLUnsubscribe;
|
2017-02-21 00:57:46 +01:00
|
|
|
else
|
|
|
|
{
|
|
|
|
[self usage];
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
owner = RETAIN([arguments objectAtIndex: 1]);
|
|
|
|
folder = RETAIN([arguments objectAtIndex: 2]);
|
|
|
|
user = RETAIN([arguments objectAtIndex: 3]);
|
|
|
|
|
|
|
|
rc = YES;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
[self usage];
|
|
|
|
rc = NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2017-02-21 16:12:55 +01:00
|
|
|
- (NSArray *) _fetchUserIDs
|
|
|
|
{
|
|
|
|
NSMutableArray *allUsers, *allSQLUsers;
|
|
|
|
NSAutoreleasePool *pool;
|
|
|
|
SOGoUserManager *lm;
|
|
|
|
NSDictionary *infos;
|
|
|
|
NSString *u;
|
|
|
|
|
|
|
|
int count, max;
|
|
|
|
|
|
|
|
lm = [SOGoUserManager sharedUserManager];
|
|
|
|
allSQLUsers = [[NSMutableArray alloc] init];
|
|
|
|
allUsers = [[NSMutableArray alloc] init];
|
|
|
|
|
|
|
|
if ([user isEqualToString: @"ALL"])
|
|
|
|
{
|
|
|
|
GCSChannelManager *cm;
|
|
|
|
NSURL *folderLocation;
|
|
|
|
GCSFolderManager *fm;
|
|
|
|
EOAdaptorChannel *fc;
|
|
|
|
NSArray *attrs;
|
|
|
|
NSString *sql;
|
|
|
|
|
|
|
|
fm = [GCSFolderManager defaultFolderManager];
|
|
|
|
cm = [fm channelManager];
|
|
|
|
folderLocation = [fm folderInfoLocation];
|
|
|
|
fc = [cm acquireOpenChannelForURL: folderLocation];
|
|
|
|
if (fc)
|
|
|
|
{
|
|
|
|
allSQLUsers = [NSMutableArray new];
|
|
|
|
sql = [NSString stringWithFormat: @"SELECT DISTINCT c_path2 FROM %@",
|
|
|
|
[folderLocation gcsTableName]];
|
|
|
|
[fc evaluateExpressionX: sql];
|
|
|
|
attrs = [fc describeResults: NO];
|
|
|
|
while ((infos = [fc fetchAttributes: attrs withZone: NULL]))
|
|
|
|
{
|
|
|
|
u = [infos objectForKey: @"c_path2"];
|
|
|
|
if (u)
|
|
|
|
[allSQLUsers addObject: u];
|
|
|
|
}
|
|
|
|
[cm releaseChannel: fc];
|
|
|
|
}
|
|
|
|
|
|
|
|
// We add our system users
|
|
|
|
[allSQLUsers addObject: @"<default>"];
|
|
|
|
|
|
|
|
if ([[SOGoSystemDefaults sharedSystemDefaults] enablePublicAccess])
|
2017-02-22 17:00:01 +01:00
|
|
|
[allSQLUsers addObject: @"anonymous"];
|
2017-02-21 16:12:55 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
[allSQLUsers addObject: user];
|
|
|
|
|
|
|
|
pool = [[NSAutoreleasePool alloc] init];
|
|
|
|
max = [allSQLUsers count];
|
|
|
|
|
|
|
|
for (count = 0; count < max; count++)
|
|
|
|
{
|
|
|
|
if (count > 0 && count%100 == 0)
|
|
|
|
{
|
|
|
|
DESTROY(pool);
|
|
|
|
pool = [[NSAutoreleasePool alloc] init];
|
|
|
|
}
|
|
|
|
|
|
|
|
u = [allSQLUsers objectAtIndex: count];
|
|
|
|
|
2017-02-22 17:00:01 +01:00
|
|
|
// We skip lookup for our 'system users' but keep them to set ACLs
|
|
|
|
if ([u isEqualToString: @"anonymous"] || [u isEqualToString: @"<default>"])
|
|
|
|
{
|
|
|
|
[allUsers addObject: u];
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Skip the owner
|
|
|
|
if ([u isEqualToString: owner])
|
2017-02-21 16:12:55 +01:00
|
|
|
continue;
|
|
|
|
|
|
|
|
infos = [lm contactInfosForUserWithUIDorEmail: u];
|
|
|
|
if (infos)
|
|
|
|
[allUsers addObject: [infos objectForKey: @"c_uid"]];
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// We haven't found the user based on the GCS table name
|
|
|
|
// Let's try to strip the domain part and search again.
|
|
|
|
// This can happen when using SOGoEnableDomainBasedUID (YES)
|
|
|
|
// but login in SOGo using a UID without domain (DomainLessLogin gets set)
|
|
|
|
NSRange r;
|
|
|
|
|
|
|
|
r = [u rangeOfString: @"@"];
|
|
|
|
|
|
|
|
if (r.location != NSNotFound)
|
|
|
|
{
|
|
|
|
u = [u substringToIndex: r.location];
|
|
|
|
infos = [lm contactInfosForUserWithUIDorEmail: u];
|
|
|
|
if (infos)
|
|
|
|
[allUsers addObject: [infos objectForKey: @"c_uid"]];
|
|
|
|
else
|
|
|
|
NSLog (@"user '%@' unknown", u);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
NSLog (@"user '%@' unknown", u);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
DESTROY(pool);
|
|
|
|
RELEASE(allSQLUsers);
|
|
|
|
|
|
|
|
return AUTORELEASE(allUsers);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-02-21 00:57:46 +01:00
|
|
|
- (void) addACLForUser: (NSString *) theUser
|
|
|
|
folder: (GCSFolder *) theFolder
|
|
|
|
{
|
2017-02-21 16:12:55 +01:00
|
|
|
NSString *currentRole, *SQL, *path, *u;
|
2017-02-21 00:57:46 +01:00
|
|
|
EOAdaptorChannel *channel;
|
2017-02-21 16:12:55 +01:00
|
|
|
NSArray *allUsers;
|
|
|
|
int i, j;
|
2017-02-21 00:57:46 +01:00
|
|
|
|
|
|
|
channel = [theFolder acquireAclChannel];
|
|
|
|
path = [NSString stringWithFormat: @"%@/%@", owner, folder];
|
2017-02-21 16:12:55 +01:00
|
|
|
allUsers = [self _fetchUserIDs];
|
2017-02-21 00:57:46 +01:00
|
|
|
|
2017-02-21 16:12:55 +01:00
|
|
|
for (i = 0; i < [allUsers count]; i++)
|
2017-02-21 00:57:46 +01:00
|
|
|
{
|
2017-02-21 16:12:55 +01:00
|
|
|
u = [allUsers objectAtIndex: i];
|
|
|
|
NSLog(@"Settings rights for user %@", u);
|
|
|
|
for (j = 0; j < [rights count]; j++)
|
|
|
|
{
|
|
|
|
currentRole = [rights objectAtIndex: j];
|
2018-01-12 20:11:53 +01:00
|
|
|
SQL = [NSString stringWithFormat: @"INSERT INTO %@"
|
2017-02-21 16:12:55 +01:00
|
|
|
@" (c_object, c_uid, c_role)"
|
|
|
|
@" VALUES ('/%@', '%@', '%@')",
|
|
|
|
[theFolder aclTableName],
|
|
|
|
path, u, currentRole];
|
|
|
|
[channel evaluateExpressionX: SQL];
|
|
|
|
}
|
2017-02-21 00:57:46 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) getACLForUser: (NSString *) theUser
|
|
|
|
folder: (GCSFolder *) theFolder
|
|
|
|
{
|
|
|
|
NSArray *allRights, *allKeys;
|
|
|
|
NSMutableDictionary *d;
|
|
|
|
EOQualifier *qualifier;
|
|
|
|
NSDictionary *right;
|
|
|
|
NSString *qs;
|
|
|
|
id o;
|
|
|
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if ([theUser isEqualToString: @"ALL"])
|
|
|
|
qualifier = nil;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
qs = [NSString stringWithFormat: @"c_uid = '%@'", theUser];
|
|
|
|
qualifier = [EOQualifier qualifierWithQualifierFormat: qs];
|
|
|
|
}
|
|
|
|
|
|
|
|
allRights = [theFolder fetchAclMatchingQualifier: qualifier];
|
|
|
|
d = [NSMutableDictionary dictionary];
|
|
|
|
|
|
|
|
for (i = 0; i < [allRights count]; i++)
|
|
|
|
{
|
|
|
|
right = [allRights objectAtIndex: i];
|
|
|
|
if ((o = [d objectForKey: [right objectForKey: @"c_uid"]]))
|
|
|
|
[o addObject: [right objectForKey: @"c_role"]];
|
|
|
|
else
|
|
|
|
[d setObject: [NSMutableArray arrayWithObject: [right objectForKey: @"c_role"]]
|
|
|
|
forKey: [right objectForKey: @"c_uid"]];
|
|
|
|
}
|
|
|
|
|
|
|
|
allKeys = [d allKeys];
|
|
|
|
|
|
|
|
for (i = 0; i < [allKeys count]; i++)
|
|
|
|
{
|
|
|
|
o = [allKeys objectAtIndex: i];
|
|
|
|
NSLog(@"Rights for %@ %@", o, [[d objectForKey: o] jsonRepresentation]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) removeACLForUser: (NSString *) theUser
|
|
|
|
folder: (GCSFolder *) theFolder
|
|
|
|
{
|
|
|
|
EOQualifier *qualifier;
|
|
|
|
NSString *qs, *path;
|
|
|
|
|
|
|
|
if ([theUser isEqualToString: @"ALL"])
|
2017-02-21 16:12:55 +01:00
|
|
|
qs = [NSString stringWithFormat: @"c_uid LIKE '\%'", theUser];
|
2017-02-21 00:57:46 +01:00
|
|
|
else
|
2017-05-17 15:44:34 +02:00
|
|
|
qs = [NSString stringWithFormat: @"c_uid = '%@'", theUser];
|
2017-02-21 16:12:55 +01:00
|
|
|
|
|
|
|
qualifier = [EOQualifier qualifierWithQualifierFormat: qs];
|
2017-02-21 00:57:46 +01:00
|
|
|
|
|
|
|
[theFolder deleteAclMatchingQualifier: qualifier];
|
|
|
|
|
|
|
|
// We clear the cache. We first strip /Users/ from our path
|
|
|
|
path = [[[[theFolder path] pathComponents] subarrayWithRange: NSMakeRange(2,3)] componentsJoinedByString: @"/"];
|
|
|
|
[[SOGoCache sharedCache] setACLs: nil
|
|
|
|
forPath: path];
|
|
|
|
}
|
|
|
|
|
2017-05-17 15:44:34 +02:00
|
|
|
- (void) subscribeOrUnsubscribeUser: (NSString *) theUser
|
|
|
|
toFolder: (GCSFolder *) theFolder
|
|
|
|
reallyDo: (BOOL) reallyDo
|
|
|
|
{
|
|
|
|
SOGoParentFolder *parentFolder;
|
|
|
|
SOGoUserFolder *userFolder;
|
|
|
|
SOGoGCSFolder *gcsFolder;
|
|
|
|
WOContext *localContext;
|
|
|
|
NSArray *components;
|
|
|
|
|
|
|
|
localContext = [WOContext context];
|
|
|
|
[localContext setActiveUser: [SOGoUser userWithLogin: owner]];
|
|
|
|
userFolder = [SOGoUserFolder objectWithName: owner inContainer: nil];
|
|
|
|
components = [folder componentsSeparatedByString: @"/"];
|
|
|
|
parentFolder = [userFolder lookupName: [components objectAtIndex: 0]
|
|
|
|
inContext: localContext
|
|
|
|
acquire: NO];
|
|
|
|
|
|
|
|
gcsFolder = [parentFolder lookupPersonalFolder: [components objectAtIndex: 1]
|
|
|
|
ignoringRights: YES];
|
|
|
|
|
2017-05-17 15:56:16 +02:00
|
|
|
[gcsFolder subscribeUserOrGroup: user reallyDo: reallyDo response: nil];
|
2017-05-17 15:44:34 +02:00
|
|
|
}
|
|
|
|
|
2017-02-21 00:57:46 +01:00
|
|
|
- (BOOL) proceed
|
|
|
|
{
|
|
|
|
NSAutoreleasePool *pool;
|
|
|
|
GCSFolderManager *fm;
|
|
|
|
GCSFolder *f;
|
|
|
|
|
|
|
|
BOOL rc;
|
|
|
|
|
|
|
|
rc = YES;
|
|
|
|
|
|
|
|
pool = [NSAutoreleasePool new];
|
|
|
|
|
|
|
|
fm = [GCSFolderManager defaultFolderManager];
|
|
|
|
f = [fm folderAtPath: [NSString stringWithFormat: @"/Users/%@/%@", owner, folder]];
|
|
|
|
|
2017-02-21 16:12:55 +01:00
|
|
|
if (!f)
|
|
|
|
{
|
|
|
|
NSLog(@"No folder %@ found for user %@", folder, owner);
|
|
|
|
rc = NO;
|
|
|
|
}
|
2017-02-21 00:57:46 +01:00
|
|
|
else
|
2017-02-21 16:12:55 +01:00
|
|
|
{
|
|
|
|
if (command == ManageACLGet)
|
|
|
|
[self getACLForUser: user folder: f];
|
|
|
|
else if (command == ManageACLRemove)
|
|
|
|
[self removeACLForUser: user folder: f];
|
|
|
|
else if (command == ManageACLAdd)
|
|
|
|
[self addACLForUser: user folder: f];
|
2017-05-17 15:44:34 +02:00
|
|
|
else if (command == ManageACLSubscribe)
|
|
|
|
[self subscribeOrUnsubscribeUser: user toFolder: f reallyDo: YES];
|
|
|
|
else if (command == ManageACLUnsubscribe)
|
|
|
|
[self subscribeOrUnsubscribeUser: user toFolder: f reallyDo: NO];
|
2017-02-21 16:12:55 +01:00
|
|
|
else
|
|
|
|
[self usage];
|
|
|
|
}
|
2017-02-21 00:57:46 +01:00
|
|
|
|
|
|
|
[pool release];
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL) run
|
|
|
|
{
|
|
|
|
return ([self parseArguments] && [self proceed]);
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|