/lib/ansible/parsing/yaml/objects.py

https://github.com/debfx/ansible · Python · 137 lines · 71 code · 29 blank · 37 comment · 6 complexity · 0a9dcb36db38314e72a237d824199949 MD5 · raw file

  1. # (c) 2012-2014, Michael DeHaan <michael.dehaan@gmail.com>
  2. #
  3. # This file is part of Ansible
  4. #
  5. # Ansible is free software: you can redistribute it and/or modify
  6. # it under the terms of the GNU General Public License as published by
  7. # the Free Software Foundation, either version 3 of the License, or
  8. # (at your option) any later version.
  9. #
  10. # Ansible is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License
  16. # along with Ansible. If not, see <http://www.gnu.org/licenses/>.
  17. # Make coding more python3-ish
  18. from __future__ import (absolute_import, division, print_function)
  19. __metaclass__ = type
  20. import yaml
  21. from ansible.module_utils.six import text_type
  22. from ansible.module_utils._text import to_bytes, to_text
  23. class AnsibleBaseYAMLObject(object):
  24. '''
  25. the base class used to sub-class python built-in objects
  26. so that we can add attributes to them during yaml parsing
  27. '''
  28. _data_source = None
  29. _line_number = 0
  30. _column_number = 0
  31. def _get_ansible_position(self):
  32. return (self._data_source, self._line_number, self._column_number)
  33. def _set_ansible_position(self, obj):
  34. try:
  35. (src, line, col) = obj
  36. except (TypeError, ValueError):
  37. raise AssertionError(
  38. 'ansible_pos can only be set with a tuple/list '
  39. 'of three values: source, line number, column number'
  40. )
  41. self._data_source = src
  42. self._line_number = line
  43. self._column_number = col
  44. ansible_pos = property(_get_ansible_position, _set_ansible_position)
  45. class AnsibleMapping(AnsibleBaseYAMLObject, dict):
  46. ''' sub class for dictionaries '''
  47. pass
  48. class AnsibleUnicode(AnsibleBaseYAMLObject, text_type):
  49. ''' sub class for unicode objects '''
  50. pass
  51. class AnsibleSequence(AnsibleBaseYAMLObject, list):
  52. ''' sub class for lists '''
  53. pass
  54. # Unicode like object that is not evaluated (decrypted) until it needs to be
  55. # TODO: is there a reason these objects are subclasses for YAMLObject?
  56. class AnsibleVaultEncryptedUnicode(yaml.YAMLObject, AnsibleBaseYAMLObject):
  57. __UNSAFE__ = True
  58. __ENCRYPTED__ = True
  59. yaml_tag = u'!vault'
  60. @classmethod
  61. def from_plaintext(cls, seq, vault, secret):
  62. if not vault:
  63. raise vault.AnsibleVaultError('Error creating AnsibleVaultEncryptedUnicode, invalid vault (%s) provided' % vault)
  64. ciphertext = vault.encrypt(seq, secret)
  65. avu = cls(ciphertext)
  66. avu.vault = vault
  67. return avu
  68. def __init__(self, ciphertext):
  69. '''A AnsibleUnicode with a Vault attribute that can decrypt it.
  70. ciphertext is a byte string (str on PY2, bytestring on PY3).
  71. The .data attribute is a property that returns the decrypted plaintext
  72. of the ciphertext as a PY2 unicode or PY3 string object.
  73. '''
  74. super(AnsibleVaultEncryptedUnicode, self).__init__()
  75. # after construction, calling code has to set the .vault attribute to a vaultlib object
  76. self.vault = None
  77. self._ciphertext = to_bytes(ciphertext)
  78. @property
  79. def data(self):
  80. if not self.vault:
  81. # FIXME: raise exception?
  82. return self._ciphertext
  83. return to_text(self.vault.decrypt(self._ciphertext))
  84. @data.setter
  85. def data(self, value):
  86. self._ciphertext = value
  87. def __repr__(self):
  88. return repr(self.data)
  89. # Compare a regular str/text_type with the decrypted hypertext
  90. def __eq__(self, other):
  91. if self.vault:
  92. return other == self.data
  93. return False
  94. def __hash__(self):
  95. return id(self)
  96. def __ne__(self, other):
  97. if self.vault:
  98. return other != self.data
  99. return True
  100. def __str__(self):
  101. return str(self.data)
  102. def __unicode__(self):
  103. return to_text(self.data, errors='surrogate_or_strict')
  104. def encode(self, encoding=None, errors=None):
  105. return self.data.encode(encoding, errors)