Tuukka Norri avatar Tuukka Norri committed b9dd2d6

Retrieving ACLs should now work
- The connection is now able to instantiate PGTSACLItems.
- PGTSClassInfo knows also the owner.
- Groups or roles with members get handled recursively.
- Also removed an old workaround from PGTSAdditions.m.

Comments (0)

Files changed (12)

Framework/Sources/PGTSACLItem.h

 
 @interface PGTSACLItem : NSObject 
 {
-    PGTSRoleDescription* role;
-    enum PGTSACLItemPrivilege privileges;
+    PGTSRoleDescription* mRole;
+    PGTSRoleDescription* mGrantingRole;
+    enum PGTSACLItemPrivilege mPrivileges;
 }
 
 - (enum PGTSACLItemPrivilege) privileges;
+- (void) setPrivileges: (enum PGTSACLItemPrivilege) privileges;
 - (PGTSRoleDescription *) role;
 - (void) setRole: (PGTSRoleDescription *) aRole;
+- (PGTSRoleDescription *) grantingRole;
+- (void) setGrantingRole: (PGTSRoleDescription *) aGrantingRole;
 
 @end

Framework/Sources/PGTSACLItem.m

 {
     if ((self = [super init]))
     {
-        privileges = kPGTSPrivilegeNone;
+        mPrivileges = kPGTSPrivilegeNone;
     }
     return self;
 }
 
 - (void) dealloc
 {
-    [role release];
+    [mRole release];
+    [mGrantingRole release];
     [super dealloc];
 }
 
 - (PGTSRoleDescription *) role
 {
-    return role; 
+    return mRole;
 }
 
 - (void) setRole: (PGTSRoleDescription *) aRole
 {
-    if (role != aRole) {
-        [role release];
-        role = [aRole retain];
+    if (mRole != aRole) {
+        [mRole release];
+        mRole = [aRole retain];
+    }
+}
+
+- (PGTSRoleDescription *) grantingRole
+{
+    return mGrantingRole; 
+}
+
+- (void) setGrantingRole: (PGTSRoleDescription *) aGrantingRole
+{
+    if (mGrantingRole != aGrantingRole) {
+        [mGrantingRole release];
+        mGrantingRole = [aGrantingRole retain];
     }
 }
 
 - (enum PGTSACLItemPrivilege) privileges
 {
-    return privileges;
+    return mPrivileges;
+}
+
+- (void) setPrivileges: (enum PGTSACLItemPrivilege) anEnum
+{
+    mPrivileges = anEnum;
 }
 
 @end

Framework/Sources/PGTSAbstractClassInfo.h

 #import <PGTS/postgresql/libpq-fe.h> 
 
 @class TSIndexDictionary;
+@class PGTSRoleDescription;
+@class PGTSACLItem;
 
 
 @interface PGTSAbstractClassInfo : PGTSAbstractObjectDescription
     Oid schemaOid;
     NSString* schemaName;
     TSIndexDictionary* aclItems;
+    PGTSRoleDescription* owner;
 }
 - (void) setSchemaOid: (Oid) anOid;
 - (void) setSchemaName: (NSString *) anString;
-
+- (void) addACLItem: (PGTSACLItem *) item;
+- (PGTSRoleDescription *) owner;
+- (void) setOwner: (PGTSRoleDescription *) anOwner;
 @end
 
 
 - (Oid) schemaOid;
 - (NSString *) schemaName;
 - (NSString *) qualifiedName;
+- (BOOL) role: (PGTSRoleDescription *) aRole 
+ hasPrivilege: (enum PGTSACLItemPrivilege) aPrivilege;
+- (NSArray *) ACLItems;
 @end

Framework/Sources/PGTSAbstractClassInfo.m

 // $Id$
 //
 
-#import <PGTS/PGTSAbstractClassInfo.h>
-#import <PGTS/PGTSResultSet.h>
-#import <PGTS/PGTSFunctions.h>
-#import <PGTS/PGTSConnection.h>
-#import <PGTS/PGTSAdditions.h>
+#import "PGTSAbstractClassInfo.h"
+#import "PGTSResultSet.h"
+#import "PGTSFunctions.h"
+#import "PGTSConnection.h"
+#import "PGTSAdditions.h"
+#import "PGTSConstants.h"
+#import "PGTSRoleDescription.h"
+#import "PGTSACLItem.h"
+#import "PGTSDatabaseInfo.h"
 #import <TSDataTypes/TSDataTypes.h>
 
 /** 
     }
 }
 
+- (void) addACLItem: (PGTSACLItem *) item
+{
+    [aclItems setObject: item atIndex: [[item role] oid]];
+}
+
+- (PGTSRoleDescription *) owner
+{
+    return owner; 
+}
+
+- (void) setOwner: (PGTSRoleDescription *) anOwner
+{
+    if (owner != anOwner) {
+        [owner release];
+        owner = [anOwner retain];
+    }
+}
+
 @end
 
 
 {
     if (nil == name)
     {
-        PGTSResultSet* res = [connection executeQuery: @"SELECT relname, n.oid, nspname FROM pg_class c, pg_namespace n "
-                                                        "WHERE c.oid = $1 AND c.relnamespace = n.oid"
-                                           parameters: PGTSOidAsObject (oid)];
+        NSString* query = 
+        @"SELECT c.relname, c.relacl, c.relowner, n.oid, n.nspname, r.rolname "
+        " FROM pg_class c, pg_namespace n, pg_roles r "
+        " WHERE c.relnamespace = n.oid AND r.oid = c.relowner AND c.oid = $1";
+        PGTSResultSet* res = [connection executeQuery: query parameters: PGTSOidAsObject (oid)];
         if (0 < [res numberOfRowsAffected])
         {
             [res advanceRow];
-            [self setName:  [res valueForFieldNamed: @"relname"]];
-            [self setSchemaName: [res valueForFieldNamed: @"nspname"]];
-            [self setSchemaOid: [[res valueForFieldNamed: @"oid"] PGTSOidValue]];
+            [self setName:  [res valueForKey: @"relname"]];
+            [self setSchemaName: [res valueForKey: @"nspname"]];
+            [self setSchemaOid: [[res valueForKey: @"oid"] PGTSOidValue]];
+            
+            PGTSRoleDescription* role = [[connection databaseInfo] roleNamed: [res valueForKey: @"rolname"]
+                                                                         oid: [[res valueForKey: @"relowner"] PGTSOidValue]];
+            [self setOwner: role];
+            
+            TSEnumerate (currentACLItem, e, [[res valueForKey: @"relacl"] objectEnumerator])
+                [self addACLItem: currentACLItem];
         }
     }
     return name;
     return [NSString stringWithFormat: @"\"%@\".\"%@\"", schemaName, name];
 }
 
+- (BOOL) role: (PGTSRoleDescription *) aRole 
+ hasPrivilege: (enum PGTSACLItemPrivilege) aPrivilege
+{
+    if (nil == name)
+        [self name];
+    
+    //First try the user's privileges, then PUBLIC's and last different groups'.
+    //The owner has all the privileges.
+    BOOL rval = (owner == aRole || [owner isEqual: aRole]);
+    if (NO == rval)
+        (0 != (aPrivilege & [[aclItems objectAtIndex: [aRole oid]] privileges]));
+    if (NO == rval)
+        rval = (0 != (aPrivilege & [[aclItems objectAtIndex: kPGTSPUBLICOid] privileges]));
+    if (NO == rval)
+    {
+        TSEnumerate (currentItem, e, [aclItems objectEnumerator])
+        {
+            if (aPrivilege & [currentItem privileges] && [[currentItem role] hasMember: aRole])
+            {
+                rval = YES;
+                break;
+            }
+        }
+    }
+    return rval;
+}
+
+- (NSArray *) ACLItems
+{
+    if (nil == name)
+        [self name];
+    return [aclItems allObjects];
+}
+
 @end

Framework/Sources/PGTSAbstractObjectDescription.m

     oid = anOid;
 }
 
+- (BOOL) isEqual: (id) anObject
+{
+    return ([anObject oid] == oid && [super isEqual: anObject]); 
+}
+
 @end

Framework/Sources/PGTSAdditions.m

 #import "PGTSTypeInfo.h"
 #import "PGTSFieldInfo.h"
 #import "PGTSDatabaseInfo.h"
+#import "PGTSACLItem.h"
 
 
-//FIXME: This really has to be removed. The function is included in libpq bundled with PostgreSQL 8.0.8 and 8.1.4
-#define PQescapeStringConn( conn, to, from, length, error ) PQescapeString( to, from, length )
+//A workaround for libpq versions earlier than 8.0.8 and 8.1.4
+//#define PQescapeStringConn( conn, to, from, length, error ) PQescapeString( to, from, length )
 
 
 
         if (nil == deserializationDictionary)
             deserializationDictionary = [NSDictionary PGTSDeserializationDictionary];
         Class elementClass = [deserializationDictionary objectForKey: [elementType name]];
+        if (Nil == elementClass)
+            elementClass = [NSData class];
         
         size_t length = strlen (value);
         unsigned int objectBaseIndex = 0;
     return [NSValue valueWithSize: s];
 }
 @end
+
+@implementation PGTSACLItem (PGTSAdditions)
++ (id) newForPGTSResultSet: (PGTSResultSet *) res withCharacters: (char *) value typeInfo: (PGTSTypeInfo *) typeInfo
+{        
+    //Role and privileges are separated by an equals sign
+    id rval = nil;
+    char* grantingRole = value;
+    char* role = strsep (&grantingRole, "=");
+    char* privileges = strsep (&grantingRole, "/");
+    
+    //Zero-length but not NULL
+    NSAssert (NULL != privileges && NULL != role && NULL != grantingRole, @"Unable to parse privileges.");
+    
+    //Role is zero-length if the privileges are for PUBLIC
+    rval = [[[PGTSACLItem alloc] init] autorelease];
+    if (0 != strlen (role))
+    {
+        PGTSDatabaseInfo* database = [[res connection] databaseInfo];
+        
+        //Remove "group " from beginning
+        if (role == strstr (role, "group "))
+            role = &role [6]; //6 == strlen ("group ");
+        if (grantingRole == strstr (role, "group "))
+            grantingRole = &grantingRole [6];
+        
+        [rval setRole: [database roleNamed: [NSString stringWithUTF8String: role]]];
+        [rval setGrantingRole: [database roleNamed: [NSString stringWithUTF8String: grantingRole]]];
+    }
+    
+    //Parse the privileges
+    enum PGTSACLItemPrivilege userPrivileges = kPGTSPrivilegeNone;
+    enum PGTSACLItemPrivilege grantOption = kPGTSPrivilegeNone;
+    for (unsigned int i = 0, length = strlen (privileges); i < length; i++)
+    {
+        switch (privileges [i])
+        {
+            case 'r': //SELECT
+                userPrivileges |= kPGTSPrivilegeSelect;
+                grantOption = kPGTSPrivilegeSelectGrant;
+                break;
+            case 'w': //UPDATE
+                userPrivileges |= kPGTSPrivilegeUpdate;
+                grantOption = kPGTSPrivilegeUpdateGrant;
+                break;
+            case 'a': //INSERT
+                userPrivileges |= kPGTSPrivilegeInsert;
+                grantOption = kPGTSPrivilegeInsertGrant;
+                break;
+            case 'd': //DELETE
+                userPrivileges |= kPGTSPrivilegeDelete;
+                grantOption = kPGTSPrivilegeDeleteGrant;
+                break;
+            case 'x': //REFERENCES
+                userPrivileges |= kPGTSPrivilegeReferences;
+                grantOption = kPGTSPrivilegeReferencesGrant;
+                break;
+            case 't': //TRIGGER
+                userPrivileges |= kPGTSPrivilegeTrigger;
+                grantOption = kPGTSPrivilegeTriggerGrant;
+                break;
+            case 'X': //EXECUTE
+                userPrivileges |= kPGTSPrivilegeExecute;
+                grantOption = kPGTSPrivilegeExecuteGrant;
+                break;
+            case 'U': //USAGE
+                userPrivileges |= kPGTSPrivilegeUsage;
+                grantOption = kPGTSPrivilegeUsageGrant;
+                break;
+            case 'C': //CREATE
+                userPrivileges |= kPGTSPrivilegeCreate;
+                grantOption = kPGTSPrivilegeCreateGrant;
+                break;
+            case 'c': //CONNECT
+                userPrivileges |= kPGTSPrivilegeConnect;
+                grantOption = kPGTSPrivilegeConnectGrant;
+                break;
+            case 'T': //TEMPORARY
+                userPrivileges |= kPGTSPrivilegeTemporary;
+                grantOption = kPGTSPrivilegeTemporaryGrant;
+                break;
+            case '*': //Grant option
+                userPrivileges |= grantOption;
+                grantOption = kPGTSPrivilegeNone;
+                break;
+            default:
+                break;
+        }
+    }
+    [rval setPrivileges: userPrivileges];
+    
+    return rval;    
+}
+@end

Framework/Sources/PGTSConstants.h

 #define PGTS_EXPORT extern
 #endif
 
+#define kPGTSPUBLICOid InvalidOid
+
 
 PGTS_EXPORT NSDictionary* kPGTSDefaultConnectionDictionary;
 

Framework/Sources/PGTSDatabaseInfo.h

 @class PGTSTypeInfo;
 @class TSIndexDictionary;
 @class PGTSConnectionPoolItem;
+@class PGTSRoleDescription;
 
 
 @interface PGTSDatabaseInfo : PGTSAbstractInfo 
 - (NSString *) connectionPoolKey;
 - (void) setConnectionPoolKey: (NSString *) aKey;
 - (void) updateTableCache: (PGTSTableInfo *) table;
+- (PGTSRoleDescription *) roleNamed: (NSString *) name;
+- (PGTSRoleDescription *) roleNamed: (NSString *) name oid: (Oid) oid;
 
 @end

Framework/Sources/PGTSDatabaseInfo.m

 // $Id$
 //
 
-#import <PGTS/PGTSDatabaseInfo.h>
-#import <PGTS/PGTSTypeInfo.h>
-#import <PGTS/PGTSTableInfo.h>
-#import <PGTS/PGTSResultSet.h>
-#import <PGTS/PGTSConnection.h>
-#import <PGTS/PGTSAdditions.h>
+#import "PGTSDatabaseInfo.h"
+#import "PGTSTypeInfo.h"
+#import "PGTSTableInfo.h"
+#import "PGTSResultSet.h"
+#import "PGTSConnection.h"
+#import "PGTSAdditions.h"
+#import "PGTSFunctions.h"
 #import <TSDataTypes/TSDataTypes.h>
 
 
     PGTSTableInfo* rval = [[schemas objectForKey: schemaName] objectForKey: tableName];
     if (nil == rval)
     {
-        NSString* queryString = @"SELECT c.oid AS oid, c.relnamespace AS schemaoid FROM pg_class c, pg_namespace n WHERE c.relname = $1 AND n.nspname = $2";
+        NSString* queryString = 
+        @"SELECT c.oid AS oid, c.relnamespace AS schemaoid, c.relacl, c.relowner, r.rolname "
+        " FROM pg_class c, pg_namespace n, pg_roles r "
+        " WHERE c.relowner = r.oid AND c.relname = $1 AND n.nspname = $2";
         PGTSResultSet* res = [connection executeQuery: queryString parameters: tableName, schemaName];
         if (0 < [res countOfRows])
         {
             rval = [self tableInfoForTableWithOid: [[res valueForKey: @"oid"] PGTSOidValue]];
             [rval setName: tableName];
             [rval setSchemaOid: [[res valueForKey: @"schemaoid"] PGTSOidValue]];
+
+            PGTSRoleDescription* role = [[connection databaseInfo] roleNamed: [res valueForKey: @"rolname"]
+                                                                         oid: [[res valueForKey: @"relowner"] PGTSOidValue]];
+            [rval setOwner: role];
+            
             [rval setSchemaName: schemaName];
+            TSEnumerate (currentACLItem, e, [[res valueForKey: @"relacl"] objectEnumerator])
+                [rval addACLItem: currentACLItem];
             [self updateTableCache: rval];
         }
     }
     }
 }
 
+- (PGTSRoleDescription *) roleNamed: (NSString *) aName
+{
+    return [self roleNamed: aName oid: InvalidOid];
+}
+
+- (PGTSRoleDescription *) roleNamed: (NSString *) originalName oid: (Oid) oid
+{
+    id aName = originalName;
+    if (nil == aName)
+        aName = [NSNull null];
+    
+    id rval = [roles objectForKey: aName];
+    if (nil == rval)
+    {
+        if (nil != originalName && InvalidOid == oid)
+        {
+            NSString* query = @"SELECT oid FROM pg_roles WHERE rolname = $1";
+            PGTSResultSet* res = [connection executeQuery: query parameters: aName];
+            if (0 < [res countOfRows])
+            {
+                [res advanceRow];
+                oid = [[res valueForKey: @"oid"] PGTSOidValue];
+            }
+        }
+        
+        rval = [[[PGTSRoleDescription alloc] init] autorelease];
+        [rval setOid: oid];
+        [rval setName: aName];
+        [roles setObject: rval forKey: aName];        
+    }
+    return rval;
+}
+
 @end

Framework/Sources/PGTSRoleDescription.h

 {
     TSIndexDictionary* roles;
 }
+@end
 
-- (void) addRole: (PGTSRoleDescription *) aRole;
+
+@interface PGTSRoleDescription (Queries)
 - (BOOL) hasMember: (PGTSRoleDescription *) aRole;
-
 @end

Framework/Sources/PGTSRoleDescription.m

 #import <PGTS/postgresql/libpq-fe.h> 
 #import "PGTSRoleDescription.h"
 #import "PGTSFunctions.h"
+#import "PGTSResultSet.h"
+#import "PGTSConnection.h"
+#import "PGTSAdditions.h"
+#import "PGTSDatabaseInfo.h"
 
 
 @implementation PGTSRoleDescription
 
-- (id) init
-{
-    if ((self = [super init]))
-    {
-        roles = [[TSIndexDictionary alloc] init];
-    }
-    return self;
-}
-
 - (void) dealloc
 {
     [roles release];
     [super dealloc];
 }
 
-- (void) addRole: (PGTSRoleDescription *) aRole
-{
-    [roles setObject: aRole atIndex: [aRole oid]];
-}
+@end
+
+
+@implementation PGTSRoleDescription (Queries)
 
 - (BOOL) hasMember: (PGTSRoleDescription *) aRole
 {
     BOOL rval = NO;
+    
+    if (nil == roles)
+    {
+        roles = [[TSIndexDictionary alloc] init];
+        NSString* query = @"SELECT r.oid, r.rolname FROM pg_roles r INNER JOIN pg_authid a WHERE r.oid = a.member AND a.roleid = $1";
+        PGTSResultSet* res = [connection executeQuery: query parameters: PGTSOidAsObject ([self oid])];
+        while ([res advanceRow])
+        {
+            Oid memberOid = [[res valueForKey: @"oid"] PGTSOidValue];
+            PGTSRoleDescription* role = [[connection databaseInfo] roleNamed: [res valueForKey: @"rolname"] oid: memberOid];
+            [roles setObject: role atIndex: memberOid];
+        }
+    }
+    
     if (nil != [roles objectAtIndex: [aRole oid]])
         rval = YES;
     else

Framework/datatypeassociations.plist

     <string>NSString</string>
     <key>int2vector</key>
     <string>NSArray</string>
+    <key>aclitem</key>
+    <string>PGTSACLItem</string>
     
     <!-- Array types -->
     <key>_abstime</key>
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.