You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

138 lines
4.7 KiB

  1. # -*- coding: utf-8 -*-
  2. # Copyright: (c) 2020, Markus Bergholz <markuman@gmail.com>
  3. # Copyright: (c) 2020, Ansible Project
  4. # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
  5. from __future__ import (absolute_import, division, print_function)
  6. __metaclass__ = type
  7. from ansible.errors import AnsibleError, AnsibleParserError
  8. from ansible.plugins.lookup import LookupBase
  9. try:
  10. from lesspass.password import generate_password
  11. except ImportError:
  12. raise AnsibleError("Please install lesspass library.")
  13. import json
  14. import yaml
  15. ANSIBLE_METADATA = {
  16. 'metadata_version': '1.1',
  17. 'status': ['preview'],
  18. 'supported_by': 'community'
  19. }
  20. DOCUMENTATION = """
  21. lookup: lesspass
  22. author:
  23. - Markus Bergholz <markuman@gmail.com>
  24. version_added: 2.10
  25. requirements:
  26. - lesspass
  27. short_description: Calculates a password based on lesspass.
  28. description:
  29. - lesspass calculates the password based on a profile and a master password.
  30. - The Minimum profile requirements is a login name and a site name.
  31. - The profile can be read from file (.yml or .json), defined by options, or mixed (overwrite
  32. profile file properties with options).
  33. - See U(https://lesspass.com/)
  34. options:
  35. login:
  36. description: A login name.
  37. default: None
  38. type: string
  39. site:
  40. description: A site.
  41. default: None
  42. type: string
  43. lowercase:
  44. description: If a password should container lowercase characters.
  45. default: True
  46. type: boolean
  47. uppercase:
  48. description: If a password should container uppercase characters.
  49. default: True
  50. type: boolean
  51. symbols:
  52. description: If a password should container symbol characters.
  53. default: True
  54. type: boolean
  55. digits:
  56. description: If a password should container digits.
  57. default: True
  58. type: boolean
  59. counter:
  60. description: An Integer value.
  61. default: 1
  62. type: integer
  63. length:
  64. description: Lenght of the password.
  65. default: 32
  66. type: integer
  67. """
  68. EXAMPLES = """
  69. # Read options from file.
  70. - debug: "msg={{ lookup('lesspass', 'mypassword', profile='ansible-vault-private_key.json') }}"
  71. # Read options from file, but overwrite 'site' property.
  72. - debug: "msg={{ lookup('lesspass', 'mypassword', profile='ansible-vault-private_key.json', site='ansible.com') }}"
  73. # Read options from file, but overwrite 'site' and 'login' property.
  74. - debug: "msg={{ lookup('lesspass', 'mypassword', profile='ansible-vault-private_key.json', site='ansible.com', login='alf') }}"
  75. # Use minimum requirements.
  76. - debug: "msg={{ lookup('lesspass', 'mypassword', site='ansible.com', login='alf') }}"
  77. # Use minimum requirements, but disable symbols.
  78. - debug: "msg={{ lookup('lesspass', 'mypassword', site='ansible.com', login='alf', digits=false, symbols=false) }}"
  79. """
  80. class LookupModule(LookupBase):
  81. def run(self, terms, variables=None, login=None, site=None,
  82. lowercase=None, uppercase=None, symbols=None, digits=None,
  83. counter=None, length=None, profile=None):
  84. if profile is not None:
  85. try:
  86. if profile.find(".yml") == (len(profile) - 4):
  87. profile = yaml.load(open(profile))
  88. elif profile.find(".json") == (len(profile) - 5):
  89. profile = json.load(open(profile))
  90. else:
  91. raise AnsibleParserError()
  92. except AnsibleParserError:
  93. raise AnsibleError("Cannot open or parse lesspass profile: %s" % profile)
  94. # overwrite profile items if given
  95. if profile is not None:
  96. profile['login'] = login or profile.get('login')
  97. profile['site'] = site or profile.get('site')
  98. profile['lowercase'] = lowercase or profile.get('lowercase')
  99. profile['uppercase'] = uppercase or profile.get('uppercase')
  100. profile['symbols'] = symbols or profile.get('symbols')
  101. profile['digits'] = digits or profile.get('digits')
  102. profile['counter'] = counter or profile.get('counter')
  103. profile['length'] = length or profile.get('length')
  104. # build profile of no profile file was given
  105. # login and site are required in this case
  106. if profile is None and login is not None and site is not None:
  107. profile = {'login': login,
  108. 'site': site,
  109. 'lowercase': lowercase or True,
  110. 'uppercase': uppercase or True,
  111. 'symbols': symbols or True,
  112. 'digits': digits or True,
  113. 'counter': 1,
  114. 'length': 32}
  115. if profile is None:
  116. raise AnsibleError("A profile or a pair of login and site is a minimum requirement")
  117. return [generate_password(profile, terms[0])]