add set/clear/list passwords cli commands

- add set/clear/list password commands
- update passwd_editor script to perform a password list
- add new password unittest

Still todo:
- package script properly into rpm with appropriate restrictive permissions
This commit is contained in:
Steve Noyes 2015-09-16 11:18:29 -04:00
parent 15b2ab00b0
commit 3da95342ab
7 changed files with 278 additions and 12 deletions

View File

@ -0,0 +1,82 @@
# Copyright(c) 2015, Oracle and/or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import os
from kollacli.exceptions import CommandError
from kollacli import utils
PWDS_FILENAME = 'passwords.yml'
PWD_EDITOR_FILENAME = 'passwd_editor.py'
def set_password(pwd_key, pwd_value):
"""set a password value
If the password name exists, it will be changed.
If it doesn't exist, a new password will be added.
"""
editor_path = os.path.join(utils.get_kollacli_home(),
'tools',
PWD_EDITOR_FILENAME)
pwd_file_path = os.path.join(utils.get_kolla_etc(),
PWDS_FILENAME)
cmd = 'sudo %s -p %s -k %s -v %s' % (editor_path, pwd_file_path,
pwd_key, pwd_value)
err, output = utils.run_cmd(cmd, print_output=False)
if err:
raise CommandError(output)
def clear_password(pwd_key):
"""clear a password
if the password exists, it will be removed from the passwords file
"""
editor_path = os.path.join(utils.get_kollacli_home(),
'tools',
PWD_EDITOR_FILENAME)
pwd_file_path = os.path.join(utils.get_kolla_etc(),
PWDS_FILENAME)
cmd = 'sudo %s -p %s -k %s -c' % (editor_path, pwd_file_path,
pwd_key)
err, output = utils.run_cmd(cmd, print_output=False)
if err:
raise CommandError(output)
def get_password_names():
"""return a list of password names"""
editor_path = os.path.join(utils.get_kollacli_home(),
'tools',
PWD_EDITOR_FILENAME)
pwd_file_path = os.path.join(utils.get_kolla_etc(),
PWDS_FILENAME)
cmd = 'sudo %s -p %s -l' % (editor_path, pwd_file_path)
err, output = utils.run_cmd(cmd, print_output=False)
if err:
raise CommandError(output)
pwd_names = []
if output and ',' in output[0]:
pwd_names = output[0].split(',')
return pwd_names

View File

@ -15,9 +15,9 @@ import logging
import os
import yaml
from kollacli.utils import change_property
from kollacli.utils import get_kolla_etc
from kollacli.utils import get_kolla_home
from kollacli.utils import change_property
ALLVARS_PATH = 'ansible/group_vars/all.yml'
GLOBALS_FILENAME = 'globals.yml'

85
kollacli/password.py Normal file
View File

@ -0,0 +1,85 @@
# Copyright(c) 2015, Oracle and/or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import argparse
import getpass
import logging
import traceback
from cliff.command import Command
from cliff.lister import Lister
from kollacli.ansible.passwords import clear_password
from kollacli.ansible.passwords import get_password_names
from kollacli.ansible.passwords import set_password
class PasswordSet(Command):
"Password Set"
log = logging.getLogger(__name__)
def get_parser(self, prog_name):
parser = super(PasswordSet, self).get_parser(prog_name)
parser.add_argument('passwordname', metavar='<passwordname>',
help='passwordname')
parser.add_argument('--insecure', nargs='?', help=argparse.SUPPRESS)
return parser
def take_action(self, parsed_args):
try:
password_name = parsed_args.passwordname.strip()
if parsed_args.insecure:
password = parsed_args.insecure.strip()
else:
password = getpass.getpass('Password: ').strip()
set_password(password_name, password)
except Exception:
raise Exception(traceback.format_exc())
class PasswordClear(Command):
"Password Clear"
log = logging.getLogger(__name__)
def get_parser(self, prog_name):
parser = super(PasswordClear, self).get_parser(prog_name)
parser.add_argument('passwordname', metavar='<passwordname>',
help='passwordname')
return parser
def take_action(self, parsed_args):
try:
password_name = parsed_args.passwordname.strip()
clear_password(password_name)
except Exception:
raise Exception(traceback.format_exc())
class PasswordList(Lister):
"""List all password names"""
log = logging.getLogger(__name__)
def take_action(self, parsed_args):
password_names = get_password_names()
password_names = sorted(password_names)
data = []
for password_name in password_names:
data.append((password_name, '-'))
return (('Password Name', 'Password'), data)

View File

@ -82,6 +82,15 @@ def convert_to_unicode(the_string):
def run_cmd(cmd, print_output=True):
"""run a system command
return:
- err_flag: False=command succeeded
True=command failed
- output: [List of strings]
if error, provides error information
if success, provides command output
"""
log = logging.getLogger(__name__)
err_flag = False
output = []
@ -111,9 +120,10 @@ def change_property(file_path, property_key, property_value, clear=False):
property value: property value
clear: flag to remove property
If clear and property doesn't exists, nothing is done.
If clear, and property exists, remove it from the property file.
If clear, and property doesn't exists, nothing is done.
If not clear, and key is not found, the new property will be appended.
If not clear, and key is found, edit property in place
If not clear, and key is found, edit property in place.
"""
try:
file_contents = []

View File

@ -31,6 +31,8 @@ console_scripts =
kollacli = kollacli.shell:main
kolla.cli =
deploy = kollacli.common:Deploy
dump = kollacli.common:Dump
group_add = kollacli.group:GroupAdd
group_remove = kollacli.group:GroupRemove
group_addhost = kollacli.group:GroupAddhost
@ -44,14 +46,15 @@ kolla.cli =
host_list = kollacli.host:HostList
host_check = kollacli.host:HostCheck
host_setup = kollacli.host:HostSetup
service_list = kollacli.service:ServiceList
list = kollacli.common:List
password_set = kollacli.password:PasswordSet
password_clear = kollacli.password:PasswordClear
password_list = kollacli.password:PasswordList
property_set = kollacli.property:PropertySet
property_clear = kollacli.property:PropertyClear
property_list = kollacli.property:PropertyList
deploy = kollacli.common:Deploy
list = kollacli.common:List
service_list = kollacli.service:ServiceList
setdeploy = kollacli.common:Setdeploy
dump = kollacli.common:Dump
[extract_messages]
keywords = _ gettext ngettext l_ lazy_gettext

70
tests/password.py Normal file
View File

@ -0,0 +1,70 @@
# Copyright(c) 2015, Oracle and/or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
from common import KollaCliTest
import unittest
class TestFunctional(KollaCliTest):
def test_password_set_clear(self):
# test list
msg = self.run_client_cmd('password list')
key = 'database_password'
value = '-'
ok = self._password_value_exists(key, value, msg)
self.assertTrue(ok, 'list failed. Password (%s/%s) not in output: %s'
% (key, value, msg))
# test append
key = 'TeStKeY'
value = '-'
self.run_client_cmd('password set %s --insecure %s' % (key, value))
msg = self.run_client_cmd('password list')
ok = self._password_value_exists(key, value, msg)
self.assertTrue(ok, 'set new password failed. Password ' +
'(%s/%s) not in output: %s'
% (key, value, msg))
# test modify existing
key = 'TeStKeY'
value = '-'
self.run_client_cmd('password set %s --insecure %s' % (key, value))
msg = self.run_client_cmd('password list')
ok = self._password_value_exists(key, value, msg)
self.assertTrue(ok, 'set modify password failed. Password ' +
'(%s/%s) not in output: %s' %
(key, value, msg))
# test clear
key = 'TeStKeY'
value = '-'
self.run_client_cmd('password clear %s' % key)
msg = self.run_client_cmd('password list')
ok = self._password_value_exists(key, value, msg)
self.assertFalse(ok, 'clear password failed. Password ' +
'(%s/%s) not in output: %s' %
(key, value, msg))
def _password_value_exists(self, key, value, cli_output):
"""Verify cli data against model data"""
# check for any host in cli output that shouldn't be there
cli_lines = cli_output.split('\n')
for cli_line in cli_lines:
if key in cli_line and value in cli_line:
return True
return False
if __name__ == '__main__':
unittest.main()

View File

@ -18,6 +18,22 @@ import sys
from kollacli import utils
def _print_pwd_keys(path):
pwd_keys = ''
prefix = ''
with open(path, 'r') as pwd_file:
for line in pwd_file:
if line.startswith('#'):
# skip commented lines
continue
if ':' in line:
pwd_key = line.split(':')[0]
pwd_keys = pwd_keys + prefix + pwd_key
prefix = ','
print(pwd_keys)
def main():
"""edit password in passwords.yml file
@ -26,9 +42,9 @@ def main():
-k key # key of password
-v value # value of password
-c # flag to clear the password
-l # return a csv string of the existing keys
-l # print to stdout a csv string of the existing keys
"""
opts, _ = getopt.getopt(sys.argv[1:], 'p:k:v:cal')
opts, _ = getopt.getopt(sys.argv[1:], 'p:k:v:cl')
path = ''
pwd_key = ''
pwd_value = ''
@ -47,11 +63,11 @@ def main():
list_flag = True
if list_flag:
# return the password keys
pass
# print the password keys
_print_pwd_keys(path)
else:
# edit a password
utils.change_property(path, pwd_key, pwd_value, clear=clear_flag)
utils.change_property(path, pwd_key, pwd_value, clear_flag)
if __name__ == '__main__':