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:
parent
7c0c7bf5c0
commit
0682137b21
@ -645,6 +645,13 @@
|
|||||||
<xs:attribute name="keyid" type="t_gpg_keyid" use="required"/>
|
<xs:attribute name="keyid" type="t_gpg_keyid" use="required"/>
|
||||||
<xs:attribute name="publish" type="xs:boolean" use="optional"/>
|
<xs:attribute name="publish" type="xs:boolean" use="optional"/>
|
||||||
<xs:attribute name="prompt_passphrase" type="xs:boolean" use="required"/>
|
<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="[!"#$%&\\'\(\)\*\+,\-\./0123456789:;<=>\?@ABCDEFGHIJKLMNOPQRSTUVWXYZ\[\]\^_`abcdefghijklmnopqrstuvwxyz\{\|\}~ ]+"/>
|
||||||
|
</xs:restriction>
|
||||||
|
</xs:simpleType>
|
||||||
|
</xs:attribute>
|
||||||
<xs:attribute name="gnupghome" use="optional">
|
<xs:attribute name="gnupghome" use="optional">
|
||||||
<xs:simpleType>
|
<xs:simpleType>
|
||||||
<xs:restriction base="xs:string">
|
<xs:restriction base="xs:string">
|
||||||
|
@ -56,7 +56,7 @@ class Conf(object):
|
|||||||
self.cfg = {}
|
self.cfg = {}
|
||||||
if validate_cfg:
|
if validate_cfg:
|
||||||
# Validation post-substitution
|
# Validation post-substitution
|
||||||
self.validate()
|
self.validate(parsed = False)
|
||||||
|
|
||||||
def get_pki_obj(self, pki, pki_type):
|
def get_pki_obj(self, pki, pki_type):
|
||||||
elem = {}
|
elem = {}
|
||||||
@ -82,8 +82,7 @@ class Conf(object):
|
|||||||
return(elem)
|
return(elem)
|
||||||
|
|
||||||
def get_source(self, source, item, _source):
|
def get_source(self, source, item, _source):
|
||||||
_source_item = {'flags': [],
|
_source_item = {'flags': [], 'fname': None}
|
||||||
'fname': None}
|
|
||||||
elem = source.xpath('./{0}'.format(item))[0]
|
elem = source.xpath('./{0}'.format(item))[0]
|
||||||
if item == 'checksum':
|
if item == 'checksum':
|
||||||
if elem.get('explicit', False):
|
if elem.get('explicit', False):
|
||||||
@ -132,6 +131,8 @@ class Conf(object):
|
|||||||
return(_source_item)
|
return(_source_item)
|
||||||
|
|
||||||
def get_xsd(self):
|
def get_xsd(self):
|
||||||
|
if isinstance(self.xsd, lxml.etree.XMLSchema):
|
||||||
|
return(self.xsd)
|
||||||
if not self.xsd:
|
if not self.xsd:
|
||||||
path = os.path.join(os.path.dirname(__file__), 'bdisk.xsd')
|
path = os.path.join(os.path.dirname(__file__), 'bdisk.xsd')
|
||||||
else:
|
else:
|
||||||
@ -231,10 +232,12 @@ class Conf(object):
|
|||||||
for e in self.profile.xpath(_xpath):
|
for e in self.profile.xpath(_xpath):
|
||||||
for se in e:
|
for se in e:
|
||||||
if not isinstance(se, lxml.etree._Comment):
|
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'):
|
for e in ('desc', 'uri', 'ver', 'max_recurse'):
|
||||||
_xpath = './meta/{0}/text()'.format(e)
|
_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.
|
# HERE is where we would handle regex patterns.
|
||||||
# But we don't, because they're in self.xml_suppl.btags['regex'].
|
# But we don't, because they're in self.xml_suppl.btags['regex'].
|
||||||
#self.cfg['regexes'] = {}
|
#self.cfg['regexes'] = {}
|
||||||
@ -249,7 +252,7 @@ class Conf(object):
|
|||||||
self.cfg['pki'] = {'clients': []}
|
self.cfg['pki'] = {'clients': []}
|
||||||
elem = self.profile.xpath('./pki')[0]
|
elem = self.profile.xpath('./pki')[0]
|
||||||
self.cfg['pki']['overwrite'] = transform.xml2py(
|
self.cfg['pki']['overwrite'] = transform.xml2py(
|
||||||
elem.get('overwrite', 'no'))
|
elem.get('overwrite', 'false'))
|
||||||
ca = elem.xpath('./ca')[0]
|
ca = elem.xpath('./ca')[0]
|
||||||
clients = elem.xpath('./client')
|
clients = elem.xpath('./client')
|
||||||
self.cfg['pki']['ca'] = self.get_pki_obj(ca, 'ca')
|
self.cfg['pki']['ca'] = self.get_pki_obj(ca, 'ca')
|
||||||
@ -266,7 +269,9 @@ class Conf(object):
|
|||||||
'uuid': None}
|
'uuid': None}
|
||||||
for a in self.cfg['profile']:
|
for a in self.cfg['profile']:
|
||||||
if a in self.profile.attrib:
|
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()
|
return()
|
||||||
|
|
||||||
def parse_sources(self):
|
def parse_sources(self):
|
||||||
@ -306,13 +311,49 @@ class Conf(object):
|
|||||||
attrib = False)
|
attrib = False)
|
||||||
return()
|
return()
|
||||||
|
|
||||||
def validate(self):
|
def validate(self, parsed = False):
|
||||||
# TODO: perform further validations that we can't do in XSD.
|
|
||||||
xsd = self.get_xsd()
|
xsd = self.get_xsd()
|
||||||
self.xsd = etree.XMLSchema(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.
|
# This would return a bool if it validates or not.
|
||||||
#self.xsd.validate(self.xml)
|
#self.xsd.validate(self.xml)
|
||||||
# We want to get a more detailed exception.
|
# We want to get a more detailed exception.
|
||||||
xml = etree.fromstring(self.xml_suppl.return_full())
|
xml = etree.fromstring(self.xml_suppl.return_full())
|
||||||
self.xsd.assertValid(xml)
|
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()
|
return()
|
||||||
|
@ -77,6 +77,8 @@ class detect(object):
|
|||||||
|
|
||||||
def password_hash_salt(self, salted_hash):
|
def password_hash_salt(self, salted_hash):
|
||||||
_hash_list = salted_hash.split('$')
|
_hash_list = salted_hash.split('$')
|
||||||
|
if len(_hash_list) < 3:
|
||||||
|
return(None)
|
||||||
salt = _hash_list[2]
|
salt = _hash_list[2]
|
||||||
return(salt)
|
return(salt)
|
||||||
|
|
||||||
@ -630,10 +632,28 @@ class transform(object):
|
|||||||
if _hash:
|
if _hash:
|
||||||
acct['hash_algo'] = _hash
|
acct['hash_algo'] = _hash
|
||||||
else:
|
else:
|
||||||
raise ValueError(
|
acct['hash_algo'] = None
|
||||||
'Invalid salted password hash: {0}'.format(
|
# We no longer raise ValueError. Per shadow(5):
|
||||||
elem.text)
|
#######################################################
|
||||||
)
|
# 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['salt_hash'] = elem.text
|
||||||
acct['passphrase'] = None
|
acct['passphrase'] = None
|
||||||
else:
|
else:
|
||||||
@ -873,13 +893,13 @@ class xml_supplicant(object):
|
|||||||
return(cfg)
|
return(cfg)
|
||||||
|
|
||||||
def _parse_regexes(self):
|
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'])
|
_key = 'regex%{0}'.format(regex.attrib['id'])
|
||||||
self.btags['regex'][_key] = regex.text
|
self.btags['regex'][_key] = regex.text
|
||||||
return()
|
return()
|
||||||
|
|
||||||
def _parse_variables(self):
|
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'][
|
self.btags['variable'][
|
||||||
'variable%{0}'.format(variable.attrib['id'])
|
'variable%{0}'.format(variable.attrib['id'])
|
||||||
] = variable.text
|
] = variable.text
|
||||||
@ -976,7 +996,6 @@ class xml_supplicant(object):
|
|||||||
return(path)
|
return(path)
|
||||||
|
|
||||||
def return_full(self):
|
def return_full(self):
|
||||||
#nsmap = self.return_naked_ns()
|
|
||||||
# https://stackoverflow.com/a/22553145/733214
|
# https://stackoverflow.com/a/22553145/733214
|
||||||
local_xml = lxml.etree.Element('bdisk',
|
local_xml = lxml.etree.Element('bdisk',
|
||||||
nsmap = self.orig_xml.nsmap,
|
nsmap = self.orig_xml.nsmap,
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
#!/bin/bash
|
#!/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
|
||||||
|
@ -43,10 +43,10 @@
|
|||||||
<!-- Salted/hashed password is "test" -->
|
<!-- Salted/hashed password is "test" -->
|
||||||
<rootpass hashed="true">$6$7KfIdtHTcXwVrZAC$LZGNeMNz7v5o/cYuA48FAxtZynpIwO5B1CPGXnOW5kCTVpXVt4SypRqfM.AoKkFt/O7MZZ8ySXJmxpELKmdlF1</rootpass>
|
<rootpass hashed="true">$6$7KfIdtHTcXwVrZAC$LZGNeMNz7v5o/cYuA48FAxtZynpIwO5B1CPGXnOW5kCTVpXVt4SypRqfM.AoKkFt/O7MZZ8ySXJmxpELKmdlF1</rootpass>
|
||||||
<user sudo="true">
|
<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: -->
|
<!-- You can also use substitution from different profiles in this same configuration: -->
|
||||||
<!-- <username>{xpath%//profile[@name='another_profile']/meta/names/uxname"}</username> -->
|
<!-- <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"
|
<password hashed="false"
|
||||||
hash_algo="sha512"
|
hash_algo="sha512"
|
||||||
salt="auto">testpassword</password>
|
salt="auto">testpassword</password>
|
||||||
@ -153,7 +153,7 @@
|
|||||||
</subject>
|
</subject>
|
||||||
</client>
|
</client>
|
||||||
</pki>
|
</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. -->
|
blank passphrase for all operations. -->
|
||||||
<gpg keyid="none"
|
<gpg keyid="none"
|
||||||
gnupghome="none"
|
gnupghome="none"
|
||||||
|
Loading…
Reference in New Issue
Block a user