[Pki-devel] [PATCH] 602 Added script to restore subsystem user.

Endi Sukma Dewata edewata at redhat.com
Thu May 28 19:46:42 UTC 2015


A new script has been added to restore a missing or corrupted
subsystem user such that it has the correct certificate, the
correct certificate mapping, and the correct membership to the
Subsystem Group.

A new Python library have been added to provide the script with
the functionality to access the PKI configuration and database.
A new CLI has also been added for troubleshooting.

https://bugzilla.redhat.com/show_bug.cgi?id=1225589

-- 
Endi S. Dewata
-------------- next part --------------
From 59e452019e7bd82bd6576fcd37f9859b4fa2eaa4 Mon Sep 17 00:00:00 2001
From: Endi S. Dewata <edewata at redhat.com>
Date: Wed, 27 May 2015 15:47:19 -0400
Subject: [PATCH] Added script to restore subsystem user.

A new script has been added to restore a missing or corrupted
subsystem user such that it has the correct certificate, the
correct certificate mapping, and the correct membership to the
Subsystem Group.

A new Python library have been added to provide the script with
the functionality to access the PKI configuration and database.
A new CLI has also been added for troubleshooting.

https://bugzilla.redhat.com/show_bug.cgi?id=1225589
---
 CMakeLists.txt                                |   10 +
 base/common/CMakeLists.txt                    |   12 +
 base/common/python/CMakeLists.txt             |    6 +
 base/common/python/pki/__init__.py            |   20 ++
 base/common/python/pki/cli.py                 |  195 +++++++++++
 base/common/python/pki/server/__init__.py     |  440 ++++++++++++++++++++++++
 base/common/python/pki/server/cli/__init__.py |   20 ++
 base/common/python/pki/server/cli/ca.py       |   38 +++
 base/common/python/pki/server/cli/cert.py     |  156 +++++++++
 base/common/python/pki/server/cli/config.py   |   90 +++++
 base/common/python/pki/server/cli/group.py    |  441 +++++++++++++++++++++++++
 base/common/python/pki/server/cli/user.py     |  406 +++++++++++++++++++++++
 base/common/sbin/pki-server                   |   87 +++++
 base/common/scripts/restore-subsystem-user.py |  113 +++++++
 14 files changed, 2034 insertions(+), 0 deletions(-)
 create mode 100644 base/common/python/CMakeLists.txt
 create mode 100644 base/common/python/pki/__init__.py
 create mode 100644 base/common/python/pki/cli.py
 create mode 100644 base/common/python/pki/server/__init__.py
 create mode 100644 base/common/python/pki/server/cli/__init__.py
 create mode 100644 base/common/python/pki/server/cli/ca.py
 create mode 100644 base/common/python/pki/server/cli/cert.py
 create mode 100644 base/common/python/pki/server/cli/config.py
 create mode 100644 base/common/python/pki/server/cli/group.py
 create mode 100644 base/common/python/pki/server/cli/user.py
 create mode 100644 base/common/sbin/pki-server
 create mode 100755 base/common/scripts/restore-subsystem-user.py

diff --git a/CMakeLists.txt b/CMakeLists.txt
index dd2a028abccd40ce8bccf9ef649e1a57c52774c3..ac077e999ee173af2011e2fe09c87d9c7208566f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -105,6 +105,16 @@ endif ()
 set(CMAKE_THREAD_PREFER_PTHREADS ON)
 find_package(Threads)
 
+find_package(PythonInterp REQUIRED)
+execute_process(
+    COMMAND
+        ${PYTHON_EXECUTABLE} -c
+        "from distutils.sysconfig import get_python_lib; print get_python_lib()"
+    OUTPUT_VARIABLE
+        PYTHON_SITE_PACKAGES
+    OUTPUT_STRIP_TRAILING_WHITESPACE
+)
+
 # config.h checks
 include(ConfigureChecks.cmake)
 configure_file(config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.h)
diff --git a/base/common/CMakeLists.txt b/base/common/CMakeLists.txt
index 861269d75efea3181c50dae454c12463016ed9c6..27e930299a94da225408d38501a9f7509fb57674 100644
--- a/base/common/CMakeLists.txt
+++ b/base/common/CMakeLists.txt
@@ -24,4 +24,16 @@ install(
         WORLD_READ
 )
 
+install(
+    DIRECTORY
+        sbin/
+    DESTINATION
+        ${SBIN_INSTALL_DIR}
+    FILE_PERMISSIONS
+        OWNER_EXECUTE OWNER_WRITE OWNER_READ
+        GROUP_EXECUTE GROUP_READ
+        WORLD_EXECUTE WORLD_READ
+)
+
 add_subdirectory(src)
+add_subdirectory(python)
diff --git a/base/common/python/CMakeLists.txt b/base/common/python/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..145d8c84be19a30695631f535623f2ab2504489a
--- /dev/null
+++ b/base/common/python/CMakeLists.txt
@@ -0,0 +1,6 @@
+install(
+    DIRECTORY
+        pki
+    DESTINATION
+        ${PYTHON_SITE_PACKAGES}
+)
diff --git a/base/common/python/pki/__init__.py b/base/common/python/pki/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..dd878d6dfb6877567d383926e22944a35bfeafe6
--- /dev/null
+++ b/base/common/python/pki/__init__.py
@@ -0,0 +1,20 @@
+#!/usr/bin/python
+# Authors:
+#     Endi S. Dewata <edewata at redhat.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Copyright (C) 2015 Red Hat, Inc.
+# All rights reserved.
+#
diff --git a/base/common/python/pki/cli.py b/base/common/python/pki/cli.py
new file mode 100644
index 0000000000000000000000000000000000000000..e3a44f56f3dbbfc61dc177ea83e13373eab11aac
--- /dev/null
+++ b/base/common/python/pki/cli.py
@@ -0,0 +1,195 @@
+#!/usr/bin/python
+# Authors:
+#     Endi S. Dewata <edewata at redhat.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Copyright (C) 2015 Red Hat, Inc.
+# All rights reserved.
+#
+
+import collections
+import getopt
+import sys
+
+
+class CLI(object):
+
+    def __init__(self, name, description):
+
+        self.name = name
+        self.description = description
+        self.parent = None
+        self.top = self
+
+        self.verbose = False
+        self.debug = False
+
+        self.modules = collections.OrderedDict()
+
+    def set_verbose(self, verbose):
+        self.verbose = verbose
+        if self.parent:
+            self.parent.set_verbose(verbose)
+
+    def set_debug(self, debug):
+        self.debug = debug
+        if self.parent:
+            self.parent.set_debug(debug)
+
+    def get_full_name(self):
+        if self.parent:
+            return self.parent.get_full_module_name(self.name)
+        return self.name
+
+    def get_full_module_name(self, module_name):
+        return self.get_full_name() + '-' + module_name
+
+    def add_module(self, module):
+        self.modules[module.name] = module
+        module.parent = self
+        module.top = self.top
+
+    def get_module(self, name):
+        return self.modules.get(name)
+
+    def print_message(self, message):
+        print '-' * len(message)
+        print message
+        print '-' * len(message)
+
+    def print_help(self):
+
+        print 'Commands:'
+
+        for module in self.modules.itervalues():
+            full_name = module.get_full_name()
+            print ' {0:30}{1:30}'.format(full_name, module.description)
+
+    def find_module(self, command):
+
+        module = self
+
+        while True:
+            (module, command) = module.parse_command(command)
+
+            if not module or not command:
+                return module
+
+    def parse_command(self, command):
+
+        # A command consists of parts joined by dashes: <part 1>-<part 2>-...-<part N>.
+        # For example: cert-request-find
+
+        # The command will be split into module name and sub command, for example:
+        #  - module name: cert
+        #  - sub command: request-find
+        module_name = None
+        sub_command = None
+
+        # Search the module by incrementally adding parts into module name.
+        # Repeat until it finds the module or until there is no more parts to add.
+        module = None
+        position = 0
+
+        while True:
+
+            # Find the next dash.
+            i = command.find('-', position)
+            if i >= 0:
+                # Dash found. Split command into module name and sub command.
+                module_name = command[0:i]
+                sub_command = command[i+1:]
+            else:
+                # Dash not found. Use the whole command.
+                module_name = command
+                sub_command = None
+
+            if self.debug:
+                print 'Module: %s' % module_name
+
+            m = self.get_module(module_name)
+            if m:
+                # Module found. Check sub command.
+                if not sub_command:
+                    # No sub command. Use this module.
+                    module = m
+                    break
+
+                # There is a sub command. It must be processed by module's children.
+                if len(m.modules) > 0:
+                    # Module has children. Use this module.
+                    module = m
+                    break
+
+                # Module doesn't have children. Keep looking.
+
+            # If there's no more dashes, stop.
+            if i < 0:
+                break
+
+            position = i + 1
+
+        return (module, sub_command)
+
+    def parse_args(self, args):
+
+        command = args[0]
+        (module, sub_command) = self.parse_command(command)
+
+        if not module:
+            raise Exception('Invalid module "%s".' % command)
+
+        # Prepare module arguments.
+        if sub_command:
+            # If module command exists, include it as arguments: <module command> <args>...
+            module_args = [sub_command] + args[1:]
+
+        else:
+            # Otherwise, pass the original arguments: <args>...
+            module_args = args[1:]
+
+        return (module, module_args)
+
+    def execute(self, argv):
+
+        try:
+            opts, args = getopt.getopt(argv, 'v', [
+                'verbose', 'help'])
+
+        except getopt.GetoptError as e:
+            print 'ERROR: ' + str(e)
+            self.print_help()
+            sys.exit(1)
+
+        if len(args) == 0:
+            self.print_help()
+            sys.exit()
+
+        for o, _ in opts:
+            if o in ('-v', '--verbose'):
+                self.set_verbose(True)
+
+            elif o == '--help':
+                self.print_help()
+                sys.exit()
+
+            else:
+                print 'ERROR: unknown option %s' % o
+                self.print_help()
+                sys.exit(1)
+
+        (module, module_args) = self.parse_args(argv)
+
+        module.execute(module_args)
diff --git a/base/common/python/pki/server/__init__.py b/base/common/python/pki/server/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..57f4a18e54c86fdda6bf17bf545c70f01110964e
--- /dev/null
+++ b/base/common/python/pki/server/__init__.py
@@ -0,0 +1,440 @@
+#!/usr/bin/python
+# Authors:
+#     Endi S. Dewata <edewata at redhat.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Copyright (C) 2015 Red Hat, Inc.
+# All rights reserved.
+#
+
+import base64
+import ldap
+import ldap.filter
+import nss.nss as nss
+import os
+
+
+class CAInstance(object):
+
+    def __init__(self):
+        self.instance_dir = '/var/lib/pki-ca'
+        self.cs_conf = os.path.join(self.instance_dir, 'conf', 'CS.cfg')
+        self.password_conf = os.path.join(self.instance_dir, 'conf', 'password.conf')
+
+        self.config = {}
+        self.passwords = {}
+
+        self.load()
+
+    def load(self):
+
+        self.config = {}
+
+        lines =  open(self.cs_conf).read().splitlines()
+
+        for line in lines:
+            parts = line.split('=', 1)
+            name = parts[0]
+            value = parts[1]
+            self.config[name] = value
+
+        self.passwords = {}
+
+        lines =  open(self.password_conf).read().splitlines()
+
+        for line in lines:
+            parts = line.split('=', 1)
+            name = parts[0]
+            value = parts[1]
+            self.passwords[name] = value
+
+    def get_system_certs(self):
+
+        tags = self.config['ca.cert.list'].split(',')
+
+        certs = []
+
+        for tag in tags:
+
+            cert = self.get_system_cert(tag)
+
+            certs.append(cert)
+
+        return certs
+
+    def get_system_cert(self, tag):
+
+        cert = {}
+        cert['tag'] = tag
+        cert['nickname'] = self.config['ca.cert.%s.nickname' % tag]
+        cert['usage'] = self.config['ca.cert.%s.certusage' % tag]
+        cert['token'] = self.config['ca.%s.tokenname' % tag]
+        cert['certificate'] = base64.b64decode(self.config['ca.%s.cert' % tag])
+        cert['request'] = base64.b64decode(self.config['ca.%s.certreq' % tag])
+
+        if not nss.nss_is_initialized():
+            nss.nss_init_nodb()
+
+        nss_cert = nss.Certificate(buffer(cert['certificate']))
+        cert['version'] = nss_cert.version
+        cert['serial_number'] = nss_cert.serial_number
+        cert['issuer'] = nss_cert.issuer
+        cert['subject'] = nss_cert.subject
+
+        return cert
+
+    def open_database(self):
+
+        hostname = self.config['internaldb.ldapconn.host']
+        port = self.config['internaldb.ldapconn.port']
+        base_dn = self.config['internaldb.basedn']
+        bind_dn = self.config['internaldb.ldapauth.bindDN']
+
+        bind_password = self.passwords['internaldb']
+
+        con = ldap.initialize('ldap://%s:%s' % (hostname, port))
+        con.simple_bind_s(bind_dn, bind_password)
+
+        return con
+
+    def create_user_object(self, entry):
+
+        user = {}
+        attrs = entry[1]
+
+        user['uid'] = attrs['uid'][0]
+        user['cn'] = attrs['cn'][0]
+        user['sn'] = attrs['sn'][0]
+        user['type'] = attrs['usertype'][0]
+
+        if 'description' in attrs:
+            user['description'] = attrs['description'][0]
+
+        if 'mail' in attrs:
+            user['mail'] = attrs['mail'][0]
+
+        if 'userCertificate' in attrs:
+             user['certificate'] = attrs['userCertificate'][0]
+
+        if 'userPassword' in attrs:
+            user['password'] = attrs['userPassword'][0]
+
+        if 'userstate' in attrs:
+            user['state'] = attrs['userstate'][0]
+
+        return user
+
+    def create_user_attrs(self, user):
+
+        attrs = [
+            ('objectclass', [
+                'top',
+                'person',
+                'organizationalPerson',
+                'inetOrgPerson',
+                'cmsUser'
+            ]),
+            ('uid', [user['uid']]),
+            ('cn', [user['cn']]),
+            ('sn', [user['sn']]),
+            ('usertype', [user['type']]),
+        ]
+
+        if 'description' in user:
+            attrs.append(('description', [user['description']]))
+
+        if 'mail' in user:
+            attrs.append(('mail', [user['mail']]))
+
+        if 'certificate' in user:
+            attrs.append(('userCertificate', [user['certificate']]))
+
+        if 'password' in user:
+            attrs.append(('userPassword', [user['password']]))
+
+        if 'state' in user:
+            attrs.append(('userstate', [user['state']]))
+
+        return attrs
+
+    def add_mod_tuple(self, modlist, name, attr, changes):
+        if name in changes:
+            if changes[name]:
+                modlist.append((ldap.MOD_REPLACE, attr, [changes[name]]))
+            else:
+                modlist.append((ldap.MOD_DELETE, attr, None))
+
+    def create_user_modlist(self, changes):
+
+        modlist = []
+
+        self.add_mod_tuple(modlist, 'cn', 'cn', changes)
+        self.add_mod_tuple(modlist, 'sn', 'sn', changes)
+        self.add_mod_tuple(modlist, 'type', 'usertype', changes)
+        self.add_mod_tuple(modlist, 'description', 'description', changes)
+        self.add_mod_tuple(modlist, 'mail', 'mail', changes)
+        self.add_mod_tuple(modlist, 'certificate', 'userCertificate', changes)
+        self.add_mod_tuple(modlist, 'password', 'userPassword', changes)
+        self.add_mod_tuple(modlist, 'state', 'userstate', changes)
+
+        return modlist
+
+    def find_system_users(self, description=None, certificate=None):
+
+        base_dn = self.config['internaldb.basedn']
+
+        filter = ''
+        counter = 0
+
+        if description:
+            escaped_value = ldap.filter.escape_filter_chars(description)
+            filter = filter + '(description=%s)' % escaped_value
+            counter = counter + 1
+
+        if certificate:
+            escaped_value = ldap.filter.escape_filter_chars(certificate)
+            filter = filter + '(userCertificate=%s)' % escaped_value
+            counter = counter + 1
+
+        if counter == 0:
+            filter = 'objectClass=*'
+
+        elif counter == 1:
+            pass
+
+        else:
+            filter = '(&' + filter + ')'
+
+        con = self.open_database()
+
+        entries = con.search_s(
+            'ou=people,%s' % base_dn,
+            ldap.SCOPE_ONELEVEL,
+            filter,
+            None)
+
+        con.unbind_s()
+
+        users = []
+        for entry in entries:
+            users.append(self.create_user_object(entry))
+
+        return users
+
+    def get_system_user(self, user_id):
+
+        base_dn = self.config['internaldb.basedn']
+
+        con = self.open_database()
+
+        entries = con.search_s(
+            'uid=%s,ou=people,%s' % (user_id, base_dn),
+            ldap.SCOPE_BASE,
+            '(objectclass=*)',
+            None)
+
+        con.unbind_s()
+
+        entry = entries[0]
+
+        return self.create_user_object(entry)
+
+    def add_system_user(self, user):
+
+        base_dn = self.config['internaldb.basedn']
+        dn = 'uid=%s,ou=people,%s' % (user['uid'], base_dn)
+
+        attrs = self.create_user_attrs(user)
+
+        con = self.open_database()
+
+        con.add_s(dn, attrs)
+
+        con.unbind_s()
+
+    def modify_system_user(self, user_id, changes):
+
+        base_dn = self.config['internaldb.basedn']
+        dn = 'uid=%s,ou=people,%s' % (user_id, base_dn)
+
+        modlist = self.create_user_modlist(changes)
+
+        con = self.open_database()
+
+        con.modify_s(dn, modlist)
+
+        con.unbind_s()
+
+    def remove_system_user(self, user_id):
+
+        base_dn = self.config['internaldb.basedn']
+
+        con = self.open_database()
+
+        con.delete_s(
+            'uid=%s,ou=people,%s' % (user_id, base_dn))
+
+        con.unbind_s()
+
+    def create_group_from_ldap_entry(self, entry):
+
+        group = {}
+        attrs = entry[1]
+
+        group['cn'] = attrs['cn'][0]
+
+        if 'description' in attrs:
+            group['description'] = attrs['description'][0]
+
+
+        return group
+
+    def create_ldap_attrs_from_group(self, user):
+
+        attrs = [
+            ('objectclass', [
+                'top',
+                'groupOfUniqueNames'
+            ]),
+            ('cn', [group['cn']]),
+        ]
+
+        if 'description' in user:
+            attrs.append(('description', [group['description']]))
+
+        return attrs
+
+    def get_system_groups(self):
+
+        base_dn = self.config['internaldb.basedn']
+
+        con = self.open_database()
+
+        entries = con.search_s(
+            'ou=groups,%s' % base_dn,
+            ldap.SCOPE_ONELEVEL,
+            '(objectClass=*)',
+            None)
+
+        con.unbind_s()
+
+        groups = []
+        for entry in entries:
+            groups.append(self.create_group_from_ldap_entry(entry))
+
+        return groups
+
+    def get_system_group(self, group_id):
+
+        base_dn = self.config['internaldb.basedn']
+
+        con = self.open_database()
+
+        entries = con.search_s(
+            'cn=%s,ou=groups,%s' % (group_id, base_dn),
+            ldap.SCOPE_BASE,
+            '(objectclass=*)',
+            None)
+
+        con.unbind_s()
+
+        entry = entries[0]
+
+        return self.create_group_from_ldap_entry(entry)
+
+    def add_system_group(self, group):
+
+        base_dn = self.config['internaldb.basedn']
+        dn = 'cn=%s,ou=groups,%s' % (group['cn'], base_dn)
+
+        attrs = self.create_ldap_attrs_from_group(group)
+
+        con = self.open_database()
+
+        con.add_s(dn, attrs)
+
+        con.unbind_s()
+
+    def remove_system_group(self, group_id):
+
+        base_dn = self.config['internaldb.basedn']
+
+        con = self.open_database()
+
+        con.delete_s(
+            'cn=%s,ou=groups,%s' % (group_id, base_dn))
+
+        con.unbind_s()
+
+    def get_system_group_members(self, group_id):
+
+        base_dn = self.config['internaldb.basedn']
+
+        con = self.open_database()
+
+        entries = con.search_s(
+            'cn=%s,ou=groups,%s' % (group_id, base_dn),
+            ldap.SCOPE_BASE,
+            '(objectclass=*)',
+            None)
+
+        con.unbind_s()
+
+        entry = entries[0]
+        attrs = entry[1]
+
+        if 'uniqueMember' in attrs:
+            members = attrs['uniqueMember']
+        else:
+            members = []
+
+        user_ids = []
+        for member_dn in members:
+            user_id = ldap.dn.explode_dn(member_dn, notypes=True)[0]
+            user_ids.append(user_id)
+
+        return user_ids
+
+    def add_system_group_member(self, group_id, member_id):
+
+        base_dn = self.config['internaldb.basedn']
+        group_dn = 'cn=%s,ou=groups,%s' % (group_id, base_dn)
+        member_dn = 'uid=%s,ou=people,%s' % (member_id, base_dn)
+
+        modlist = [
+            (ldap.MOD_ADD, 'uniqueMember', [member_dn]) 
+        ]
+
+        con = self.open_database()
+
+        con.modify_s(group_dn, modlist)
+
+        con.unbind_s()
+
+    def remove_system_group_member(self, group_id, member_id):
+
+        base_dn = self.config['internaldb.basedn']
+        group_dn = 'cn=%s,ou=groups,%s' % (group_id, base_dn)
+        member_dn = 'uid=%s,ou=people,%s' % (member_id, base_dn)
+
+        modlist = [
+            (ldap.MOD_DELETE, 'uniqueMember', [member_dn]) 
+        ]
+
+        con = self.open_database()
+
+        con.modify_s(group_dn, modlist)
+
+        con.unbind_s()
diff --git a/base/common/python/pki/server/cli/__init__.py b/base/common/python/pki/server/cli/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..dd878d6dfb6877567d383926e22944a35bfeafe6
--- /dev/null
+++ b/base/common/python/pki/server/cli/__init__.py
@@ -0,0 +1,20 @@
+#!/usr/bin/python
+# Authors:
+#     Endi S. Dewata <edewata at redhat.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Copyright (C) 2015 Red Hat, Inc.
+# All rights reserved.
+#
diff --git a/base/common/python/pki/server/cli/ca.py b/base/common/python/pki/server/cli/ca.py
new file mode 100644
index 0000000000000000000000000000000000000000..e89d564932a4423664de1488387b15ac937e5040
--- /dev/null
+++ b/base/common/python/pki/server/cli/ca.py
@@ -0,0 +1,38 @@
+#!/usr/bin/python
+# Authors:
+#     Endi S. Dewata <edewata at redhat.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Copyright (C) 2015 Red Hat, Inc.
+# All rights reserved.
+#
+
+import pki.cli
+import pki.server.cli.cert
+import pki.server.cli.config
+import pki.server.cli.group
+import pki.server.cli.user
+
+
+class CACLI(pki.cli.CLI):
+
+    def __init__(self):
+        super(CACLI, self).__init__('ca',
+                                    'CA management commands')
+
+        self.add_module(pki.server.cli.cert.CertCLI())
+        self.add_module(pki.server.cli.config.ConfigCLI())
+        self.add_module(pki.server.cli.group.GroupCLI())
+        self.add_module(pki.server.cli.user.UserCLI())
diff --git a/base/common/python/pki/server/cli/cert.py b/base/common/python/pki/server/cli/cert.py
new file mode 100644
index 0000000000000000000000000000000000000000..c58275eeabd27f54271cbc7e086ac93cd0de8146
--- /dev/null
+++ b/base/common/python/pki/server/cli/cert.py
@@ -0,0 +1,156 @@
+#!/usr/bin/python
+# Authors:
+#     Endi S. Dewata <edewata at redhat.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Copyright (C) 2015 Red Hat, Inc.
+# All rights reserved.
+#
+
+import base64
+import getopt
+import ldap
+import os
+import sys
+
+import pki.cli
+from pki.server import CAInstance
+
+
+class CertCLI(pki.cli.CLI):
+
+    def __init__(self):
+        super(CertCLI, self).__init__('cert',
+                                      'Certificate management commands')
+
+        self.add_module(CertFindCLI())
+        self.add_module(CertShowCLI())
+
+    @staticmethod
+    def print_cert(cert, all=False):
+
+        print '  Tag: %s' % cert['tag']
+        print '  Nickname: %s' % cert['nickname']
+        print '  Version: %s' % cert['version']
+        print '  Serial Number: %s' % cert['serial_number']
+        print '  Issuer: %s' % cert['issuer']
+        print '  Subject: %s' % cert['subject']
+
+        if not all:
+            return
+
+        print '  Usage: %s' % cert['usage']
+        print '  Token: %s' % cert['token']
+
+        if 'certificate' in cert:
+            print '  Certificate: %s' % base64.b64encode(cert['certificate'])
+
+        if 'request' in cert:
+            print '  Request: %s' % base64.b64encode(cert['request'])
+
+
+class CertFindCLI(pki.cli.CLI):
+
+    def __init__(self):
+        super(CertFindCLI, self).__init__('find', 'Find certificates')
+
+    def print_help(self):
+        print 'Usage: pki-server %s [OPTIONS]' % self.get_full_name()
+        print
+        print '  -v, --verbose                Run in verbose mode.'
+        print '      --help                   Show help message.'
+        print
+
+    def execute(self, argv):
+
+        try:
+            opts, _ = getopt.gnu_getopt(argv, 'i:v', [
+                'verbose', 'help'])
+
+        except getopt.GetoptError as e:
+            print 'ERROR: ' + str(e)
+            self.print_help()
+            sys.exit(1)
+
+        for o, _ in opts:
+            if o in ('-v', '--verbose'):
+                self.set_verbose(True)
+
+            elif o == '--help':
+                self.print_help()
+                sys.exit()
+
+            else:
+                print 'ERROR: unknown option ' + o
+                self.print_help()
+                sys.exit(1)
+
+        ca = CAInstance()
+
+        certs = ca.get_system_certs()
+
+        for cert in certs:
+            CertCLI.print_cert(cert)
+            print
+
+
+class CertShowCLI(pki.cli.CLI):
+
+    def __init__(self):
+        super(CertShowCLI, self).__init__('show', 'Show certificate')
+
+    def print_help(self):
+        print 'Usage: pki-server %s [OPTIONS] <certificate tag>' % self.get_full_name()
+        print
+        print '  -v, --verbose                Run in verbose mode.'
+        print '      --help                   Show help message.'
+        print
+
+    def execute(self, argv):
+
+        try:
+            opts, args = getopt.gnu_getopt(argv, 'i:v', [
+                'verbose', 'help'])
+
+        except getopt.GetoptError as e:
+            print 'ERROR: ' + str(e)
+            self.print_help()
+            sys.exit(1)
+
+        if len(args) != 1:
+            print 'ERROR: missing certificate tag'
+            self.print_help()
+            sys.exit(1)
+
+        tag = args[0]
+
+        for o, _ in opts:
+            if o in ('-v', '--verbose'):
+                self.set_verbose(True)
+
+            elif o == '--help':
+                self.print_help()
+                sys.exit()
+
+            else:
+                print 'ERROR: unknown option ' + o
+                self.print_help()
+                sys.exit(1)
+
+        ca = CAInstance()
+
+        cert = ca.get_system_cert(tag)
+
+        CertCLI.print_cert(cert, True)
diff --git a/base/common/python/pki/server/cli/config.py b/base/common/python/pki/server/cli/config.py
new file mode 100644
index 0000000000000000000000000000000000000000..63a846ddae3c36bcea150799607361cbefa889ec
--- /dev/null
+++ b/base/common/python/pki/server/cli/config.py
@@ -0,0 +1,90 @@
+#!/usr/bin/python
+# Authors:
+#     Endi S. Dewata <edewata at redhat.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Copyright (C) 2015 Red Hat, Inc.
+# All rights reserved.
+#
+
+import base64
+import getopt
+import ldap
+import os
+import sys
+
+import pki.cli
+from pki.server import CAInstance
+
+
+class ConfigCLI(pki.cli.CLI):
+
+    def __init__(self):
+        super(ConfigCLI, self).__init__('config',
+                                      'Configuration management commands')
+
+        self.add_module(ConfigShowCLI())
+
+    @staticmethod
+    def print_config(config, all=False):
+
+        print '  Instance ID: %s' % config['service.instanceID']
+        print '  Instance Directory: %s' % config['service.instanceDir']
+        print '  Port: %s' % config['service.unsecurePort']
+        print '  Secure Port: %s' % config['service.securePort']
+        print '  Client Auth Secure Port: %s' % config['service.clientauth_securePort']
+        print '  Non-Client Auth Secure Port: %s' % config['service.non_clientauth_securePort']
+        print '  Security Domain Port: %s' % config['service.securityDomainPort']
+
+
+class ConfigShowCLI(pki.cli.CLI):
+
+    def __init__(self):
+        super(ConfigShowCLI, self).__init__('show', 'Show configuration')
+
+    def print_help(self):
+        print 'Usage: pki-server %s [OPTIONS]' % self.get_full_name()
+        print
+        print '  -v, --verbose                Run in verbose mode.'
+        print '      --help                   Show help message.'
+        print
+
+    def execute(self, argv):
+
+        try:
+            opts, args = getopt.gnu_getopt(argv, 'i:v', [
+                'verbose', 'help'])
+
+        except getopt.GetoptError as e:
+            print 'ERROR: ' + str(e)
+            self.print_help()
+            sys.exit(1)
+
+        for o, _ in opts:
+            if o in ('-v', '--verbose'):
+                self.set_verbose(True)
+
+            elif o == '--help':
+                self.print_help()
+                sys.exit()
+
+            else:
+                print 'ERROR: unknown option ' + o
+                self.print_help()
+                sys.exit(1)
+
+        ca = CAInstance()
+
+        ConfigCLI.print_config(ca.config)
diff --git a/base/common/python/pki/server/cli/group.py b/base/common/python/pki/server/cli/group.py
new file mode 100644
index 0000000000000000000000000000000000000000..b8ca5af6931e73a1e274c232a84b82d00144aa92
--- /dev/null
+++ b/base/common/python/pki/server/cli/group.py
@@ -0,0 +1,441 @@
+#!/usr/bin/python
+# Authors:
+#     Endi S. Dewata <edewata at redhat.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Copyright (C) 2015 Red Hat, Inc.
+# All rights reserved.
+#
+
+import base64
+import getopt
+import os
+import sys
+
+import pki.cli
+from pki.server import CAInstance
+from pki.server.cli.user import UserCLI
+
+
+class GroupCLI(pki.cli.CLI):
+
+    def __init__(self):
+        super(GroupCLI, self).__init__('group',
+                                      'Group management commands')
+
+        self.add_module(GroupFindCLI())
+        self.add_module(GroupShowCLI())
+        self.add_module(GroupAddCLI())
+        self.add_module(GroupRemoveCLI())
+        self.add_module(GroupMemberCLI())
+
+    @staticmethod
+    def print_group(group, all=False):
+
+        print '  Group ID: %s' % group['cn']
+
+        if 'description' in group:
+            print '  Description: %s' % group['description']
+
+        if not all:
+            return
+
+
+class GroupFindCLI(pki.cli.CLI):
+
+    def __init__(self):
+        super(GroupFindCLI, self).__init__('find', 'Find groups')
+
+    def print_help(self):
+        print 'Usage: pki-server group-find [OPTIONS]'
+        print
+        print '  -v, --verbose                Run in verbose mode.'
+        print '      --help                   Show help message.'
+        print
+
+    def execute(self, argv):
+
+        try:
+            opts, _ = getopt.gnu_getopt(argv, 'i:v', [
+                'verbose', 'help'])
+
+        except getopt.GetoptError as e:
+            print 'ERROR: ' + str(e)
+            self.print_help()
+            sys.exit(1)
+
+        for o, _ in opts:
+            if o in ('-v', '--verbose'):
+                self.set_verbose(True)
+
+            elif o == '--help':
+                self.print_help()
+                sys.exit()
+
+            else:
+                print 'ERROR: unknown option ' + o
+                self.print_help()
+                sys.exit(1)
+
+        ca = CAInstance()
+
+        groups = ca.get_system_groups()
+
+        for group in groups:
+            GroupCLI.print_group(group)
+            print
+
+
+class GroupShowCLI(pki.cli.CLI):
+
+    def __init__(self):
+        super(GroupShowCLI, self).__init__('show', 'Show group')
+
+    def print_help(self):
+        print 'Usage: pki-server group-show [OPTIONS] <group ID>'
+        print
+        print '  -v, --verbose                Run in verbose mode.'
+        print '      --help                   Show help message.'
+        print
+
+    def execute(self, argv):
+
+        try:
+            opts, args = getopt.gnu_getopt(argv, 'i:v', [
+                'verbose', 'help'])
+
+        except getopt.GetoptError as e:
+            print 'ERROR: ' + str(e)
+            self.print_help()
+            sys.exit(1)
+
+        if len(args) != 1:
+            print 'ERROR: missing group ID'
+            self.print_help()
+            sys.exit(1)
+
+        group_id = args[0]
+
+        for o, _ in opts:
+            if o in ('-v', '--verbose'):
+                self.set_verbose(True)
+
+            elif o == '--help':
+                self.print_help()
+                sys.exit()
+
+            else:
+                print 'ERROR: unknown option ' + o
+                self.print_help()
+                sys.exit(1)
+
+        ca = CAInstance()
+
+        group = ca.get_system_group(group_id)
+
+        GroupCLI.print_group(group, True)
+
+
+class GroupAddCLI(pki.cli.CLI):
+
+    def __init__(self):
+        super(GroupAddCLI, self).__init__('add', 'Add group')
+
+    def print_help(self):
+        print 'Usage: pki-server group-add [OPTIONS] <group ID>'
+        print
+        print '  -v, --verbose                Run in verbose mode.'
+        print '      --help                   Show help message.'
+        print
+
+    def execute(self, argv):
+
+        try:
+            opts, args = getopt.gnu_getopt(argv, 'i:v', [
+                'desc=', 'verbose', 'help'])
+
+        except getopt.GetoptError as e:
+            print 'ERROR: ' + str(e)
+            self.print_help()
+            sys.exit(1)
+
+        if len(args) != 1:
+            print 'ERROR: missing group ID'
+            self.print_help()
+            sys.exit(1)
+
+        group_id = args[0]
+        description = None
+
+        for o, a in opts:
+            if o in ('-v', '--verbose'):
+                self.set_verbose(True)
+
+            elif o == '--desc':
+                description = a
+
+            elif o == '--help':
+                self.print_help()
+                sys.exit()
+
+            else:
+                print 'ERROR: unknown option ' + o
+                self.print_help()
+                sys.exit(1)
+
+        group = {}
+        group['cn'] = group_id
+
+        if description:
+            group['description'] = description
+
+        ca = CAInstance()
+
+        ca.add_system_group(group)
+
+
+class GroupRemoveCLI(pki.cli.CLI):
+
+    def __init__(self):
+        super(GroupRemoveCLI, self).__init__('del', 'Remove group')
+
+    def print_help(self):
+        print 'Usage: pki-server group-del [OPTIONS] <group ID>'
+        print
+        print '  -v, --verbose                Run in verbose mode.'
+        print '      --help                   Show help message.'
+        print
+
+    def execute(self, argv):
+
+        try:
+            opts, args = getopt.gnu_getopt(argv, 'i:v', [
+                'verbose', 'help'])
+
+        except getopt.GetoptError as e:
+            print 'ERROR: ' + str(e)
+            self.print_help()
+            sys.exit(1)
+
+        if len(args) != 1:
+            print 'ERROR: missing group ID'
+            self.print_help()
+            sys.exit(1)
+
+        group_id = args[0]
+
+        for o, _ in opts:
+            if o in ('-v', '--verbose'):
+                self.set_verbose(True)
+
+            elif o == '--help':
+                self.print_help()
+                sys.exit()
+
+            else:
+                print 'ERROR: unknown option ' + o
+                self.print_help()
+                sys.exit(1)
+
+        ca = CAInstance()
+
+        ca.remove_system_group(group_id)
+
+
+class GroupMemberCLI(pki.cli.CLI):
+
+    def __init__(self):
+        super(GroupMemberCLI, self).__init__('member',
+                                      'Group member management commands')
+
+        self.add_module(GroupMemberFindCLI())
+        self.add_module(GroupMemberAddCLI())
+        self.add_module(GroupMemberRemoveCLI())
+
+
+class GroupMemberFindCLI(pki.cli.CLI):
+
+    def __init__(self):
+        super(GroupMemberFindCLI, self).__init__('find', 'Find group members')
+
+    def print_help(self):
+        print 'Usage: pki-server group-member-find [OPTIONS] <group ID>'
+        print
+        print '  -v, --verbose                Run in verbose mode.'
+        print '      --help                   Show help message.'
+        print
+
+    def execute(self, argv):
+
+        try:
+            opts, args = getopt.gnu_getopt(argv, 'i:v', [
+                'desc=', 'verbose', 'help'])
+
+        except getopt.GetoptError as e:
+            print 'ERROR: ' + str(e)
+            self.print_help()
+            sys.exit(1)
+
+        if len(args) < 1:
+            print 'ERROR: missing group ID'
+            self.print_help()
+            sys.exit(1)
+
+        if len(args) > 1:
+            print 'ERROR: too many arguments'
+            self.print_help()
+            sys.exit(1)
+
+        group_id = args[0]
+
+        for o, _ in opts:
+            if o in ('-v', '--verbose'):
+                self.set_verbose(True)
+
+            elif o == '--help':
+                self.print_help()
+                sys.exit()
+
+            else:
+                print 'ERROR: unknown option ' + o
+                self.print_help()
+                sys.exit(1)
+
+        ca = CAInstance()
+
+        user_ids = ca.get_system_group_members(group_id)
+
+        for user_id in user_ids:
+            user = ca.get_system_user(user_id)
+            UserCLI.print_user(user)
+            print
+
+
+class GroupMemberAddCLI(pki.cli.CLI):
+
+    def __init__(self):
+        super(GroupMemberAddCLI, self).__init__('add', 'Add group member')
+
+    def print_help(self):
+        print 'Usage: pki-server group-member-add [OPTIONS] <group ID> <user ID>'
+        print
+        print '  -v, --verbose                Run in verbose mode.'
+        print '      --help                   Show help message.'
+        print
+
+    def execute(self, argv):
+
+        try:
+            opts, args = getopt.gnu_getopt(argv, 'i:v', [
+                'desc=', 'verbose', 'help'])
+
+        except getopt.GetoptError as e:
+            print 'ERROR: ' + str(e)
+            self.print_help()
+            sys.exit(1)
+
+        if len(args) < 1:
+            print 'ERROR: missing group ID'
+            self.print_help()
+            sys.exit(1)
+
+        if len(args) < 2:
+            print 'ERROR: missing user ID'
+            self.print_help()
+            sys.exit(1)
+
+        if len(args) > 2:
+            print 'ERROR: too many arguments'
+            self.print_help()
+            sys.exit(1)
+
+        group_id = args[0]
+        user_id = args[1]
+
+        for o, _ in opts:
+            if o in ('-v', '--verbose'):
+                self.set_verbose(True)
+
+            elif o == '--help':
+                self.print_help()
+                sys.exit()
+
+            else:
+                print 'ERROR: unknown option ' + o
+                self.print_help()
+                sys.exit(1)
+
+        ca = CAInstance()
+
+        ca.add_system_group_member(group_id, user_id)
+
+
+class GroupMemberRemoveCLI(pki.cli.CLI):
+
+    def __init__(self):
+        super(GroupMemberRemoveCLI, self).__init__('del', 'Remove group member')
+
+    def print_help(self):
+        print 'Usage: pki-server group-del-member [OPTIONS] <group ID> <user ID>'
+        print
+        print '  -v, --verbose                Run in verbose mode.'
+        print '      --help                   Show help message.'
+        print
+
+    def execute(self, argv):
+
+        try:
+            opts, args = getopt.gnu_getopt(argv, 'i:v', [
+                'verbose', 'help'])
+
+        except getopt.GetoptError as e:
+            print 'ERROR: ' + str(e)
+            self.print_help()
+            sys.exit(1)
+
+        if len(args) < 1:
+            print 'ERROR: missing group ID'
+            self.print_help()
+            sys.exit(1)
+
+        if len(args) < 2:
+            print 'ERROR: missing user ID'
+            self.print_help()
+            sys.exit(1)
+
+        if len(args) > 2:
+            print 'ERROR: too many arguments'
+            self.print_help()
+            sys.exit(1)
+
+        group_id = args[0]
+        user_id = args[1]
+
+        for o, _ in opts:
+            if o in ('-v', '--verbose'):
+                self.set_verbose(True)
+
+            elif o == '--help':
+                self.print_help()
+                sys.exit()
+
+            else:
+                print 'ERROR: unknown option ' + o
+                self.print_help()
+                sys.exit(1)
+
+        ca = CAInstance()
+
+        ca.remove_system_group_member(group_id, user_id)
diff --git a/base/common/python/pki/server/cli/user.py b/base/common/python/pki/server/cli/user.py
new file mode 100644
index 0000000000000000000000000000000000000000..e4a8dcafecb8f23412f4b6e5008d8b1ceff9fd77
--- /dev/null
+++ b/base/common/python/pki/server/cli/user.py
@@ -0,0 +1,406 @@
+#!/usr/bin/python
+# Authors:
+#     Endi S. Dewata <edewata at redhat.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Copyright (C) 2015 Red Hat, Inc.
+# All rights reserved.
+#
+
+import base64
+import getopt
+import os
+import sys
+
+import pki.cli
+from pki.server import CAInstance
+
+
+class UserCLI(pki.cli.CLI):
+
+    def __init__(self):
+        super(UserCLI, self).__init__('user',
+                                      'User management commands')
+
+        self.add_module(UserFindCLI())
+        self.add_module(UserShowCLI())
+        self.add_module(UserAddCLI())
+        self.add_module(UserModifyCLI())
+        self.add_module(UserDelCLI())
+
+    @staticmethod
+    def print_user(user, all=False):
+
+        print '  User ID: %s' % user['uid']
+        print '  Common Name: %s' % user['cn']
+        print '  Surname: %s' % user['sn']
+        print '  Type: %s' % user['type']
+
+        if 'description' in user:
+            print '  Description: %s' % user['description']
+
+        if 'mail' in user:
+            print '  E-mail: %s' % user['mail']
+
+        if not all:
+            return
+
+        if 'certificate' in user:
+             print '  Certificate: %s' % base64.b64encode(user['certificate'])
+
+        if 'password' in user:
+            print '  Password: %s' % user['password']
+
+        if 'state' in user:
+            print '  State: %s' % user['state']
+
+
+class UserFindCLI(pki.cli.CLI):
+
+    def __init__(self):
+        super(UserFindCLI, self).__init__('find', 'Find users')
+
+    def print_help(self):
+        print 'Usage: pki-server %s [OPTIONS]' % self.get_full_name()
+        print
+        print '      --desc <description>     Description.'
+        print '      --cert <certificate>     Base-64 encoded certificate.'
+        print
+        print '  -v, --verbose                Run in verbose mode.'
+        print '      --help                   Show help message.'
+        print
+
+    def execute(self, argv):
+
+        try:
+            opts, _ = getopt.gnu_getopt(argv, 'i:v', [
+                'desc=', 'cert=',
+                'verbose', 'help'])
+
+        except getopt.GetoptError as e:
+            print 'ERROR: ' + str(e)
+            self.print_help()
+            sys.exit(1)
+
+        description = None
+        certificate = None
+
+        for o, a in opts:
+            if o in ('-v', '--verbose'):
+                self.set_verbose(True)
+
+            elif o == '--help':
+                self.print_help()
+                sys.exit()
+
+            elif o == '--desc':
+                description = a
+
+            elif o == '--cert':
+                certificate = base64.b64decode(a)
+
+            else:
+                print 'ERROR: unknown option ' + o
+                self.print_help()
+                sys.exit(1)
+
+        ca = CAInstance()
+
+        users = ca.find_system_users(
+            description=description,
+            certificate=certificate)
+
+        for user in users:
+            UserCLI.print_user(user)
+            print
+
+
+class UserShowCLI(pki.cli.CLI):
+
+    def __init__(self):
+        super(UserShowCLI, self).__init__('show', 'Show user')
+
+    def print_help(self):
+        print 'Usage: pki-server %s [OPTIONS] <user ID>' % self.get_full_name()
+        print
+        print '  -v, --verbose                Run in verbose mode.'
+        print '      --help                   Show help message.'
+        print
+
+    def execute(self, argv):
+
+        try:
+            opts, args = getopt.gnu_getopt(argv, 'i:v', [
+                'verbose', 'help'])
+
+        except getopt.GetoptError as e:
+            print 'ERROR: ' + str(e)
+            self.print_help()
+            sys.exit(1)
+
+        if len(args) != 1:
+            print 'ERROR: missing user ID'
+            self.print_help()
+            sys.exit(1)
+
+        user_id = args[0]
+
+        for o, _ in opts:
+            if o in ('-v', '--verbose'):
+                self.set_verbose(True)
+
+            elif o == '--help':
+                self.print_help()
+                sys.exit()
+
+            else:
+                print 'ERROR: unknown option ' + o
+                self.print_help()
+                sys.exit(1)
+
+        ca = CAInstance()
+
+        user = ca.get_system_user(user_id)
+
+        UserCLI.print_user(user, True)
+
+
+class UserAddCLI(pki.cli.CLI):
+
+    def __init__(self):
+        super(UserAddCLI, self).__init__('add', 'Add user')
+
+    def print_help(self):
+        print 'Usage: pki-server %s [OPTIONS] <user ID>' % self.get_full_name()
+        print
+        print '  -v, --verbose                Run in verbose mode.'
+        print '      --help                   Show help message.'
+        print
+
+    def execute(self, argv):
+
+        try:
+            opts, args = getopt.gnu_getopt(argv, 'i:v', [
+                'cn=', 'sn=', 'type=',
+                'desc=', 'mail=', 'cert=',
+                'password=', 'state=',
+                'verbose', 'help'])
+
+        except getopt.GetoptError as e:
+            print 'ERROR: ' + str(e)
+            self.print_help()
+            sys.exit(1)
+
+        if len(args) != 1:
+            print 'ERROR: missing user ID'
+            self.print_help()
+            sys.exit(1)
+
+        user_id = args[0]
+        cn = user_id
+        sn = user_id
+        type = 'adminType'
+        description = None
+        mail = None
+        certificate = None
+        password = None
+        state = None
+
+        for o, a in opts:
+            if o in ('-v', '--verbose'):
+                self.set_verbose(True)
+
+            elif o == '--cn':
+                cn = a
+
+            elif o == '--sn':
+                sn = a
+
+            elif o == '--type':
+                type = a
+
+            elif o == '--desc':
+                description = a
+
+            elif o == '--mail':
+                mail = a
+
+            elif o == '--cert':
+                certificate = a
+
+            elif o == '--password':
+                password = a
+
+            elif o == '--state':
+                state = a
+
+            elif o == '--help':
+                self.print_help()
+                sys.exit()
+
+            else:
+                print 'ERROR: unknown option ' + o
+                self.print_help()
+                sys.exit(1)
+
+        user = {}
+        user['uid'] = user_id
+        user['cn'] = cn
+        user['sn'] = sn
+        user['type'] = type
+
+        if description:
+            user['description'] = description
+
+        if mail:
+            user['mail'] = mail
+
+        if certificate:
+            user['certificate'] = base64.b64decode(certificate)
+
+        if password:
+            user['password'] = password
+
+        if state:
+            user['state'] = state
+
+        ca = CAInstance()
+
+        ca.add_system_user(user)
+
+
+class UserModifyCLI(pki.cli.CLI):
+
+    def __init__(self):
+        super(UserModifyCLI, self).__init__('mod', 'Modify user')
+
+    def print_help(self):
+        print 'Usage: pki-server %s [OPTIONS] <user ID>' % self.get_full_name()
+        print
+        print '  -v, --verbose                Run in verbose mode.'
+        print '      --help                   Show help message.'
+        print
+
+    def execute(self, argv):
+
+        try:
+            opts, args = getopt.gnu_getopt(argv, 'i:v', [
+                'cn=', 'sn=', 'type=',
+                'desc=', 'mail=', 'cert=',
+                'password=', 'state=',
+                'verbose', 'help'])
+
+        except getopt.GetoptError as e:
+            print 'ERROR: ' + str(e)
+            self.print_help()
+            sys.exit(1)
+
+        if len(args) != 1:
+            print 'ERROR: missing user ID'
+            self.print_help()
+            sys.exit(1)
+
+        user_id = args[0]
+
+        changes = {}
+
+        for o, a in opts:
+            if o in ('-v', '--verbose'):
+                self.set_verbose(True)
+
+            elif o == '--cn':
+                changes['cn'] = a
+
+            elif o == '--sn':
+                changes['sn'] = a
+
+            elif o == '--type':
+                changes['type'] = a
+
+            elif o == '--desc':
+                changes['description'] = a
+
+            elif o == '--mail':
+                changes['mail'] = a
+
+            elif o == '--cert':
+                changes['certificate'] = a
+
+            elif o == '--password':
+                changes['password'] = a
+
+            elif o == '--state':
+                changes['state'] = a
+
+            elif o == '--help':
+                self.print_help()
+                sys.exit()
+
+            else:
+                print 'ERROR: unknown option ' + o
+                self.print_help()
+                sys.exit(1)
+
+        ca = CAInstance()
+
+        ca.modify_system_user(user_id, changes)
+
+
+class UserDelCLI(pki.cli.CLI):
+
+    def __init__(self):
+        super(UserDelCLI, self).__init__('del', 'Delete user')
+
+    def print_help(self):
+        print 'Usage: pki-server %s [OPTIONS] <user ID>' % self.get_full_name()
+        print
+        print '  -v, --verbose                Run in verbose mode.'
+        print '      --help                   Show help message.'
+        print
+
+    def execute(self, argv):
+
+        try:
+            opts, args = getopt.gnu_getopt(argv, 'i:v', [
+                'verbose', 'help'])
+
+        except getopt.GetoptError as e:
+            print 'ERROR: ' + str(e)
+            self.print_help()
+            sys.exit(1)
+
+        if len(args) != 1:
+            print 'ERROR: missing user ID'
+            self.print_help()
+            sys.exit(1)
+
+        user_id = args[0]
+
+        for o, _ in opts:
+            if o in ('-v', '--verbose'):
+                self.set_verbose(True)
+
+            elif o == '--help':
+                self.print_help()
+                sys.exit()
+
+            else:
+                print 'ERROR: unknown option ' + o
+                self.print_help()
+                sys.exit(1)
+
+        ca = CAInstance()
+
+        ca.remove_system_user(user_id)
diff --git a/base/common/sbin/pki-server b/base/common/sbin/pki-server
new file mode 100644
index 0000000000000000000000000000000000000000..4749a8e5ad0b7b160b27f626326a70c1fccc50c4
--- /dev/null
+++ b/base/common/sbin/pki-server
@@ -0,0 +1,87 @@
+#!/usr/bin/python
+# Authors:
+#     Endi S. Dewata <edewata at redhat.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Copyright (C) 2015 Red Hat, Inc.
+# All rights reserved.
+#
+
+import getopt
+import sys
+
+import pki.cli
+import pki.server.cli.ca
+
+
+class PKIServerCLI(pki.cli.CLI):
+
+    def __init__(self):
+
+        super(PKIServerCLI, self).__init__('pki-server', 'PKI server command-line interface')
+
+        self.add_module(pki.server.cli.ca.CACLI())
+
+    def get_full_module_name(self, module_name):
+        return module_name
+
+    def print_help(self):
+
+        print 'Usage: pki-server [OPTIONS]'
+        print
+        print '  -v, --verbose                Run in verbose mode.'
+        print '      --debug                  Show debug messages.'
+        print '      --help                   Show help message.'
+        print
+
+        super(PKIServerCLI, self).print_help()
+
+    def execute(self, argv):
+
+        try:
+            opts, args = getopt.getopt(argv[1:], 'v', [
+                'verbose', 'debug', 'help'])
+
+        except getopt.GetoptError as e:
+            print 'ERROR: ' + str(e)
+            self.print_help()
+            sys.exit(1)
+
+        for o, _ in opts:
+            if o in ('-v', '--verbose'):
+                self.set_verbose(True)
+
+            elif o == '--debug':
+                self.set_verbose(True)
+                self.set_debug(True)
+
+            elif o == '--help':
+                self.print_help()
+                sys.exit()
+
+            else:
+                print 'ERROR: unknown option ' + o
+                self.print_help()
+                sys.exit(1)
+
+        if self.verbose:
+            print 'Command: %s' % ' '.join(args)
+
+        super(PKIServerCLI, self).execute(args)
+
+
+if __name__ == '__main__':
+    cli = PKIServerCLI()
+    cli.execute(sys.argv)
diff --git a/base/common/scripts/restore-subsystem-user.py b/base/common/scripts/restore-subsystem-user.py
new file mode 100755
index 0000000000000000000000000000000000000000..dd62e7daa0ab40fb46da232bd4d46a394cef02f1
--- /dev/null
+++ b/base/common/scripts/restore-subsystem-user.py
@@ -0,0 +1,113 @@
+#!/usr/bin/python
+# Authors:
+#     Endi S. Dewata <edewata at redhat.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Copyright (C) 2015 Red Hat, Inc.
+# All rights reserved.
+#
+
+import base64
+import ldap
+import sys
+
+from pki.server import CAInstance
+
+print 'Restoring subsystem user'
+
+ca = CAInstance()
+
+type = ca.config['cs.type']
+hostname = ca.config['machineName']
+port = ca.config['service.securePort']
+
+user_id = '%s-%s-%s' % (type, hostname, port)
+
+cert = ca.get_system_cert('subsystem')
+version = cert['version']
+serial_number = cert['serial_number']
+issuer = cert['issuer']
+subject = cert['subject']
+
+description = '%s;%s;%s;%s' % (version, serial_number, issuer, subject)
+print 'Subsystem certificate: %s' % description
+
+certificate = cert['certificate']
+print '-----BEGIN CERTIFICATE-----'
+print base64.b64encode(certificate)
+print '-----END CERTIFICATE-----'
+
+users = ca.find_system_users(certificate=certificate)
+members = ca.get_system_group_members('Subsystem Group')
+
+for user in users:
+    print 'User %s has subsystem certificate' % user['uid']
+
+    if user['uid'] in members:
+        print 'User already in Subsystem Group'
+
+        if 'description' in user:
+            desc = user['description']
+        else:
+            desc = None
+
+        if desc == description:
+            print 'User has the correct certificate mapping'
+            print 'User %s is the subsystem user' % user['uid']
+
+        else:
+            changes = {}
+            changes['description'] = description
+            ca.modify_system_user(user_id, changes)
+            print 'Certificate mapping fixed'
+            print 'Subsystem user %s restored' % user['uid']
+
+        sys.exit()
+
+# If the user with the subsystem certificate doesn't exist,
+# or if the user exists but not in the Subsystem Group,
+# create a new subsystem user
+
+try:
+    user = {}
+    user['uid'] = user_id
+    user['cn'] = user_id
+    user['sn'] = user_id
+    user['type'] = 'agentType'
+    user['description'] = description
+    user['certificate'] = certificate
+    user['state'] = '1'
+
+    ca.add_system_user(user)
+    print 'New subsystem user %s added' % user_id
+
+except ldap.ALREADY_EXISTS:
+    print 'Subsystem user %s already exists' % user_id
+    user = ca.get_system_user(user_id)
+
+    changes = {}
+    changes['description'] = description
+    changes['certificate'] = certificate
+    ca.modify_system_user(user_id, changes)
+    print 'User certificate updated'
+
+try:
+    ca.add_system_group_member('Subsystem Group', user_id)
+    print 'User added to Subsystem Group'
+
+except ldap.TYPE_OR_VALUE_EXISTS:
+    print 'User already in Subsystem Group'
+
+print 'Subsystem user %s restored' % user_id
-- 
1.7.1



More information about the Pki-devel mailing list