parsing, but need to finish extended value validations for confparser and then get to work actually making the stuff go

This commit is contained in:
brent s. 2018-05-30 13:43:30 -04:00
parent 7c0c7bf5c0
commit 0682137b21
5 changed files with 88 additions and 21 deletions

View File

@ -645,6 +645,13 @@
<xs:attribute name="keyid" type="t_gpg_keyid" use="required"/>
<xs:attribute name="publish" type="xs:boolean" use="optional"/>
<xs:attribute name="prompt_passphrase" type="xs:boolean" use="required"/>
<xs:attribute name="passphrase" use="optional">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:pattern value="[!&quot;#$%&amp;\\'\(\)\*\+,\-\./0123456789:;&lt;=&gt;\?@ABCDEFGHIJKLMNOPQRSTUVWXYZ\[\]\^_`abcdefghijklmnopqrstuvwxyz\{\|\}~ ]+"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute name="gnupghome" use="optional">
<xs:simpleType>
<xs:restriction base="xs:string">

View File

@ -56,7 +56,7 @@ class Conf(object):
self.cfg = {}
if validate_cfg:
# Validation post-substitution
self.validate()
self.validate(parsed = False)

def get_pki_obj(self, pki, pki_type):
elem = {}
@ -82,8 +82,7 @@ class Conf(object):
return(elem)

def get_source(self, source, item, _source):
_source_item = {'flags': [],
'fname': None}
_source_item = {'flags': [], 'fname': None}
elem = source.xpath('./{0}'.format(item))[0]
if item == 'checksum':
if elem.get('explicit', False):
@ -132,6 +131,8 @@ class Conf(object):
return(_source_item)

def get_xsd(self):
if isinstance(self.xsd, lxml.etree.XMLSchema):
return(self.xsd)
if not self.xsd:
path = os.path.join(os.path.dirname(__file__), 'bdisk.xsd')
else:
@ -231,10 +232,12 @@ class Conf(object):
for e in self.profile.xpath(_xpath):
for se in e:
if not isinstance(se, lxml.etree._Comment):
self.cfg[t][se.tag] = se.text
self.cfg[t][se.tag] = transform.xml2py(se.text,
attrib = False)
for e in ('desc', 'uri', 'ver', 'max_recurse'):
_xpath = './meta/{0}/text()'.format(e)
self.cfg[e] = self.profile.xpath(_xpath)[0]
self.cfg[e] = transform.xml2py(self.profile.xpath(_xpath)[0],
attrib = False)
# HERE is where we would handle regex patterns.
# But we don't, because they're in self.xml_suppl.btags['regex'].
#self.cfg['regexes'] = {}
@ -249,7 +252,7 @@ class Conf(object):
self.cfg['pki'] = {'clients': []}
elem = self.profile.xpath('./pki')[0]
self.cfg['pki']['overwrite'] = transform.xml2py(
elem.get('overwrite', 'no'))
elem.get('overwrite', 'false'))
ca = elem.xpath('./ca')[0]
clients = elem.xpath('./client')
self.cfg['pki']['ca'] = self.get_pki_obj(ca, 'ca')
@ -266,7 +269,9 @@ class Conf(object):
'uuid': None}
for a in self.cfg['profile']:
if a in self.profile.attrib:
self.cfg['profile'][a] = self.profile.attrib[a]
self.cfg['profile'][a] = transform.xml2py(
self.profile.attrib[a],
attrib = True)
return()

def parse_sources(self):
@ -306,13 +311,49 @@ class Conf(object):
attrib = False)
return()

def validate(self):
# TODO: perform further validations that we can't do in XSD.
def validate(self, parsed = False):
xsd = self.get_xsd()
if not isinstance(xsd, lxml.etree.XMLSchema):
self.xsd = etree.XMLSchema(xsd)
else:
pass
# This would return a bool if it validates or not.
#self.xsd.validate(self.xml)
# We want to get a more detailed exception.
xml = etree.fromstring(self.xml_suppl.return_full())
self.xsd.assertValid(xml)
if parsed:
# TODO: perform further validations that we can't do in XSD.
# We wait until after it's parsed to evaluate because otherwise we
# can't use utils.valid().
# We only bother with stuff that would hinder building, though -
# e.g. we don't check that profile's UUID is a valid UUID4.
# URLs
for url in (self.cfg['uri'], self.cfg['dev']['website']):
if not valid.url(url):
raise ValueError('{0} is not a valid URL.'.format(url))
# Emails
for k in self.cfg['gpg']['keys']:
if not valid.email(k['email']):
raise ValueError(
'GPG key {0}: {1} is not a valid email '
'address'.format(k['name'], k['email']))
if not valid.email(self.cfg['dev']['email']):
raise ValueError('{0} is not a valid email address'.format(
self.cfg['dev']['email']))
if self.cfg['pki']:
if 'subject' in self.cfg['pki']['ca']:
if not valid.email(
self.cfg['pki']['ca']['subject']['emailAddress']):
raise ValueError('{0} is not a valid email '
'address'.format(
self.cfg['pki']['ca']['subject']['emailAddress']))

if not self.cfg['pki'][x]['subject']:
continue
if not valid.email(
self.cfg['pki'][x]['subject']['emailAddress']):
raise ValueError('{0} is not a valid email '
'address'.format(
self.cfg['pki'][x]['subject']['email']))
return()

View File

@ -77,6 +77,8 @@ class detect(object):

def password_hash_salt(self, salted_hash):
_hash_list = salted_hash.split('$')
if len(_hash_list) < 3:
return(None)
salt = _hash_list[2]
return(salt)

@ -630,10 +632,28 @@ class transform(object):
if _hash:
acct['hash_algo'] = _hash
else:
raise ValueError(
'Invalid salted password hash: {0}'.format(
elem.text)
)
acct['hash_algo'] = None
# We no longer raise ValueError. Per shadow(5):
#######################################################
# If the password field contains some string that is
# not a valid result of crypt(3), for instance ! or *,
# the user will not be able to use a unix password to
# log in (but the user may log in the system by other
# means).
# This field may be empty, in which case no passwords
# are required to authenticate as the specified login
# name. However, some applications which read the
# /etc/shadow file may decide not to permit any access
# at all if the password field is empty.
# A password field which starts with an exclamation
# mark means that the password is locked. The remaining
# characters on the line represent the password field
# before the password was locked.
#######################################################
# raise ValueError(
# 'Invalid salted password hash: {0}'.format(
# elem.text)
# )
acct['salt_hash'] = elem.text
acct['passphrase'] = None
else:
@ -873,13 +893,13 @@ class xml_supplicant(object):
return(cfg)

def _parse_regexes(self):
for regex in self.profile.xpath('//meta/regexes/pattern'):
for regex in self.profile.xpath('./meta/regexes/pattern'):
_key = 'regex%{0}'.format(regex.attrib['id'])
self.btags['regex'][_key] = regex.text
return()

def _parse_variables(self):
for variable in self.profile.xpath('//meta/variables/variable'):
for variable in self.profile.xpath('./meta/variables/variable'):
self.btags['variable'][
'variable%{0}'.format(variable.attrib['id'])
] = variable.text
@ -976,7 +996,6 @@ class xml_supplicant(object):
return(path)

def return_full(self):
#nsmap = self.return_naked_ns()
# https://stackoverflow.com/a/22553145/733214
local_xml = lxml.etree.Element('bdisk',
nsmap = self.orig_xml.nsmap,

View File

@ -1,3 +1,3 @@
#!/bin/bash

xmllint -schema ../bdisk/bdisk.xsd ../docs/examples/multi_profile.xml --noout
xmllint -schema /opt/dev/bdisk/bdisk/bdisk.xsd /opt/dev/bdisk/docs/examples/multi_profile.xml --noout

View File

@ -43,10 +43,10 @@
<!-- Salted/hashed password is "test" -->
<rootpass hashed="true">$6$7KfIdtHTcXwVrZAC$LZGNeMNz7v5o/cYuA48FAxtZynpIwO5B1CPGXnOW5kCTVpXVt4SypRqfM.AoKkFt/O7MZZ8ySXJmxpELKmdlF1</rootpass>
<user sudo="true">
<username>{xpath%//meta/names/uxname/text()}</username>
<username>{xpath%../../../meta/names/uxname/text()}</username>
<!-- You can also use substitution from different profiles in this same configuration: -->
<!-- <username>{xpath%//profile[@name='another_profile']/meta/names/uxname"}</username> -->
<comment>{xpath%//meta/dev/author/text()}</comment>
<comment>{xpath%../../../meta/dev/author/text()}</comment>
<password hashed="false"
hash_algo="sha512"
salt="auto">testpassword</password>
@ -153,7 +153,7 @@
</subject>
</client>
</pki>
<!-- If prompt_passphrase is "no" and passphrase attribute is not given for a gpg element, we will try to use a
<!-- If prompt_passphrase is "false" and passphrase attribute is not given for a gpg element, we will try to use a
blank passphrase for all operations. -->
<gpg keyid="none"
gnupghome="none"