1/* SOGoWebDAVAclManager.m - this file is part of SOGo
2 *
3 * Copyright (C) 2008-2015 Inverse inc.
4 *
5 * This file is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
8 * any later version.
9 *
10 * This file is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; see the file COPYING.  If not, write to
17 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
19 */
20
21#import <Foundation/NSArray.h>
22#import <Foundation/NSEnumerator.h>
23#import <Foundation/NSString.h>
24#import <Foundation/NSValue.h>
25
26#import <NGObjWeb/SoClass.h>
27#import <NGObjWeb/SoClassSecurityInfo.h>
28#import <NGExtensions/NSObject+Logs.h>
29
30#import "NSDictionary+Utilities.h"
31#import "NSObject+DAV.h"
32#import "SOGoObject.h"
33#import "SOGoUser.h"
34#import "SOGoWebDAVValue.h"
35
36#import "SOGoWebDAVAclManager.h"
37
38static NSNumber *yesObject = nil;
39
40@interface SoClass (SOGoDAVPermissions)
41
42- (BOOL) userRoles: (NSArray *) userRoles
43    havePermission: (NSString *) permission;
44
45@end
46
47@implementation SoClass (SOGoDAVPermissions)
48
49- (BOOL) userRoles: (NSArray *) userRoles
50    havePermission: (NSString *) permission
51{
52  BOOL result;
53  SoClass *currentClass;
54  NSArray *roles;
55
56  result = NO;
57
58  currentClass = self;
59  while (!result && currentClass)
60    {
61      roles = [[currentClass soClassSecurityInfo]
62		defaultRolesForPermission: permission];
63      if ([roles firstObjectCommonWithArray: userRoles])
64	{
65 	  // NSLog (@"matched '%@': %@", permission, roles);
66	  result = YES;
67	}
68      else
69	currentClass = [currentClass soSuperClass];
70    }
71
72  return result;
73}
74
75@end
76
77@implementation SOGoWebDAVAclManager
78
79+ (void) initialize
80{
81  if (!yesObject)
82    {
83      yesObject = [NSNumber numberWithBool: YES];
84      [yesObject retain];
85    }
86}
87
88- (id) init
89{
90  if ((self = [super init]))
91    {
92      aclTree = [NSMutableDictionary new];
93      [self registerDAVPermission: davElement (@"all", @"DAV:")
94                         abstract: YES
95                   withEquivalent: nil
96                        asChildOf: nil];
97    }
98
99  return self;
100}
101
102- (void) dealloc
103{
104  [aclTree release];
105  [super dealloc];
106}
107
108- (void) _registerChild: (NSMutableDictionary *) newEntry
109		     of: (NSDictionary *) parentPermission
110{
111  NSString *identifier;
112  NSMutableDictionary *parentEntry;
113  NSMutableArray *children;
114
115  identifier = [parentPermission keysWithFormat: @"{%{ns}}%{method}"];
116  parentEntry = [aclTree objectForKey: identifier];
117  if (parentEntry)
118    {
119      children = [parentEntry objectForKey: @"children"];
120      if (!children)
121        {
122          children = [NSMutableArray array];
123          [parentEntry setObject: children forKey: @"children"];
124        }
125      [children addObject: newEntry];
126      [newEntry setObject: parentEntry forKey: @"parent"];
127    }
128  else
129    [self errorWithFormat: @"parent entry '%@' does not exist in DAV"
130	  @" permissions table", identifier];
131}
132
133- (void) registerDAVPermission: (NSDictionary *) davPermission
134	 	      abstract: (BOOL) abstract
135		withEquivalent: (NSString *) sogoPermission
136		     asChildOf: (NSDictionary *) otherDAVPermission
137{
138  NSMutableDictionary *newEntry;
139  NSString *identifier;
140
141  newEntry = [NSMutableDictionary new];
142  identifier = [davPermission keysWithFormat: @"{%{ns}}%{method}"];
143  if ([aclTree objectForKey: identifier])
144    [self warnWithFormat:
145            @"entry '%@' already exists in DAV permissions table",
146          identifier];
147  [aclTree setObject: newEntry forKey: identifier];
148  [newEntry setObject: davPermission forKey: @"permission"];
149  if (abstract)
150    [newEntry setObject: yesObject forKey: @"abstract"];
151  if (sogoPermission)
152    [newEntry setObject: sogoPermission forKey: @"equivalent"];
153
154  if (otherDAVPermission)
155    [self _registerChild: newEntry of: otherDAVPermission];
156
157  [newEntry release];
158}
159
160#warning this method should be simplified!
161/* We add the permissions that fill those conditions:
162   - should match the sogo permissions implied by the user roles
163   If all the child permissions of a permission are included, then this
164   permission will be included too. Conversely, if a permission is included,
165   all the child permissions will be included too. */
166
167- (BOOL) _fillArray: (NSMutableArray *) davPermissions
168     withPermission: (NSDictionary *) permission
169       forUserRoles: (NSArray *) userRoles
170	withSoClass: (SoClass *) soClass
171     matchSOGoPerms: (BOOL) matchSOGoPerms
172{
173  NSString *sogoPermission;
174  NSDictionary *childPermission;
175  NSEnumerator *children;
176  BOOL appended, childrenAppended;
177
178  // NSLog (@"attempt to add permission: %@",
179  //        [permission objectForKey: @"permission"]);
180  appended = YES;
181  if (matchSOGoPerms)
182    {
183      sogoPermission = [permission objectForKey: @"equivalent"];
184      if (sogoPermission
185	  && [soClass userRoles: userRoles havePermission: sogoPermission])
186	[davPermissions addObject: [permission objectForKey: @"permission"]];
187      else
188	appended = NO;
189      // NSLog (@"permission '%@' appended: %d", sogoPermission, appended);
190    }
191  else
192    [davPermissions
193      addObject: [permission objectForKey: @"permission"]];
194
195  children = [[permission objectForKey: @"children"] objectEnumerator];
196  if (children)
197    {
198      childrenAppended = YES;
199      while ((childPermission = [children nextObject]))
200	childrenAppended &= [self _fillArray: davPermissions
201                              withPermission: childPermission
202                                forUserRoles: userRoles
203                                 withSoClass: soClass
204                              matchSOGoPerms: (matchSOGoPerms
205                                               && !appended)];
206      if (childrenAppended && !appended)
207	{
208          // NSLog (@"  adding perm '%@' because children were appended",
209          //        [permission objectForKey: @"permission"]);
210	  [davPermissions
211	    addObject: [permission objectForKey: @"permission"]];
212	  appended = YES;
213	}
214    }
215
216  return appended;
217}
218
219- (NSArray *) davPermissionsForRoles: (NSArray *) roles
220			    onObject: (SOGoObject *) object
221{
222  NSMutableArray *davPermissions;
223  SoClass *soClass;
224
225  davPermissions = [NSMutableArray array];
226  soClass = [[object class] soClass];
227  [self _fillArray: davPermissions
228	withPermission: [aclTree objectForKey: @"{DAV:}all"]
229	forUserRoles: roles
230	withSoClass: soClass
231	matchSOGoPerms: YES];
232
233  return davPermissions;
234}
235
236- (SOGoWebDAVValue *)
237    _supportedPrivilegeSetFromPermission: (NSDictionary *) perm
238{
239  NSMutableArray *privilege;
240  NSEnumerator *children;
241  NSDictionary *currentPerm;
242
243  privilege = [NSMutableArray array];
244  [privilege addObject:
245	       davElementWithContent (@"privilege",
246				      @"DAV:",
247				      [perm objectForKey: @"permission"])];
248  if ([[perm objectForKey: @"abstract"] boolValue])
249    [privilege addObject: davElement (@"abstract", @"DAV:")];
250  children = [[perm objectForKey: @"children"] objectEnumerator];
251  while ((currentPerm = [children nextObject]))
252    [privilege addObject:
253		 [self _supportedPrivilegeSetFromPermission: currentPerm]];
254
255  return davElementWithContent (@"supported-privilege",
256				@"DAV:", privilege);
257}
258
259- (SOGoWebDAVValue *) treeAsWebDAVValue
260{
261  return [self _supportedPrivilegeSetFromPermission:
262		 [aclTree objectForKey: @"{DAV:}all"]];
263}
264
265- (id) copyWithZone: (NSZone *) aZone
266{
267  SOGoWebDAVAclManager *x;
268
269  x = [[SOGoWebDAVAclManager allocWithZone: aZone] init];
270  x->aclTree = [aclTree mutableCopyWithZone: aZone];
271
272  return x;
273}
274
275@end
276