diff options
author | Vishwanatha Subbanna <vishwa@linux.vnet.ibm.com> | 2016-09-21 15:49:26 +0530 |
---|---|---|
committer | Patrick Williams <patrick@stwcx.xyz> | 2016-09-27 18:42:55 +0000 |
commit | 5b090c61086f196fbaa9279ae7728d3680f52e43 (patch) | |
tree | 60e7a49d53e282c9a84b1f25397a1c240bc69050 | |
parent | d5b7e9385bd62dddaf765810c16f68ea2a1c6324 (diff) | |
download | phosphor-settingsd-5b090c61086f196fbaa9279ae7728d3680f52e43.tar.gz phosphor-settingsd-5b090c61086f196fbaa9279ae7728d3680f52e43.zip |
Validate user inputs in settings daemon
Currently, the properties defined under /org/openbmc/settings/host0 can take any
value and it is upto the consumer daemons to act only if the valid data was
given.
This patch will provide a validation logic for all the properties and will raise
an exception in the case of invalid inputs. Only on a valid input, will the data
gets written and saved.
Validation methods and types per property are provided in the configuration file
and used by the manager whenever a particular property is changed.
Change-Id: I0731ce6e00ab3cb4e11deb98c03fda8d5adad913
Signed-off-by: Vishwanatha Subbanna <vishwa@linux.vnet.ibm.com>
-rw-r--r-- | settings.yaml | 29 | ||||
-rw-r--r-- | settings_file.py | 91 | ||||
-rw-r--r-- | settings_manager.py | 101 |
3 files changed, 185 insertions, 36 deletions
diff --git a/settings.yaml b/settings.yaml index 9e08e29..ca26840 100644 --- a/settings.yaml +++ b/settings.yaml @@ -1,6 +1,6 @@ --- # Settings Config File -host: +org.openbmc.settings.Host: powercap: name: power_cap type: i @@ -8,39 +8,66 @@ host: min: 0 max: 1000 unit: watts + validation: range bootflags: name: boot_flags type: s default: "default" + validation: list + allowed: ["Network", "Disk", "Safe", "CDROM", "Setup", "default"] sysstate: name: system_state type: s default: "" + validation: None powerpolicy: name: power_policy type: s default: "RESTORE_LAST_STATE" + validation: list + allowed: ["ALWAYS_POWER_ON", "RESTORE_LAST_STATE", "LEAVE_OFF"] restrictedmode: name: restricted_mode type: b default: false + min: 0 + max: 1 + validation: range bootpolicy: name: boot_policy type: s default: "ONETIME" + validation: list + allowed: ["ONETIME", "PERMANENT"] networkconfig: name: network_config type: s default: "ipaddress=,prefix=,gateway=,mac=,addr_type=" + validation: custom + method: validate_net_config TimeMode: name: time_mode type: s default: "NTP" + validation: list + allowed: ["NTP", "MANUAL"] TimeOwner: name: time_owner type: s default: "BMC" + validation: list + allowed: ["BMC", "HOST", "SPLIT", "BOTH"] UseDhcpNtp: name: use_dhcp_ntp type: s default: "yes" + validation: list + allowed: ["yes", "no"] + +# Example of using regex +# macaddress: +# name: mac_address +# type: s +# default: "aa:bb:cc:dd:ee:ff" +# validation: regex +# regex: '([a-fA-F0-9]{2}[:|\-]?){6}' diff --git a/settings_file.py b/settings_file.py index ced0d5e..f0c6d13 100644 --- a/settings_file.py +++ b/settings_file.py @@ -1,30 +1,65 @@ #!/usr/bin/python -u SETTINGS=\ -{'host': {'TimeMode': {'default': 'NTP', 'name': 'time_mode', 'type': 's'}, - 'TimeOwner': {'default': 'BMC', 'name': 'time_owner', 'type': 's'}, - 'UseDhcpNtp': {'default': 'yes', - 'name': 'use_dhcp_ntp', - 'type': 's'}, - 'bootflags': {'default': 'default', - 'name': 'boot_flags', - 'type': 's'}, - 'bootpolicy': {'default': 'ONETIME', - 'name': 'boot_policy', - 'type': 's'}, - 'networkconfig': {'default': - 'ipaddress=,prefix=,gateway=,mac=,addr_type=', - 'name': 'network_config', - 'type': 's'}, - 'powercap': {'default': 0, - 'max': 1000, - 'min': 0, - 'name': 'power_cap', - 'type': 'i', - 'unit': 'watts'}, - 'powerpolicy': {'default': 'RESTORE_LAST_STATE', - 'name': 'power_policy', - 'type': 's'}, - 'restrictedmode': {'default': False, - 'name': 'restricted_mode', - 'type': 'b'}, - 'sysstate': {'default': '', 'name': 'system_state', 'type': 's'}}} +{'org.openbmc.settings.Host': {'TimeMode': {'allowed': ['NTP', 'MANUAL'], + 'default': 'NTP', + 'name': 'time_mode', + 'type': 's', + 'validation': 'list'}, + 'TimeOwner': {'allowed': ['BMC', + 'HOST', + 'SPLIT', + 'BOTH'], + 'default': 'BMC', + 'name': 'time_owner', + 'type': 's', + 'validation': 'list'}, + 'UseDhcpNtp': {'allowed': ['yes', 'no'], + 'default': 'yes', + 'name': 'use_dhcp_ntp', + 'type': 's', + 'validation': 'list'}, + 'bootflags': {'allowed': ['Network', + 'Disk', + 'Safe', + 'CDROM', + 'Setup', + 'default'], + 'default': 'default', + 'name': 'boot_flags', + 'type': 's', + 'validation': 'list'}, + 'bootpolicy': {'allowed': ['ONETIME', + 'PERMANENT'], + 'default': 'ONETIME', + 'name': 'boot_policy', + 'type': 's', + 'validation': 'list'}, + 'networkconfig': {'default': 'ipaddress=,prefix=,gateway=,mac=,addr_type=', + 'method': 'validate_net_config', + 'name': 'network_config', + 'type': 's', + 'validation': 'custom'}, + 'powercap': {'default': 0, + 'max': 1000, + 'min': 0, + 'name': 'power_cap', + 'type': 'i', + 'unit': 'watts', + 'validation': 'range'}, + 'powerpolicy': {'allowed': ['ALWAYS_POWER_ON', + 'RESTORE_LAST_STATE', + 'LEAVE_OFF'], + 'default': 'RESTORE_LAST_STATE', + 'name': 'power_policy', + 'type': 's', + 'validation': 'list'}, + 'restrictedmode': {'default': False, + 'max': 1, + 'min': 0, + 'name': 'restricted_mode', + 'type': 'b', + 'validation': 'range'}, + 'sysstate': {'default': '', + 'name': 'system_state', + 'type': 's', + 'validation': 'None'}}} diff --git a/settings_manager.py b/settings_manager.py index 864ee9d..997c56f 100644 --- a/settings_manager.py +++ b/settings_manager.py @@ -8,10 +8,12 @@ import os import os.path as path import sys from obmc.dbuslib.bindings import DbusProperties, get_dbus +from IPy import IP settings_file_path = os.path.join(sys.prefix, 'share/obmc-phosphor-settings') sys.path.insert(1, settings_file_path) import settings_file as s +import re DBUS_NAME = 'org.openbmc.settings.Host' OBJ_NAME = '/org/openbmc/settings/host0' @@ -20,10 +22,14 @@ CONTROL_INTF = 'org.openbmc.Settings' class HostSettingsObject(DbusProperties): def __init__(self, bus, name, settings, path): - DbusProperties.__init__(self) - dbus.service.Object.__init__(self, bus, name) - + super(HostSettingsObject, self).__init__(conn=bus, object_path=name, + validator=self.input_validator) + self.bus = bus self.path = path + # Needed to ignore the validation on default networkconfig values as + # opposed to user giving the same. + self.adminmode = True + if not os.path.exists(path): os.mkdir(path) @@ -35,11 +41,13 @@ class HostSettingsObject(DbusProperties): path="/org/openbmc/settings/host0") # Create the dbus properties - for i in settings['host'].iterkeys(): - shk = settings['host'][i] + for i in settings[DBUS_NAME].iterkeys(): + shk = settings[DBUS_NAME][i] self.set_settings_property(shk['name'], shk['type'], shk['default']) + # Done with consuming factory settings. + self.adminmode = False def get_bmc_value(self, name): try: @@ -58,11 +66,11 @@ class HostSettingsObject(DbusProperties): if bmcv: value = bmcv if type == "i": - self.Set(DBUS_NAME, name, value) + self.Set(DBUS_NAME, name, int(value)) elif type == "s": self.Set(DBUS_NAME, name, str(value)) elif type == "b": - self.Set(DBUS_NAME, name, value) + self.Set(DBUS_NAME, name, bool(value)) # Save the settings to the BMC. This will write the settings value in # individual files named by the property name to the BMC. @@ -85,6 +93,85 @@ class HostSettingsObject(DbusProperties): def SettingsUpdated(self, sname): pass + def validate_regex(self, regex, value): + if not re.compile(regex).search(value): + raise ValueError, "Invalid input. Data does not satisfy regex" + + def validate_range(self, min, max, value): + if value not in range(min, max): + raise ValueError, "Invalid input. Data not in allowed range" + + def validate_list_ignore_case(self, lst, value): + if value.lower() not in map(lambda val: val.lower(), lst): + raise ValueError, "Invalid input. Data not in allowed values" + + # validate host network configuration + # need "ipaddress=,prefix=,gateway=,mac=,addr_type=" + # Must be able to handle any order + def validate_net_config(self, value): + if self.adminmode: + return + + # Need all of these to be given by the user. + user_config = [] + all_config = ['ipaddress', 'prefix', 'gateway', 'mac', 'addr_type'] + + # This has a hard data format mentioned above so no blanks allowed. + if value.count(" ") or value.count("=") != 5: + raise ValueError, "Invalid Network Data. No white spaces allowed" + + config = value.split(',') + for key_value in config: + key , value = key_value.split('=') + if not key or not value: + raise ValueError, "Invalid key or Data" + + # Add the current key seen so we can compare at the end to see + # if all values have been given + user_config.append(key.lower()) + + if key.lower() == 'ipaddress' or key.lower() == 'gateway': + IP(value) + + elif key.lower() == 'mac': + regex = '([a-fA-F0-9]{2}[:|\-]?){6}' + self.validate_regex(regex, value) + + elif key.lower() == 'prefix': + self.validate_range(0, 33, int(value)) + + elif key.lower() == 'addr_type': + allowed = ["STATIC", "DYNAMIC"] + self.validate_list_ignore_case(allowed, value) + + # Did user pass everything ?? + if set(all_config) - set(user_config): + raise ValueError, "Invalid Network Data. All information is mandatory" + + # Validate to see if the changes are in order + def input_validator(self, iface, proprty, value): + settings = s.SETTINGS + shk = {} + for key in settings[iface].iterkeys(): + if proprty == settings[iface][key]['name']: + shk = settings[iface][key] + break + + # User entered key is not present + if not shk: raise KeyError, "Invalid Property" + + if shk['validation'] == 'list': + self.validate_list_ignore_case(shk['allowed'], value) + + elif shk['validation'] == 'range': + self.validate_range(shk['min'], shk['max']+1, value) + + elif shk['validation'] == 'regex': + self.validate_regex(shk['regex'], value) + + elif shk['validation'] == 'custom': + getattr(self, shk['method'])(value) + if __name__ == '__main__': dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) |