class SQLFolderItems(SQLGatewayBase): """SQL folder items gateway""" __implements__ = SQLGatewayBase.__implements__ schema = RowSequenceSchema() schema.add('key', 'string', 1) schema.add('oid', 'string') schema.add('classification', 'classification') table_name = 'folder_items' table_schema = RowSequenceSchema() table_schema.add('name', 'string', 1) table_schema.add('child_oid', 'int', 0) def load(self, event): table = self.get_table(event) rows = table.select(self.column_names, oid=event.oid) res = [] h = [] for name, child_oid in rows: s = str(child_oid) classification = event.classify(s) res.append((name, s, classification)) h.append((name, long(child_oid))) h.sort() return res, tuple(h) def store(self, event, state): table = self.get_table(event) rows = [(name, long(child_oid)) for (name, child_oid, cls) in state] rows.sort() # Note that set_many() requires the child_oid column to match # its database type. table.set_many(event.oid, ('name', ), ('child_oid', ), rows) return tuple(rows)
class SQLProperties(SQLGatewayBase): """SQL properties gateway """ __implements__ = SQLGatewayBase.__implements__ schema = RowSequenceSchema() schema.add('id', 'string', 1) schema.add('type', 'string') schema.add('data', 'string') table_name = 'properties' table_schema = RowSequenceSchema() table_schema.add('id', 'string', 1) table_schema.add('type', 'string', 0) table_schema.add('data', 'blob', 0) def load(self, event): table = self.get_table(event) rows = table.select(self.column_names, oid=event.oid) rows.sort() return rows, tuple(rows) def store(self, event, state): table = self.get_table(event) rows = [(id, t, data) for id, t, data in state] table.set_many(event.oid, ('id', ), ('type', 'data'), rows) state = list(state) state.sort() return tuple(state)
def __init__(self, conn_name='db'): self.conn_name = conn_name if self.table_schema is None: if self.schema is not None: self.table_schema = self.schema else: self.table_schema = RowSequenceSchema() self.column_names = [f.name for f in self.table_schema.get_columns()]
def init(self, event): conn = self.get_connection(event) for table_name, columns in self.table_defs.items(): table_schema = RowSequenceSchema() for args in columns: table_schema.add(*args) table = conn.define_table(table_name, table_schema) if not conn.exists(table_name, 'table'): table.create() elif event.clear_all: table.delete_rows()
class SQLGatewayBase: """SQL gateway base class""" __implements__ = IGateway, IDatabaseInitializer # override these in subclasses table_name = None schema = None table_schema = None oid_columns = [ColumnSchema('oid', 'int', 1)] def __init__(self, conn_name='db'): self.conn_name = conn_name if self.table_schema is None: if self.schema is not None: self.table_schema = self.schema else: self.table_schema = RowSequenceSchema() self.column_names = [f.name for f in self.table_schema.get_columns()] def get_connection(self, event): return event.connections[self.conn_name] def get_table(self, event): c = event.connections[self.conn_name] return c.get_table(self.table_name) def create(self, event): self.get_table(event).create() def init(self, event): conn = self.get_connection(event) assert IRDBMSConnection.isImplementedBy(conn) all = RowSequenceSchema( self.oid_columns + self.table_schema.get_columns()) table = conn.define_table(self.table_name, all) if conn.exists(self.table_name, 'table'): if IDatabaseInitEvent.isImplementedBy(event) and event.clear_all: table.delete_rows() else: table.create() def load(self, event): raise NotImplementedError, "abstract method" def store(self, event, obj): raise NotImplementedError, "abstract method" def get_sources(self, event): return None
class SQLSecurityAttributes(SQLGatewayBase): """SQL security attribute storage""" __implements__ = SQLGatewayBase.__implements__ schema = RowSequenceSchema() schema.add('declaration_type', 'string') schema.add('role', 'string') schema.add('permission', 'string') schema.add('username', 'string') table_name = 'security' oid_columns = [ColumnSchema('oid', 'int', 0)] # Don't create a primary key def load(self, event): table = self.get_table(event) items = table.select(self.column_names, oid=event.oid) items.sort() return items, tuple(items) def store(self, event, state): table = self.get_table(event) table.set_many(event.oid, (), self.column_names, state) state = list(state) state.sort() return tuple(state)
class SQLObjectData(SQLGatewayBase): """SQL object data gateway""" __implements__ = SQLGatewayBase.__implements__ schema = ColumnSchema('data', 'string') table_name = 'object_data' table_schema = RowSequenceSchema() table_schema.add('data', 'blob', 0) def load(self, event): table = self.get_table(event) firstcol = self.column_names[:1] items = table.select(firstcol, oid=event.oid) if items: state = str(items[0][0]) else: state = '' return state, state def store(self, event, state): conn = self.get_connection(event) table = self.get_table(event) firstcol = self.column_names[:1] data = (state, ) table.set_one(event.oid, firstcol, data, event.is_new) return state
def init(self, event): conn = self.get_connection(event) all = RowSequenceSchema(self.oid_columns + self.table_schema.get_columns()) table = conn.define_table(self.table_name, all) if not conn.exists(self.table_name, 'table'): table.create()
class SQLClassification(SQLGatewayBase): __implements__ = SQLGatewayBase.__implements__ schema = ColumnSchema('classification', 'classification') table_name = 'classification' table_schema = RowSequenceSchema() table_schema.add('class_name', 'string', 0) table_schema.add('mapper_name', 'string', 0) def load(self, event): table = self.get_table(event) rows = table.select(self.column_names, oid=event.oid) classification = {} if rows: rec = rows[0] if rec[0]: classification['class_name'] = rec[0] if rec[1]: classification['mapper_name'] = rec[1] else: raise KeyError(event.oid) return classification, rec def store(self, event, classification): conn = self.get_connection(event) table = self.get_table(event) row = (classification.get('class_name', ''), classification.get('mapper_name', '')) try: table.set_one(event.oid, self.column_names, row, event.is_new) except conn.module.DatabaseError: raise OIDConflictError(event.oid) return row
class SQLItemId(SQLGatewayBase): """SQL item ID gateway. Piggybacks SQLFolderItems for init and store. Makes the assumption that the item is stored in only one place. """ __implements__ = SQLGatewayBase.__implements__ schema = ColumnSchema('id', 'string') table_name = 'folder_items' table_schema = RowSequenceSchema() table_schema.add('child_oid', 'int', 1) table_schema.add('name', 'string', 0) def init(self, event): pass def load(self, event): table = self.get_table(event) rows = table.select(('name', ), child_oid=event.oid) if len(rows) >= 1: name = rows[0][0] # Accept only the first result else: name = None # Disable conflict checking by returning None as the hash value. return name, None def store(self, event, state): return None
class SQLModTime(SQLGatewayBase): """SQL object mod time gateway""" __implements__ = SQLGatewayBase.__implements__ schema = ColumnSchema('mtime', 'int') # second table_name = 'mtime' table_schema = RowSequenceSchema() table_schema.add('mtime', 'long', 0) def load(self, event): table = self.get_table(event) items = table.select(self.column_names, oid=event.oid) if items: state = long(items[0][0]) else: state = 0L return state, state def store(self, event, state): state = long(state) table = self.get_table(event) data = (state, ) table.set_one(event.oid, self.column_names, data, event.is_new) return state
class SQLRemainder(SQLObjectData): """SQL remainder pickle gateway""" __implements__ = SQLGatewayBase.__implements__ table_name = 'remainder' table_schema = RowSequenceSchema() table_schema.add('pickle', 'blob', 0)
class ZSQLMethodPropertiesSerializer: __implements__ = ISerializer schema = RowSequenceSchema() schema.add('id', 'string', 1) schema.add('type', 'string') schema.add('data', 'string') attributes = { 'title': str, 'connection_id': str, 'max_rows_': int, 'max_cache_': int, 'cache_time': int, 'class_name_': str, 'class_file_': str, 'zclass': str, # XXX, what's that 'allow_simple_one_argument_traversal': int, 'connection_hook': str, } def can_serialize(self, obj): return isinstance(obj, SQL) def serialize(self, event): obj = event.obj assert isinstance(obj, SQL) res = [] for attribute, factory in self.attributes.items(): if not hasattr(obj, attribute): continue value = getattr(obj, attribute) t = factory.__name__ if value is None: if factory in (int, long): value = 0 else: value = '' value = str(value) event.serialized(attribute, value, 1) res.append((attribute, t, value)) event.ignore('_col') return res def deserialize(self, event, state): obj = event.obj assert isinstance(obj, SQL) for attribute, t, value in state: factory = self.attributes.get(attribute) if factory is None: continue value = factory(value) setattr(obj, attribute, value) event.deserialized(attribute, value) if not hasattr(obj, 'connection_id'): obj.connection_id = ''
def init(self, event): conn = self.get_connection(event) assert IRDBMSConnection.isImplementedBy(conn) all = RowSequenceSchema( self.oid_columns + self.table_schema.get_columns()) table = conn.define_table(self.table_name, all) if conn.exists(self.table_name, 'table'): if IDatabaseInitEvent.isImplementedBy(event) and event.clear_all: table.delete_rows() else: table.create()
def get_schema_for_class(self, module_name, class_name): """Returns the class-defined property schema. This Zope2-ism should be made pluggable later on. """ d = {} m = __import__(module_name, d, d, ('__doc__', )) klass = getattr(m, class_name) schema = RowSequenceSchema() props = getattr(klass, '_properties', ()) if not props: return None for p in props: if not safe_property_types.has_key(p['type']): # Don't store this property in its own column. # It is of a type that's hard to convert faithfully. continue prop_name = p['id'] if prop_name == 'oid': name = '_oid' else: name = prop_name schema.add(name, p['type'], 0) return schema
def get_schema_for_class(self, module_name, class_name): """Returns the class-defined property schema. This Zope2-ism should be made pluggable later on. """ d = {} m = __import__(module_name, d, d, ('__doc__',)) klass = getattr(m, class_name) schema = RowSequenceSchema() props = getattr(klass, '_properties', ()) if not props: return None for p in props: if not safe_property_types.has_key(p['type']): # Don't store this property in its own column. # It is of a type that's hard to convert faithfully. continue prop_name = p['id'] if prop_name == 'oid': name = '_oid' else: name = prop_name schema.add(name, p['type'], 0) return schema
class BTreeFolder2Items: """BTreeFolder2 items (de)serializer """ __implements__ = ISerializer schema = RowSequenceSchema() schema.add('key', 'string', 1) schema.add('oid', 'string') schema.add('classification', 'classification') def can_serialize(self, obj): return hasattr(obj, '_tree') def serialize(self, event): obj = event.obj assert self.can_serialize(obj) state = [] event.ignore('_objects') d = obj._tree event.ignore(('_tree', '_mt_index', '_count')) for id in obj.objectIds(): base = d[id] oid = event.obj_db.identify(base) if oid is None: oid = event.obj_db.new_oid() event.referenced(id, base, True, oid) # No need to pass classification. state.append((id, oid, None)) # The structure that makes up the BTree (the root node and # the buckets) are unmanaged. Tell the event about them. event.upos.extend(find_unmanaged(obj._tree, obj._tree.values())) return state def deserialize(self, event, state): obj = event.obj if hasattr(obj, '_initBTrees'): # Version 1.0.1+ of BTreeFolder2 obj._initBTrees() else: # Crufty workaround for older versions obj.__init__(obj.id) assert self.can_serialize(obj) for (id, oid, classification) in state: subob = event.resolve(id, oid, classification) obj._setOb(id, subob) # The tree and the buckets are unmanaged. event.upos.extend(find_unmanaged(obj._tree, obj._tree.values()))
class FSDirectoryItems(FSGatewayBase): """Read/write objects in a filesystem directory.""" __implements__ = IGateway schema = RowSequenceSchema() schema.add('key', 'string', 1) schema.add('oid', 'string') schema.add('classification', 'classification') def load(self, event): c = self.get_connection(event) if c.read_node_type(event.oid) != 'd': raise LoadError("Not a directory") data = list(c.read_directory(event.oid)) data.sort() # Assign OIDs to previously existing subobjects. assigned = {} for objname, child_oid in data: if child_oid is None: child_oid = event.conf.oid_gen.new_oid(event) assigned[objname] = child_oid if assigned: # Saw existing objects. Tell the connection what their OIDs are. c.assign_existing(event.oid, assigned.items()) # Return the results. res = [] hash_value = [] for objname, child_oid in data: if child_oid is None: child_oid = assigned[objname] classification = event.classify(child_oid) # Return info about each subobject. res.append((objname, child_oid, classification)) hash_value.append((objname, child_oid)) return res, tuple(hash_value) def store(self, event, state): c = self.get_connection(event) c.write_node_type(event.oid, 'd') data = [] for objname, child_oid, classification in state: data.append((objname, child_oid)) data.sort() c.write_directory(event.oid, data) return tuple(data)
class UserFolderSerializer: """Serializer for a user folder. This version lets the application keep a list of all users in RAM. """ __implements__ = ISerializer schema = RowSequenceSchema() schema.add('id', 'string', 1) schema.add('password', 'string') schema.add('roles', 'string:list') schema.add('domains', 'string:list') def can_serialize(self, obj): return isinstance(obj, UserFolder) def serialize(self, event): obj = event.obj assert isinstance(obj, UserFolder), repr(obj) state = [] event.ignore('data') for id, user in obj.data.items(): assert isinstance(user, User), repr(user) assert len(user.__dict__.keys()) == 4, user.__dict__.keys() r = list(user.roles) r.sort() d = list(user.domains) d.sort() state.append((id, user.__, tuple(r), tuple(d))) event.serialized(id, user, 0) event.upos.append(obj.data) event.upos.extend(obj.data.values()) return state def deserialize(self, event, state): obj = event.obj assert isinstance(obj, UserFolder) obj.data = PersistentMapping() for id, password, roles, domains in state: user = User(id, password, roles, domains) obj.data[id] = user event.deserialized(id, user) event.upos.append(obj.data) event.upos.extend(obj.data.values())
class FolderItems: """Zope 2 folder items (de)serializer """ __implements__ = ISerializer schema = RowSequenceSchema() schema.add('key', 'string', 1) schema.add('oid', 'string') schema.add('classification', 'classification') def can_serialize(self, obj): return isinstance(obj, ObjectManager) def serialize(self, event): obj = event.obj assert isinstance(obj, ObjectManager), repr(obj) state = [] event.ignore('_objects') d = obj.__dict__ for id in obj.objectIds(): if d.has_key(id): base = d[id] else: # Fall back to _getOb. base = aq_base(obj._getOb(id)) oid = event.obj_db.identify(base) if oid is None: oid = event.obj_db.new_oid() event.referenced(id, base, True, oid) # No need to pass classification. state.append((id, oid, None)) return state def deserialize(self, event, state): obj = event.obj assert isinstance(obj, ObjectManager), obj for (id, oid, classification) in state: subob = event.resolve(id, oid, classification) obj._setOb(id, subob) obj._objects += ({ 'id': id, 'meta_type': subob.__class__.meta_type, }, )
class FSProperties(FSGatewayBase): """Simple properties to filesystem properties annotation gateway. """ __implements__ = IGateway schema = RowSequenceSchema() schema.add('id', 'string', 1) schema.add('type', 'string') schema.add('data', 'string') def __init__(self, annotation='properties', conn_name='fs'): self.annotation = str(annotation) FSGatewayBase.__init__(self, conn_name) def load(self, event): fs_conn = self.get_connection(event) text = fs_conn.read_annotation(event.oid, self.annotation, '') res = [] if text: lines = text.split('\n') for line in lines: if '=' in line: k, v = line.split('=', 1) if ':' in k: k, t = k.split(':', 1) else: t = 'string' res.append((k, t, unescape_string(v))) res.sort() return res, tuple(res) def store(self, event, state): lines = [] for k, t, v in state: lines.append('%s:%s=%s' % (k, t, escape_string(v))) lines.sort() text = '\n'.join(lines) fs_conn = self.get_connection(event) fs_conn.write_annotation(event.oid, self.annotation, text) state = list(state) state.sort() return tuple(state)
class SecurityAttributes: """Zope 2 security attribute serializer.""" __implements__ = ISerializer schema = RowSequenceSchema() schema.add('declaration_type', 'string') schema.add('role', 'string') schema.add('permission', 'string') schema.add('username', 'string') def can_serialize(self, obj): return 1 def serialize(self, event): res = [] # Get security attributes from the instance only, not the class. # There's no need to serialize the class attributes. obj_d = event.obj.__dict__ eo = obj_d.get('_owner') if eo is not None: event.ignore('_owner') path, username = eo if '/' in username: raise ValueError, '/ not allowed in user names' s = '%s/%s' % ('/'.join(path), username) res.append(('executable-owner', '', '', s)) roles = obj_d.get('__ac_roles__') if roles is not None: event.ignore('__ac_roles__') roles = list(roles) roles.sort() class_roles = getattr(event.obj.__class__, '__ac_roles__', None) if class_roles: class_roles = list(class_roles) class_roles.sort() if roles != class_roles: for role in roles: res.append(('define-role', role, '', '')) # else inherit roles from the class local_roles = obj_d.get('__ac_local_roles__') if local_roles is not None: event.ignore('__ac_local_roles__') for username, roles in local_roles.items(): for role in roles: res.append(('local-role', role, '', username)) proxy_roles = obj_d.get('_proxy_roles') if proxy_roles is not None: event.ignore('_proxy_roles') for role in proxy_roles: res.append(('proxy-role', role, '', '')) p_dict = None for attr, value in obj_d.items(): if attr.endswith('_Permission') and attr.startswith('_'): if p_dict is None: p_dict = get_permission_dict() p = p_dict.get(attr) if p is not None: event.ignore(attr) for role in value: res.append(('permission-role', role, p, '')) # List means acquired, tuple means not acquired. if isinstance(value, TupleType): res.append(('permission-no-acquire', '', p, '')) return res def deserialize(self, event, state): local_roles = {} # { username -> [role,] } defined_roles = [] # [role,] proxy_roles = [] # [role,] permission_roles = {} # { permission -> [role,] } permission_acquired = {} # { permission -> 0 or 1 } obj = event.obj for decl_type, role, permission, username in state: if decl_type == 'executable-owner': assert not role assert not permission #assert username pos = username.rfind('/') if pos < 0: # Default to the root folder ufolder = ['acl_users'] uname = username else: ufolder = list(username[:pos].split('/')) uname = username[pos + 1:] assert ufolder assert uname obj._owner = (ufolder, uname) elif decl_type == 'local-role': #assert role assert not permission #assert username r = local_roles.get(username) if r is None: r = [] local_roles[username] = r r.append(role) elif decl_type == 'define-role': #assert role assert not permission assert not username defined_roles.append(role) elif decl_type == 'proxy-role': #assert role assert not permission assert not username proxy_roles.append(role) elif decl_type == 'permission-role': #assert role #assert permission assert not username r = permission_roles.get(permission) if r is None: r = [] permission_roles[permission] = r r.append(role) if not permission_acquired.has_key(permission): permission_acquired[permission] = 1 elif decl_type == 'permission-no-acquire': assert not role #assert permission assert not username permission_acquired[permission] = 0 else: raise ValueError, ('declaration_type %s unknown' % repr(decl_type)) if local_roles: obj.__ac_local_roles__ = local_roles if defined_roles: defined_roles.sort() obj.__ac_roles__ = tuple(defined_roles) if proxy_roles: obj._proxy_roles = tuple(proxy_roles) for p, acquired in permission_acquired.items(): roles = permission_roles.get(p, []) if not acquired: roles = tuple(roles) setattr(obj, pname(p), roles)
class PersistentMappingSerializer: """(de)serializer of a persistent mapping that uses string keys. Serializes both references and second-class persistent objects. Because of this flexibility, the schema is a little complex. """ __implements__ = ISerializer # This schema includes both a list of items that are references to # persistent objects and a pickle containing items that are not # references. schema1 = RowSequenceSchema() schema1.add('key', 'string', 1) schema1.add('oid', 'string') schema1.add('classification', 'classification') schema2 = ColumnSchema('data', 'string') schema = {'references': schema1, 'others': schema2} def can_serialize(self, obj): return isinstance(obj, PersistentMapping) def serialize(self, event): assert self.can_serialize(event.obj) refs = [] others = {} for key, value in event.obj.items(): if is_persistent(value): oid = event.obj_db.identify(value) if oid is None: oid = event.obj_db.new_oid() event.referenced(key, value, False, oid) # No need to pass classification. refs.append((key, oid, None)) else: event.serialized(key, value, False) others[key] = value event.ignore(('data', '_container')) if others: # Encode as a sorted list to preserve order. others_list = others.items() others_list.sort() s = encode_to_text(dumps(others_list, 1), others.keys()) else: s = '' return {'references': refs, 'others': s} def deserialize(self, event, state): assert self.can_serialize(event.obj) data_dict = {} s = state['others'] if s: s = decode_from_text(s) if s: data = loads(s) if hasattr(data, 'items'): # Stored as a dictionary data_list = data.items() data_dict = data else: # Stored as a sequence of tuples data_list = data for key, value in data: data_dict[key] = value for key, value in data_list: event.deserialized(key, value) for (key, oid, classification) in state['references']: value = event.resolve(key, oid, classification) data_dict[key] = value event.obj.__init__(data_dict)
class OFSProperties: """Serializer for OFS.PropertyManager properties.""" __implements__ = ISerializer schema = RowSequenceSchema() schema.add('id', 'string', 1) schema.add('type', 'string') schema.add('data', 'string') def can_serialize(self, obj): return isinstance(obj, PropertyManager) def serialize(self, event): res = [] obj = event.obj assert isinstance(obj, PropertyManager), repr(obj) assert obj._properties is obj._propertyMap() event.ignore('_properties') for p in obj._properties: name = p['id'] t = p['type'] event.ignore(name) data = obj.getProperty(name) if t == 'lines': v = '\n'.join(data) elif t == 'boolean': v = data and '1' or '0' elif string_repr_types.get(t): v = str(data) else: # Pickle the value and any extra info about the property. # Extra info is present in select and multi-select properties. d = p.copy() del d['id'] del d['type'] if d.has_key('mode'): del d['mode'] d['value'] = data v = dumps(d) res.append((name, t, v)) return res def deserialize(self, event, state): obj = event.obj assert isinstance(obj, PropertyManager) assert obj._properties is obj._propertyMap() if not state: # No stored properties. Revert the object to its # class-defined property schema. if obj.__dict__.has_key('_properties'): del obj._properties return old_props = obj.propdict() new_props = {} for id, t, v in state: p = old_props.get(id) if p is None: p = {'mode': 'wd'} else: p = p.copy() p['id'] = id p['type'] = t if v and not string_repr_types.get(t) and t != 'lines': # v is a pickle. # Check the pickle for extra property info. d = loads(v) if isinstance(d, DictType): del d['value'] if d: # The data is stored with extra property info. p.update(d) new_props[id] = p if old_props != new_props: obj._properties = tuple(new_props.values()) for id, t, v in state: if t == 'lines': data = v.split('\n') elif t == 'boolean': # match 0, [f]alse, [n]o if (not v or v == '0' or v[:1].lower() in 'fn'): data = 0 else: data = 1 elif string_repr_types.get(t): data = str(v) elif v: d = loads(v) if isinstance(d, DictType): # The data is stored with extra property info. data = d['value'] else: data = d else: # Fall back to a default. data = '' obj._updateProperty(id, data)
class SQLUserList(SQLGatewayBase): """Stores and retrieves all users for a folder at once.""" __implements__ = SQLGatewayBase.__implements__ schema = RowSequenceSchema() schema.add('id', 'string', 1) schema.add('password', 'string') schema.add('roles', 'string:list') schema.add('domains', 'string:list') table_defs = { 'users': [('oid', 'int', 1), ('id', 'string', 1), ('password', 'string', 0)], 'user_roles': [('oid', 'int', 0), ('id', 'string', 0), ('role', 'string', 0)], 'user_domains': [('oid', 'int', 0), ('id', 'string', 0), ('domain', 'string', 0)], } def init(self, event): conn = self.get_connection(event) for table_name, columns in self.table_defs.items(): table_schema = RowSequenceSchema() for args in columns: table_schema.add(*args) table = conn.define_table(table_name, table_schema) if not conn.exists(table_name, 'table'): table.create() elif event.clear_all: table.delete_rows() def load(self, event): conn = self.get_connection(event) rows = conn.get_table('users').select(('id', 'password'), oid=event.oid) data = {} for id, password in rows: data[id] = (password, [], []) rows = conn.get_table('user_roles').select(('id', 'role'), oid=event.oid) for id, role in rows: row = data.get(id) if row is not None: row[1].append(role) rows = conn.get_table('user_domains').select(('id', 'domain'), oid=event.oid) for id, domain in rows: row = data.get(id) if row is not None: row[2].append(domain) records = [] for id, (password, roles, domains) in data.items(): roles = list(roles) roles.sort() domains = list(domains) domains.sort() records.append((id, password, tuple(roles), tuple(domains))) records.sort() return records, tuple(records) def store(self, event, state): oid = event.oid conn = self.get_connection(event) rows = [(id, pw) for id, pw, roles, domains in state] conn.get_table('users').set_many(event.oid, (), ( 'id', 'password', ), rows) roles_d = {} domains_d = {} for id, pw, roles, domains in state: for role in roles: roles_d[(id, role)] = 1 for domain in domains: domains_d[(id, domain)] = 1 conn.get_table('user_roles').set_many(event.oid, (), ( 'id', 'role', ), roles_d.keys()) conn.get_table('user_domains').set_many(event.oid, (), ( 'id', 'domain', ), domains_d.keys()) state = list(state) state.sort() return tuple(state)
class FSSecurityAttributes(FSGatewayBase): """Gateway for storing security attributes.""" __implements__ = IGateway schema = RowSequenceSchema() schema.add('declaration_type', 'string') schema.add('role', 'string') schema.add('permission', 'string') schema.add('username', 'string') def __init__(self, annotation='security', conn_name='fs'): self.annotation = annotation FSGatewayBase.__init__(self, conn_name) def load(self, event): fs_conn = self.get_connection(event) text = fs_conn.read_annotation(event.oid, self.annotation, '') res = [] if text: lines = text.split('\n') for line in lines: line = line.strip() if not line or line.startswith('#'): continue params = string_to_params(line) if params: decl_type = params[0][0] row = [decl_type, '', '', ''] for k, v in params[1:]: k = k.lower() if '_' in k: # temporary backward compatibility k = k.split('_', 1)[0] if k == 'role': row[1] = v elif k == 'permission': row[2] = v elif k == 'username': row[3] = v else: raise ValueError( "Could not read security declaration " "%s for %s" % (repr(line), repr(event.oid))) res.append(tuple(row)) res.sort() return res, tuple(res) def store(self, event, state): lines = [] for d, r, p, u in state: params = [(d, '')] if r: params.append(('role', r)) if p: params.append(('permission', p)) if u: params.append(('username', u)) s = params_to_string(params) lines.append(s) if lines: lines.sort() text = '\n'.join(lines) fs_conn = self.get_connection(event) fs_conn.write_annotation(event.oid, self.annotation, text) state = list(state) state.sort() return tuple(state)
class FSUserList(FSGatewayBase): """User list gateway, where the user list is stored in a flat file.""" __implements__ = IGateway schema = RowSequenceSchema() schema.add('id', 'string', 1) schema.add('password', 'string') schema.add('roles', 'string:list') schema.add('domains', 'string:list') def load(self, event): c = self.get_connection(event) assert c.read_node_type(event.oid) == 'f' text = c.read_data(event.oid) res = [] for line in text.split('\n'): L = line.strip() if not L.startswith('#') and ':' in L: id, password, rolelist, domainlist = L.split(':', 3) roles = self._split_list(rolelist) domains = self._split_list(domainlist) res.append((id, password, roles, domains)) res.sort() return res, text def _split_list(self, s): return tuple([item.strip() for item in s.split(',') if item]) def _join_list(self, items): for item in items: if item.strip() != item: raise MappingError( "Leading and trailing whitespace are not allowed " "in roles and domains") item = item.strip() if not item: raise MappingError("Empty role or domain not allowed") if ',' in item or ':' in item or '\n' in item: raise MappingError( "Commas, colons, and newlines are not allowed " "in roles and domains") return ','.join(items) def store(self, event, state): replace_lines = {} for id, password, roles, domains in state: if ':' in id or '\n' in id: raise MappingError('User IDs cannot have colons or newlines') if id.startswith('#'): raise MappingError('User IDs cannot start with #') if ':' in password or '\n' in password: raise MappingError('Passwords cannot have colons or newlines') rolelist = self._join_list(roles) domainlist = self._join_list(domains) to_write = '%s:%s:%s:%s' % (id, password, rolelist, domainlist) replace_lines[id] = to_write oid = event.oid fs_conn = self.get_connection(event) fs_conn.write_node_type(oid, 'f') # Read the existing text only to maintain the current order. text = fs_conn.read_data(oid, allow_missing=1) if text is None: text = '' new_lines = [] # Replace / remove users for line in text.split('\n'): L = line.strip() if not L.startswith('#'): if ':' in L: name, stuff = L.split(':', 1) replace = replace_lines.get(name, '') if replace and replace != L: new_lines.append(replace) del replace_lines[name] # else remove the line else: new_lines.append(line) # Append new users for line in replace_lines.values(): new_lines.append(line) # Write it text = '\n'.join(new_lines) fs_conn.write_data(oid, text) serial = list(state) serial.sort() return text
class SQLMultiTableProperties(SQLGatewayBase): """Combines fixed and variable properties. """ __implements__ = IGateway, IDatabaseInitializer schema = SQLProperties.schema table_name = 'property_tables' table_schema = RowSequenceSchema() table_schema.add('class_name', 'string', 1) table_schema.add('table_name', 'string', 0) oid_columns = [] # No OID column def __init__(self, conn_name='db'): self.var_props = SQLProperties(conn_name=conn_name) self.fixed_props = {} # class name -> SQLFixedProperties instance SQLGatewayBase.__init__(self, conn_name) def get_sources(self, event): return None def init(self, event): conn = self.get_connection(event) table = conn.define_table(self.table_name, self.table_schema) if not conn.exists(self.table_name, 'table'): table.create() self.var_props.init(event) if event.clear_all: # Clear the fixed property tables. recs = table.select(('table_name', )) for (name, ) in recs: conn.clear_table(name) self.fixed_props = {} def get_schema_for_class(self, module_name, class_name): """Returns the class-defined property schema. This Zope2-ism should be made pluggable later on. """ d = {} m = __import__(module_name, d, d, ('__doc__', )) klass = getattr(m, class_name) schema = RowSequenceSchema() props = getattr(klass, '_properties', ()) if not props: return None for p in props: if not safe_property_types.has_key(p['type']): # Don't store this property in its own column. # It is of a type that's hard to convert faithfully. continue prop_name = p['id'] if prop_name == 'oid': name = '_oid' else: name = prop_name schema.add(name, p['type'], 0) return schema def get_fixed_props(self, event): """Returns a SQLFixedProperties instance or None. """ classification = event.classification if classification is None: return None cn = classification.get('class_name') if cn is None: return None if self.fixed_props.has_key(cn): return self.fixed_props[cn] # May be None # Gather info about the class pos = cn.rfind('.') if pos < 0: raise ValueError, "Not a qualified class name: %s" % repr(cn) module_name = cn[:pos] class_name = cn[pos + 1:] schema = self.get_schema_for_class(module_name, class_name) if schema is None or not schema.get_columns(): # No fixed properties exist for this class. self.fixed_props[cn] = None return None # Allocate a table name conn = self.get_connection(event) table = self.get_table(event) rows = table.select(('table_name', ), class_name=cn) if rows: table_name = rows[0][0] else: attempt = 0 while 1: # Find an available table name. table_name = '%s_properties' % (class_name[:16]) if attempt: table_name += '_%02d' % attempt if not conn.exists(table_name, 'table'): break attempt += 1 table.insert(('class_name', 'table_name'), (cn, table_name)) # Create the fixed properties and table fp = SQLFixedProperties(self.conn_name, table_name, schema) fp.init(event) # XXX If the transaction gets aborted, the table creation will # be undone, but self.fixed_props won't see the change. # Perhaps we need to reset self.fixed_props on abort. self.fixed_props[cn] = fp return fp def load(self, event): """Returns a combination of states from two tables.""" var_state, var_hash = self.var_props.load(event) fp = self.get_fixed_props(event) if fp is None: return var_state, var_hash fixed_state, fixed_hash = fp.load(event) # Merge fixed_state and var_state, letting fixed_state # override var_state except when the value in fixed_state is # None. res = [] placement = {} # property name -> placement in results for rec in fixed_state: placement[rec[0]] = len(res) res.append(rec) for rec in var_state: index = placement.get(rec[0]) if index is None: res.append(rec) elif res[index][2] is None: # override the fixed value, since it was None. res[index] = rec return res, (fixed_hash, var_hash) def store(self, event, state): """Stores state in two tables.""" fp = self.get_fixed_props(event) if fp is None: return self.var_props.store(event, state) # Store the fixed state first and find out what got left over. leftover = {} state = list(state) state.sort() fixed_hash = fp.store(event, state, leftover=leftover) if leftover: var_state = [] for prop_name, (typ, value) in leftover.items(): var_state.append((prop_name, typ, value)) var_hash = self.var_props.store(event, var_state) else: var_hash = () return (fixed_hash, var_hash)