def ValidateRowDict(self, table_name, row_dict, none_ok=False, all_none_ok=False): """Checks row dictionaries for correctness in reference to know data types and column names in the coresponding table. Input: table_name: string of table name row_dict: dict of row none_ok: bool of allowance of None as a value in the dict all_none_ok: bool of allowance of None as every value in the dict Raises: UnexpectedDataError: Missing key in dictionary UnexpectedDataError: Dictionary has extra key that is not used. FunctionError: No Function to check data type UnexpectedDataError: Invalid data type UnexpectedDataError: Need to fill out at least one value in dict """ main_dict = helpers_lib.GetRowDict(table_name) for key in main_dict.iterkeys(): if (key not in row_dict): raise errors.UnexpectedDataError( 'Missing key %s in dictionary' % key) for key, value in row_dict.iteritems(): if (key not in main_dict): raise errors.UnexpectedDataError( 'Dictionary has extra key that is not ' 'used: %s' % key) if (not 'is%s' % main_dict[key] in dir(self)): raise errors.FunctionError('No function to check data ' 'type: %s' % main_dict[key]) if (not getattr(self, 'is%s' % main_dict[key])(value)): if ((not none_ok and not key.endswith('_id')) or (none_ok and value is not None)): raise errors.UnexpectedDataError( 'Invalid data type %s for %s: %s' % (main_dict[key], key, value)) if (none_ok and not all_none_ok): for value in row_dict.values(): if (value is not None): return raise errors.UnexpectedDataError( 'Need to fill out at least one value ' 'in dict')
def ValidateRecordArgsDict(self, record_type, record_args_dict, none_ok=False): """Type checks record args dynamically. Inputs: record_type: string of record_type record_args_dict: dictionary for args keyed by arg name. a filled out dict from GetEmptyRecordArgsDict() none_ok: boolean of if None types should be acepted. Raises: InvalidInputError: dict for record type should have these keys FucntionError: No function to check data type UnexpectedDataError: Invalid data type """ record_type_dict = self.GetRecordArgsDict(record_type) if (not set(record_type_dict.keys()) == set(record_args_dict.keys())): raise errors.InvalidInputError( 'dict for record type %s should have ' 'these keys: %s' % (record_type, record_type_dict)) if (self.data_validation_instance is None): self.InitDataValidation() data_validation_methods = dir(data_validation.DataValidation([], [])) for record_arg_name in record_args_dict.keys(): if (not 'is%s' % record_type_dict[record_arg_name] in data_validation_methods): raise errors.FucntionError( 'No function to check data type %s' % record_type_dict[record_arg_name]) if (none_ok and record_args_dict[record_arg_name] is None): continue if (not getattr(self.data_validation_instance, 'is%s' % record_type_dict[record_arg_name])( record_args_dict[record_arg_name])): raise errors.UnexpectedDataError( 'Invalid data type %s: %s' % (record_type_dict[record_arg_name], record_args_dict[record_arg_name]))
def CreateRecordsFromZoneObject(zone_object, zone_name=None, view_name=None, zone_origin=None, views_list=None): """Creates a list of record dictionaries from a dns.zone object Inputs: zone_object: a dns.zone object zone_origin: a string of the zone origin zone_name: string of zone name view_name: string of view name views_list: list of view dictionaries. Note: This is only necessary if being called by AddFormattedRecords. Output: make_record_args_list: list of dictionaries of records""" if (zone_origin is None): zone_origin = str(zone_object.origin) make_record_args_list = [] for record_tuple in zone_object.items(): record_target = unicode(record_tuple[0]) for record_set in record_tuple[1].rdatasets: ttl = record_set.ttl for record_object in record_set.items: if (record_object.rdtype == dns.rdatatype.PTR): record_type = u'ptr' assignment_host = FixHostname(unicode(record_object), zone_origin) record_args_dict = {u'assignment_host': assignment_host} elif (record_object.rdtype == dns.rdatatype.A): record_type = u'a' record_args_dict = { u'assignment_ip': unicode(record_object) } elif (record_object.rdtype == dns.rdatatype.AAAA): record_type = u'aaaa' record_args_dict = { u'assignment_ip': unicode(IPy.IP(str(record_object)).strFullsize()) } elif (record_object.rdtype == dns.rdatatype.CNAME): record_type = u'cname' assignment_host = FixHostname(unicode(record_object), zone_origin) record_args_dict = {u'assignment_host': assignment_host} elif (record_object.rdtype == dns.rdatatype.HINFO): record_type = u'hinfo' record_args_dict = { u'hardware': unicode(record_object.cpu), u'os': unicode(record_object.os) } elif (record_object.rdtype == dns.rdatatype.TXT): record_type = u'txt' record_args_dict = {u'quoted_text': unicode(record_object)} elif (record_object.rdtype == dns.rdatatype.MX): record_type = u'mx' mail_server = FixHostname(unicode(record_object.exchange), zone_origin) record_args_dict = { u'priority': record_object.preference, u'mail_server': mail_server } elif (record_object.rdtype == dns.rdatatype.NS): record_type = u'ns' name_server = FixHostname(unicode(record_object), zone_origin) record_args_dict = {u'name_server': name_server} elif (record_object.rdtype == dns.rdatatype.SRV): record_type = u'srv' assignment_host = FixHostname( unicode(record_object.target), zone_origin) record_args_dict = { u'priority': record_object.priority, u'weight': record_object.weight, u'port': record_object.port, u'assignment_host': assignment_host } elif (record_object.rdtype == dns.rdatatype.SOA): record_type = u'soa' name_server = FixHostname(unicode(record_object.mname), zone_origin) admin_email = FixHostname(unicode(record_object.rname), zone_origin) record_args_dict = { u'name_server': name_server, u'admin_email': admin_email, u'serial_number': record_object.serial, u'retry_seconds': record_object.retry, u'refresh_seconds': record_object.refresh, u'expiry_seconds': record_object.expire, u'minimum_seconds': record_object.minimum } else: raise errors.UnexpectedDataError( 'Unkown record type: %s.\n %s' % (dns.rdatatype.to_text( record_object.rdtype), record_object)) if (record_object.rdtype == dns.rdatatype.SOA and view_name == u'any' and views_list): for single_view in views_list: if (single_view != u'any'): make_record_args_list.append({ u'record_type': record_type, u'record_target': record_target, u'record_zone_name': zone_name, u'record_arguments': record_args_dict, u'record_view_dependency': single_view, u'ttl': ttl }) else: make_record_args_list.append({ u'record_type': record_type, u'record_target': record_target, u'record_zone_name': zone_name, u'record_arguments': record_args_dict, u'record_view_dependency': view_name, u'ttl': ttl }) return make_record_args_list
def ListRow(self, *args, **kwargs): """Lists rows in the database using a dictionary of tables. Then returns the rows found. Joins are auto generated on the fly based on foreign keys in the database. Inputs: args: pairs of string of table name and dict of rows kwargs: lock_rows: default False column: column to search range on, if using multiple tables, the column must be in the first table in args. range_values: range tuple of values to search within for on column is_date: boolean of if range is of dates example usage: ListRow('users', user_row_dict, 'user_group_assignments', user_assign_row_dict, lock_rows=True) Raises: TransactionError: Must run StartTansaction before inserting UnexpectedDataError: If is_date is specified you must specify column and range UnexpectedDataError: If column or range is specified both are needed InvalidInputError: Found unknown option(s) UnexpectedDataError: No args given, must at least have a pair of table name and row dict UnexpectedDataError: Number of unnamed args is not even. Args should be entered in pairs of table name and row dict. InvalidInputError: Table name not valid InvalidInputError: Column not found in row UnexpectedDataError: Column in table is not a DateTime type UnexpectedDataError: Date from range is not a valid datetime object InvalidInputError: Range must be int if is_date is not set InvalidInputError: Multiple tables were passed in but no joins were found Outputs: tuple of row dicts consisting of all the tables that were in the input. all column names in the db are unique so no colisions occour example: ({'user_name': 'sharrell', 'access_level': 10, 'user_group_assignments_group_name: 'cs', 'user_group_assignments_user_name: 'sharrell'}, {'user_name': 'sharrell', 'access_level': 10, 'user_group_assignments_group_name: 'eas', 'user_group_assignments_user_name: 'sharrell'}) """ if (not self.transaction_init): raise errors.TransactionError( 'Must run StartTansaction before getting ' 'data.') if (self.data_validation_instance is None): self.InitDataValidation() valid_tables = helpers_lib.GetValidTables() tables = {} table_names = [] lock_rows = False column = None range_values = () is_date = None if (kwargs): if ('lock_rows' in kwargs): lock_rows = kwargs['lock_rows'] del kwargs['lock_rows'] if ('column' in kwargs): column = kwargs['column'] del kwargs['column'] if ('range_values' in kwargs): range_values = kwargs['range_values'] del kwargs['range_values'] if ('is_date' in kwargs): is_date = kwargs['is_date'] del kwargs['is_date'] if (column is None and is_date is not None): raise errors.UnexpectedDataError( 'If is_date is specified you must ' 'specify column and range') if (bool(column) ^ bool(range_values)): raise errors.UnexpectedDataError( 'If column or range is specified ' 'both are needed') if (kwargs): raise errors.InvalidInputError('Found unknown option(s): ' '%s' % kwargs.keys()) if (not args): raise errors.UnexpectedDataError( 'No args given, must at least have a ' 'pair of table name and row dict') if (len(args) % 2): raise errors.UnexpectedDataError( 'Number of unnamed args is not even. Args ' 'should be entered in pairs of table name ' 'and row dict.') count = 0 for arg in args: count += 1 if (count % 2): if (not arg in valid_tables): raise errors.InvalidInputError('Table name not valid: %s' % arg) current_table_name = arg else: # do checking in validate row dict to check if it is a dict self.data_validation_instance.ValidateRowDict( current_table_name, arg, none_ok=True, all_none_ok=True) tables[current_table_name] = arg table_names.append(current_table_name) if (range_values): if (column not in args[1]): raise errors.InvalidInputError('Column %s not found in row' 'dictionary: %s' % (column, args[1])) if (is_date): if (constants.TABLES[args[0]][column] != 'DateTime'): raise errors.UnexpectedDataError( 'column: %s in table %s is not a' 'DateTime type' % (column, args[0])) for date in range_values: if (not self.data_validation_instance.isDateTime(date)): raise errors.UnexpectedDataError( 'Date: %s from range is not a valid ' 'datetime object' % date) else: for value in range_values: if (not self.data_validation_instance.isUnsignedInt(value) ): raise errors.InvalidInputError( 'Range must be int if is_date ' 'is not set') query_where = [] if (len(tables) > 1): if (not self.foreign_keys): self.cursor_execute( 'SELECT table_name, column_name, ' 'referenced_table_name, referenced_column_name ' 'FROM information_schema.key_column_usage WHERE ' 'referenced_table_name IS NOT NULL AND ' 'referenced_table_schema="%s"' % self.db_name) self.foreign_keys = self.cursor.fetchall() for key in self.foreign_keys: if (key['table_name'] in table_names and key['referenced_table_name'] in table_names): query_where.append('(%(table_name)s.%(column_name)s=' '%(referenced_table_name)s.' '%(referenced_column_name)s)' % key) if (not query_where): raise errors.InvalidInputError( 'Multiple tables were passed in but no ' 'joins were found') column_names = [] search_dict = {} for table_name, row_dict in tables.iteritems(): for key, value in row_dict.iteritems(): column_names.append('%s.%s' % (table_name, key)) if (value is not None): search_dict[key] = value query_where.append('%s%s%s%s' % (key, '=%(', key, ')s')) if (range_values): search_dict['start'] = range_values[0] search_dict['end'] = range_values[1] query_where.append( '%s%s%s%s' % (column, '>=%(start)s AND ', column, '<=%(end)s')) query_end = '' if (query_where): query_end = 'WHERE %s' % ' AND '.join(query_where) if (lock_rows): query_end = '%s FOR UPDATE' % query_end query = 'SELECT %s FROM %s %s' % (','.join(column_names), ','.join(table_names), query_end) self.cursor_execute(query, search_dict) return self.cursor.fetchall()
def Authorize(self, method, record_data=None, current_transaction=False): """Check to see if the user is authorized to run the given operation. Inputs: method: what the user's trying to do record_data: dictionary of target, zone_name, view_name, record_type, and record_args_dict for the record that is being modified. {'target': 'test_target', 'zone_name': 'test_zone', 'view_name': 'test_view', 'record_type': 'a', 'record_args_dict' : { u'assignment_ip' : '192.168.1.1' } } current_transaction: bool of if this function is run from inside a transaction in the db_access class Raises: MaintenanceError: Roster is currently under maintenance. MissingDataTypeError: Incomplete record data provided for access method. AuthorizationError: Authorization failure. """ function_name, current_args = helpers_lib.GetFunctionNameAndArgs() if( not current_transaction ): self.db_instance.StartTransaction() try: maintenance_mode = self.db_instance.CheckMaintenanceFlag() if( record_data and record_data.has_key('zone_name') ): if( record_data['zone_name'] ): pulled_origin = self.db_instance.GetZoneOrigins( record_data['zone_name'], record_data['view_name']) #Making sure we pulled something that exists if( pulled_origin is not None ): self.zone_origin_cache[record_data['zone_name']] = pulled_origin[ record_data['zone_name']] else: view_name = record_data['view_name'] if( view_name.endswith('_dep') ): view_name = view_name[:-4] #Strip off '_dep' raise errors.UnexpectedDataError('Specified zone-view assignment ' 'does not exist for zone %s view %s' % ( record_data['zone_name'], view_name)) finally: if( not current_transaction ): self.db_instance.EndTransaction() if( maintenance_mode and self.user_perms['user_access_level'] != constants.ACCESS_LEVELS['dns_admin'] ): raise errors.MaintenanceError('Roster is currently under maintenance.') if( record_data is not None and record_data.has_key('zone_name') ): target_string = ' with %s on %s of type %s' % (record_data['target'], record_data['zone_name'], record_data['record_type']) user_group_perms = {} record_target = record_data['target'] # Get user group permissions for record zone if( record_data['zone_name'] ): for origin in self.zone_origin_cache[record_data['zone_name']]: user_group_perms[origin] = [] if( record_target == u'@' ): ip_address = helpers_lib.UnReverseIP(origin) else: ip_address = helpers_lib.UnReverseIP('%s.%s' % ( record_target, origin)) #Looking for permissions in the forward zones for zone in self.user_perms['forward_zones']: if( zone['zone_name'] == record_data['zone_name'] ): user_group_perms[origin].append(zone['group_permission']) #If we haven't found any, look in the reverse ranges if( user_group_perms[origin] == [] ): validation_instance = self.db_instance.data_validation_instance if( validation_instance.isIPv4IPAddress(ip_address) or validation_instance.isIPv6IPAddress(ip_address) ): for cidr in self.user_perms['reverse_ranges']: if( IPy.IP(cidr['cidr_block']).overlaps(ip_address) ): user_group_perms[origin].append(cidr['group_permission']) else: target_string = '' auth_fail_string = ('User %s is not allowed to use %s%s' % (self.user_name, method, target_string)) #authorizing method if( self.abilities.has_key(method) ): method_hash = self.abilities[method] if( int(self.user_access_level) >= constants.ACCESS_LEVELS['dns_admin'] ): return if( method_hash['check'] ): # Secondary check - ensure the target is in a range delegated to # the user if( record_data is None ): raise errors.MissingDataTypeError( 'No record data provided for access method ' '%s' % method) elif( not record_data.has_key('zone_name') or record_data['zone_name'] is None or not record_data.has_key('view_name') or record_data['view_name'] is None or not record_data.has_key('target') or record_data['target'] is None or not record_data.has_key('record_type') or record_data['record_type'] is None or not record_data.has_key('record_args_dict') or record_data['record_args_dict'] is None ): raise errors.MissingDataTypeError( 'Incomplete record data provided for access ' 'method %s' % method) # If user or less, check permission for zone origin elif( int(self.user_access_level) <= constants.ACCESS_LEVELS[ 'unlocked_user'] ): user_has_permission = False for origin in self.zone_origin_cache[record_data['zone_name']]: if( record_data['record_type'] in user_group_perms[origin] ): user_has_permission = True if( not user_has_permission ): raise errors.AuthorizationError(auth_fail_string) if( int(self.user_access_level) < constants.ACCESS_LEVELS['unlocked_user'] ): # if a or aaaa if( record_data['record_args_dict'].has_key(u'assignment_ip') ): ip = IPy.IP(record_data['record_args_dict'][u'assignment_ip']) for reverse_range in self.reverse_ranges: if( IPy.IP(reverse_range['cidr_block']).overlaps(ip) ): break else: raise errors.AuthorizationError(auth_fail_string) # if cname, mx, ns, or ptr elif( record_data['record_args_dict'].has_key(u'assignment_host') or record_data['record_args_dict'].has_key(u'mail_server') or record_data['record_args_dict'].has_key(u'name_server') ): hostname = None if( record_data['record_args_dict'].has_key(u'assignment_host') ): hostname = record_data['record_args_dict'][u'assignment_host'] elif( record_data['record_args_dict'].has_key(u'mail_server') ): hostname = record_data['record_args_dict'][u'mail_server'] elif( record_data['record_args_dict'].has_key(u'name_server') ): hostname = record_data['record_args_dict'][u'name_server'] smallest_zone = hostname found = False while( smallest_zone ): for domain in self.zone_origin_cache: for origin in self.zone_origin_cache[domain]: if( smallest_zone == origin ): found = True if( not found ): try: smallest_zone = smallest_zone.split('.', 1)[1] except IndexError: break else: break if( not found ): raise errors.AuthorizationError(auth_fail_string) for zone in self.forward_zones: if( self.zone_origin_cache.has_key(zone['zone_name']) ): found = False for origin in self.zone_origin_cache[zone['zone_name']]: if( smallest_zone == origin ): found = True break if( found ): break else: raise errors.AuthorizationError(auth_fail_string) for forward_zone in self.forward_zones: if( record_data['zone_name'] == forward_zone['zone_name'] ): return # Can't find it in forward zones, maybe it's a reverse try: ip = IPy.IP(ip_address) # Good, we have an IP. See if we hit any delegated ranges. for reverse_range in self.reverse_ranges: if( IPy.IP(reverse_range['cidr_block']).overlaps(ip) ): return # fail to find a matching IP range with appropriate perms self.log_instance.LogAction(self.user_name, function_name, current_args, False, current_transaction) raise errors.AuthorizationError(auth_fail_string) except ValueError: # fail to find a matching zone with appropriate perms self.log_instance.LogAction(self.user_name, function_name, current_args, False, current_transaction) raise errors.AuthorizationError(auth_fail_string) else: return else: # fail to find a matching method self.log_instance.LogAction(self.user_name, function_name, current_args, False, current_transaction) raise errors.AuthorizationError(auth_fail_string)