SCAutolib 2.0.0__tar.gz → 3.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {SCAutolib-2.0.0/SCAutolib.egg-info → SCAutolib-3.0.0}/PKG-INFO +1 -1
- {SCAutolib-2.0.0 → SCAutolib-3.0.0}/SCAutolib/__init__.py +19 -20
- {SCAutolib-2.0.0 → SCAutolib-3.0.0}/SCAutolib/cli_commands.py +2 -1
- {SCAutolib-2.0.0 → SCAutolib-3.0.0}/SCAutolib/controller.py +167 -105
- SCAutolib-3.0.0/SCAutolib/enums.py +40 -0
- {SCAutolib-2.0.0 → SCAutolib-3.0.0}/SCAutolib/models/CA.py +111 -39
- SCAutolib-3.0.0/SCAutolib/models/card.py +445 -0
- {SCAutolib-2.0.0 → SCAutolib-3.0.0}/SCAutolib/models/file.py +15 -12
- {SCAutolib-2.0.0 → SCAutolib-3.0.0}/SCAutolib/models/gui.py +2 -1
- SCAutolib-3.0.0/SCAutolib/models/user.py +179 -0
- {SCAutolib-2.0.0 → SCAutolib-3.0.0}/SCAutolib/templates/user.cnf +1 -1
- {SCAutolib-2.0.0 → SCAutolib-3.0.0}/SCAutolib/utils.py +73 -50
- {SCAutolib-2.0.0 → SCAutolib-3.0.0/SCAutolib.egg-info}/PKG-INFO +1 -1
- {SCAutolib-2.0.0 → SCAutolib-3.0.0}/SCAutolib.egg-info/SOURCES.txt +1 -0
- {SCAutolib-2.0.0 → SCAutolib-3.0.0}/setup.py +1 -1
- {SCAutolib-2.0.0 → SCAutolib-3.0.0}/test/test_ca.py +18 -6
- {SCAutolib-2.0.0 → SCAutolib-3.0.0}/test/test_card.py +21 -2
- {SCAutolib-2.0.0 → SCAutolib-3.0.0}/test/test_cli.py +6 -0
- SCAutolib-3.0.0/test/test_controller.py +33 -0
- {SCAutolib-2.0.0 → SCAutolib-3.0.0}/test/test_openssl_conf.py +2 -1
- SCAutolib-3.0.0/test/test_user.py +26 -0
- {SCAutolib-2.0.0 → SCAutolib-3.0.0}/test/test_utils.py +2 -3
- SCAutolib-2.0.0/SCAutolib/models/card.py +0 -285
- SCAutolib-2.0.0/SCAutolib/models/user.py +0 -346
- SCAutolib-2.0.0/test/test_controller.py +0 -59
- SCAutolib-2.0.0/test/test_user.py +0 -48
- {SCAutolib-2.0.0 → SCAutolib-3.0.0}/LICENSE +0 -0
- {SCAutolib-2.0.0 → SCAutolib-3.0.0}/MANIFEST.in +0 -0
- {SCAutolib-2.0.0 → SCAutolib-3.0.0}/README.md +0 -0
- {SCAutolib-2.0.0 → SCAutolib-3.0.0}/SCAutolib/exceptions.py +0 -0
- {SCAutolib-2.0.0 → SCAutolib-3.0.0}/SCAutolib/models/__init__.py +0 -0
- {SCAutolib-2.0.0 → SCAutolib-3.0.0}/SCAutolib/models/authselect.py +0 -0
- {SCAutolib-2.0.0 → SCAutolib-3.0.0}/SCAutolib/models/log.py +0 -0
- {SCAutolib-2.0.0 → SCAutolib-3.0.0}/SCAutolib/templates/ca.cnf +0 -0
- {SCAutolib-2.0.0 → SCAutolib-3.0.0}/SCAutolib/templates/gnome_disable_welcome +0 -0
- {SCAutolib-2.0.0 → SCAutolib-3.0.0}/SCAutolib/templates/softhsm2.conf +0 -0
- {SCAutolib-2.0.0 → SCAutolib-3.0.0}/SCAutolib/templates/sssd.conf +0 -0
- {SCAutolib-2.0.0 → SCAutolib-3.0.0}/SCAutolib/templates/virt_cacard.service +0 -0
- {SCAutolib-2.0.0 → SCAutolib-3.0.0}/SCAutolib/templates/virtcacard.cil +0 -0
- {SCAutolib-2.0.0 → SCAutolib-3.0.0}/SCAutolib.egg-info/dependency_links.txt +0 -0
- {SCAutolib-2.0.0 → SCAutolib-3.0.0}/SCAutolib.egg-info/entry_points.txt +0 -0
- {SCAutolib-2.0.0 → SCAutolib-3.0.0}/SCAutolib.egg-info/requires.txt +0 -0
- {SCAutolib-2.0.0 → SCAutolib-3.0.0}/SCAutolib.egg-info/top_level.txt +0 -0
- {SCAutolib-2.0.0 → SCAutolib-3.0.0}/requirements.txt +0 -0
- {SCAutolib-2.0.0 → SCAutolib-3.0.0}/setup.cfg +0 -0
- {SCAutolib-2.0.0 → SCAutolib-3.0.0}/test/test_file.py +0 -0
- {SCAutolib-2.0.0 → SCAutolib-3.0.0}/test/test_sssd_conf.py +0 -0
- {SCAutolib-2.0.0 → SCAutolib-3.0.0}/tox.ini +0 -0
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
from enum import Enum, auto
|
|
2
|
-
|
|
3
1
|
import coloredlogs
|
|
4
2
|
import logging
|
|
5
3
|
import subprocess
|
|
@@ -7,6 +5,8 @@ from pathlib import Path
|
|
|
7
5
|
import time
|
|
8
6
|
from schema import Schema, Use, Or, And, Optional
|
|
9
7
|
|
|
8
|
+
from SCAutolib.enums import CardType, UserType
|
|
9
|
+
|
|
10
10
|
fmt = "%(name)s:%(module)s.%(funcName)s.%(lineno)d [%(levelname)s] %(message)s"
|
|
11
11
|
date_fmt = "%H:%M:%S"
|
|
12
12
|
coloredlogs.install(level="DEBUG", fmt=fmt, datefmt=date_fmt,
|
|
@@ -49,24 +49,23 @@ schema_cas = Schema(And(
|
|
|
49
49
|
# Specify validation schema for all users
|
|
50
50
|
schema_user = Schema({'name': Use(str),
|
|
51
51
|
'passwd': Use(str),
|
|
52
|
-
'
|
|
53
|
-
Optional('card_dir', default=None): Use(Path),
|
|
54
|
-
'card_type': Or("virtual", "real", "removinator"),
|
|
55
|
-
Optional('cert', default=None): Use(Path),
|
|
56
|
-
Optional('key', default=None): Use(Path),
|
|
57
|
-
'local': Use(bool)})
|
|
58
|
-
|
|
52
|
+
'user_type': Or(UserType.local, UserType.ipa)})
|
|
59
53
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
54
|
+
# Specify validation schema for all cards
|
|
55
|
+
schema_card = Schema({'name': Use(str),
|
|
56
|
+
'pin': Use(str),
|
|
57
|
+
Optional('card_details', default=None): Use(str),
|
|
58
|
+
'cardholder': Use(str),
|
|
59
|
+
'CN': Use(str),
|
|
60
|
+
Optional('UID', default=None): Use(str),
|
|
61
|
+
Optional('expires', default=None): Use(str),
|
|
62
|
+
'card_type': Or(CardType.virtual, CardType.physical),
|
|
63
|
+
'ca_name': Use(str),
|
|
64
|
+
Optional('ca_cert', default=None): Use(str),
|
|
65
|
+
Optional('slot', default=None): Use(str),
|
|
66
|
+
Optional('uri', default=None): Use(str),
|
|
67
|
+
Optional('cert', default=None): Use(str),
|
|
68
|
+
Optional('key', default=None): Use(str)})
|
|
70
69
|
|
|
71
70
|
|
|
72
71
|
def run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True,
|
|
@@ -85,7 +84,7 @@ def run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True,
|
|
|
85
84
|
:type sleep: int
|
|
86
85
|
:param return_code: acceptable return codes from given commands.
|
|
87
86
|
If check=True, and the return code of the cmd is not in the return_code
|
|
88
|
-
list
|
|
87
|
+
list a subprocess.CalledProcessError exception would be raised.
|
|
89
88
|
:type return_code: list
|
|
90
89
|
:param cmd: Command to be executed
|
|
91
90
|
:type cmd: list or str
|
|
@@ -6,8 +6,9 @@ import click
|
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
from sys import exit
|
|
8
8
|
|
|
9
|
-
from SCAutolib import logger, exceptions, schema_user
|
|
9
|
+
from SCAutolib import logger, exceptions, schema_user
|
|
10
10
|
from SCAutolib.controller import Controller
|
|
11
|
+
from SCAutolib.enums import ReturnCode
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
@click.group()
|
|
@@ -4,15 +4,16 @@ from schema import Schema, Use
|
|
|
4
4
|
from shutil import rmtree
|
|
5
5
|
from typing import Union
|
|
6
6
|
|
|
7
|
-
from SCAutolib import exceptions, schema_cas, schema_user
|
|
7
|
+
from SCAutolib import exceptions, schema_cas, schema_user, schema_card
|
|
8
8
|
from SCAutolib import (logger, run, LIB_DIR, LIB_BACKUP, LIB_DUMP,
|
|
9
9
|
LIB_DUMP_USERS, LIB_DUMP_CAS, LIB_DUMP_CARDS,
|
|
10
10
|
TEMPLATES_DIR)
|
|
11
11
|
from SCAutolib.models import CA, file, user, card, authselect as auth
|
|
12
|
-
from SCAutolib.models.file import File
|
|
13
|
-
from SCAutolib.
|
|
12
|
+
from SCAutolib.models.file import File, OpensslCnf
|
|
13
|
+
from SCAutolib.enums import (OSVersion, CardType, UserType)
|
|
14
|
+
from SCAutolib.utils import (_check_selinux, _gen_private_key,
|
|
14
15
|
_get_os_version, _install_packages,
|
|
15
|
-
_check_packages, dump_to_json,
|
|
16
|
+
_check_packages, dump_to_json, ca_factory)
|
|
16
17
|
|
|
17
18
|
|
|
18
19
|
class Controller:
|
|
@@ -68,12 +69,31 @@ class Controller:
|
|
|
68
69
|
def prepare(self, force: bool, gdm: bool, install_missing: bool,
|
|
69
70
|
graphical: bool):
|
|
70
71
|
"""
|
|
71
|
-
|
|
72
|
-
|
|
72
|
+
Prepare system for testing. This method provides complex configuration
|
|
73
|
+
of system under test for testing including creation of CAs, users and
|
|
74
|
+
smart cards in the system and objects that represents them in SCAutolib.
|
|
75
|
+
Configuration is based on config file and CLI options.
|
|
76
|
+
|
|
77
|
+
:param force: Defines if existing objects, files, users, services etc.
|
|
78
|
+
should be erased or overwritten if they already exist. True stands
|
|
79
|
+
for erase/overwrite. This parameter is forwarded to several methods
|
|
80
|
+
and it can have slightly different meaning in each of them.
|
|
81
|
+
For details see docstrings of the methods.
|
|
82
|
+
:type force: bool
|
|
83
|
+
|
|
84
|
+
:param gdm: If True, GDM package would be installed
|
|
85
|
+
:type gdm: bool
|
|
86
|
+
:param install_missing: If True, all missing packages would be
|
|
87
|
+
installed
|
|
88
|
+
:type install_missing: bool
|
|
89
|
+
:param graphical: If True, GUI tests dependencies are installed
|
|
90
|
+
:type graphical: bool
|
|
73
91
|
"""
|
|
74
92
|
self.setup_system(install_missing, gdm, graphical)
|
|
75
93
|
|
|
76
|
-
#
|
|
94
|
+
# Prepare CAs: Virtual cards are populated by certificates that are: a)
|
|
95
|
+
# created locally and signed by local CA configured on the system under
|
|
96
|
+
# test, or b) created and signed using FreeIPA.
|
|
77
97
|
try:
|
|
78
98
|
self.setup_local_ca(force=force)
|
|
79
99
|
except exceptions.SCAutolibWrongConfig as e:
|
|
@@ -82,9 +102,20 @@ class Controller:
|
|
|
82
102
|
self.setup_ipa_client(force=force)
|
|
83
103
|
except exceptions.SCAutolibWrongConfig as e:
|
|
84
104
|
logger.info(e)
|
|
105
|
+
|
|
85
106
|
for usr in self.lib_conf["users"]:
|
|
86
|
-
|
|
87
|
-
|
|
107
|
+
self.setup_user(usr, force=force)
|
|
108
|
+
|
|
109
|
+
# Create cards defined in config. For physical cards only objects will
|
|
110
|
+
# be created while for virtual cards tokens will be created and enrolled
|
|
111
|
+
for token in self.lib_conf["cards"]:
|
|
112
|
+
# prepare CA objects for physical cards
|
|
113
|
+
if token["card_type"] == CardType.physical:
|
|
114
|
+
self.setup_custom_ca(token)
|
|
115
|
+
self.setup_card(token)
|
|
116
|
+
elif token["card_type"] == CardType.virtual:
|
|
117
|
+
c = self.setup_card(token)
|
|
118
|
+
self.enroll_card(c)
|
|
88
119
|
|
|
89
120
|
def setup_system(self, install_missing: bool, gdm: bool, graphical: bool):
|
|
90
121
|
"""
|
|
@@ -97,6 +128,8 @@ class Controller:
|
|
|
97
128
|
:type install_missing: bool
|
|
98
129
|
:param gdm: If True, GDM package would be installed
|
|
99
130
|
:type gdm: bool
|
|
131
|
+
:param graphical: If True, GUI tests dependencies are installed
|
|
132
|
+
:type graphical: bool
|
|
100
133
|
:return:
|
|
101
134
|
"""
|
|
102
135
|
for d in (LIB_DIR, LIB_BACKUP, LIB_DUMP, LIB_DUMP_USERS, LIB_DUMP_CAS,
|
|
@@ -112,13 +145,15 @@ class Controller:
|
|
|
112
145
|
packages += ["tesseract", "ffmpeg-free"]
|
|
113
146
|
|
|
114
147
|
# Prepare for virtual cards
|
|
115
|
-
if
|
|
148
|
+
if any(c["card_type"] == CardType.virtual
|
|
149
|
+
for c in self.lib_conf["cards"]):
|
|
116
150
|
packages += ["pcsc-lite-ccid", "pcsc-lite", "virt_cacard",
|
|
117
151
|
"vpcd", "softhsm"]
|
|
118
152
|
run("dnf -y copr enable jjelen/vsmartcard")
|
|
119
153
|
|
|
120
154
|
# Add IPA packages if needed
|
|
121
|
-
if
|
|
155
|
+
if any([u["user_type"] != UserType.local
|
|
156
|
+
for u in self.lib_conf["users"]]):
|
|
122
157
|
packages += self._general_steps_for_ipa()
|
|
123
158
|
|
|
124
159
|
# Check for installed packages
|
|
@@ -148,16 +183,16 @@ class Controller:
|
|
|
148
183
|
self.sssd_conf.save()
|
|
149
184
|
self._general_steps_for_virtual_sc()
|
|
150
185
|
|
|
151
|
-
base_user = user.
|
|
186
|
+
base_user = user.User("base-user", "redhat")
|
|
152
187
|
base_user.add_user()
|
|
153
188
|
dump_to_json(base_user)
|
|
154
|
-
dump_to_json(user.
|
|
155
|
-
|
|
189
|
+
dump_to_json(user.User(username="root",
|
|
190
|
+
password=self.lib_conf["root_passwd"]))
|
|
156
191
|
|
|
157
192
|
def setup_local_ca(self, force: bool = False):
|
|
158
193
|
"""
|
|
159
194
|
Setup local CA based on configuration from the configuration file. All
|
|
160
|
-
necessary
|
|
195
|
+
necessary files for this operation (e.g. CNF file for self-signed root
|
|
161
196
|
certificate) would be created along the way.
|
|
162
197
|
|
|
163
198
|
:param force: If local CA already exists in given directory, specifies
|
|
@@ -171,11 +206,31 @@ class Controller:
|
|
|
171
206
|
raise exceptions.SCAutolibWrongConfig(msg)
|
|
172
207
|
|
|
173
208
|
ca_dir: Path = self.lib_conf["ca"]["local_ca"]["dir"]
|
|
174
|
-
|
|
209
|
+
ca_dir.mkdir(exist_ok=True, parents=True)
|
|
210
|
+
|
|
211
|
+
cnf = OpensslCnf(ca_dir.joinpath("ca.cnf"), "CA", str(ca_dir))
|
|
212
|
+
self.local_ca = ca_factory(path=ca_dir, cnf=cnf, create=True)
|
|
213
|
+
if force:
|
|
214
|
+
logger.warning(f"Removing previous local CA from {ca_dir}")
|
|
215
|
+
self.local_ca.cleanup()
|
|
216
|
+
cnf.create()
|
|
217
|
+
cnf.save()
|
|
218
|
+
self.local_ca.setup()
|
|
219
|
+
self.local_ca.update_ca_db()
|
|
220
|
+
run(["systemctl", "restart", "sssd"], sleep=5)
|
|
221
|
+
|
|
175
222
|
logger.info(f"Local CA is configured in {ca_dir}")
|
|
176
223
|
|
|
177
224
|
dump_to_json(self.local_ca)
|
|
178
225
|
|
|
226
|
+
def setup_custom_ca(self, card_data: dict):
|
|
227
|
+
if card_data["card_type"] == CardType.physical:
|
|
228
|
+
ca = ca_factory(create=True, card_data=card_data)
|
|
229
|
+
ca.setup()
|
|
230
|
+
if not ca._ca_cert.is_file():
|
|
231
|
+
raise FileNotFoundError(f"File not found: {ca._ca_cert}")
|
|
232
|
+
dump_to_json(ca)
|
|
233
|
+
|
|
179
234
|
def setup_ipa_client(self, force: bool = False):
|
|
180
235
|
"""
|
|
181
236
|
Configure IPA client for given IPA server on current host. IPA server
|
|
@@ -210,8 +265,7 @@ class Controller:
|
|
|
210
265
|
|
|
211
266
|
def setup_user(self, user_dict: dict, force: bool = False):
|
|
212
267
|
"""
|
|
213
|
-
Configure the user on the specified system (local machine/CA).
|
|
214
|
-
would be configured along with the card based on configurations.
|
|
268
|
+
Configure the user on the specified system (local machine/CA).
|
|
215
269
|
|
|
216
270
|
:param force: specify if the user should be re-created with its
|
|
217
271
|
card directory
|
|
@@ -221,41 +275,14 @@ class Controller:
|
|
|
221
275
|
:return: the user object
|
|
222
276
|
"""
|
|
223
277
|
new_user = None
|
|
224
|
-
card_dir: Path = user_dict["card_dir"]
|
|
225
|
-
# Card dir is home dir of the user home directory
|
|
226
|
-
if force and card_dir.exists():
|
|
227
|
-
rmtree(card_dir)
|
|
228
278
|
|
|
229
|
-
if user_dict["
|
|
279
|
+
if user_dict["user_type"] == UserType.local:
|
|
230
280
|
new_user = user.User(username=user_dict["name"],
|
|
231
|
-
|
|
232
|
-
password=user_dict["passwd"],
|
|
233
|
-
card_dir=card_dir,
|
|
234
|
-
cert=user_dict["cert"], key=user_dict["key"],
|
|
235
|
-
local=True)
|
|
281
|
+
password=user_dict["passwd"])
|
|
236
282
|
if force:
|
|
237
|
-
if new_user.cert and new_user.cert.exists():
|
|
238
|
-
self.local_ca.revoke_cert(new_user.cert)
|
|
239
283
|
new_user.delete_user()
|
|
240
|
-
user_dict["card_dir"].mkdir(exist_ok=True)
|
|
241
284
|
new_user.add_user()
|
|
242
285
|
|
|
243
|
-
csr_path = new_user.card_dir.joinpath(f"csr-{new_user.username}.csr")
|
|
244
|
-
cnf = file.OpensslCnf(filepath=csr_path, conf_type="user",
|
|
245
|
-
replace=new_user.username)
|
|
246
|
-
cnf.create()
|
|
247
|
-
cnf.save()
|
|
248
|
-
|
|
249
|
-
new_user.cnf = cnf.path
|
|
250
|
-
self.sssd_conf.set(
|
|
251
|
-
section=f"certmap/shadowutils/{new_user.username}",
|
|
252
|
-
key="matchrule",
|
|
253
|
-
value=f"<SUBJECT>.*CN={new_user.username}.*")
|
|
254
|
-
self.sssd_conf.save()
|
|
255
|
-
self.sssd_conf.update_default_content()
|
|
256
|
-
run(["systemctl", "restart", "sssd"])
|
|
257
|
-
logger.debug(f"Match rule for user {new_user.username} is added "
|
|
258
|
-
f"to /etc/sssd/sssd.conf")
|
|
259
286
|
else:
|
|
260
287
|
if self.ipa_ca is None:
|
|
261
288
|
msg = "Can't proceed in configuration of IPA user because no " \
|
|
@@ -263,75 +290,109 @@ class Controller:
|
|
|
263
290
|
raise exceptions.SCAutolibException(msg)
|
|
264
291
|
new_user = user.IPAUser(ipa_server=self.ipa_ca,
|
|
265
292
|
username=user_dict["name"],
|
|
266
|
-
|
|
267
|
-
password=user_dict["passwd"],
|
|
268
|
-
card_dir=card_dir,
|
|
269
|
-
cert=user_dict["cert"],
|
|
270
|
-
key=user_dict["key"],
|
|
271
|
-
local=False)
|
|
293
|
+
password=user_dict["passwd"])
|
|
272
294
|
if force:
|
|
273
|
-
if new_user.cert and new_user.cert.exists():
|
|
274
|
-
self.ipa_ca.revoke_cert(new_user.cert)
|
|
275
295
|
new_user.delete_user()
|
|
276
|
-
user_dict["card_dir"].mkdir(exist_ok=True)
|
|
277
|
-
|
|
278
296
|
new_user.add_user()
|
|
279
|
-
|
|
280
|
-
if user_dict["card_type"] == "virtual":
|
|
281
|
-
hsm_conf = file.SoftHSM2Conf(
|
|
282
|
-
new_user.card_dir.joinpath("sofhtsm2.conf"),
|
|
283
|
-
new_user.card_dir)
|
|
284
|
-
hsm_conf.create()
|
|
285
|
-
hsm_conf.save()
|
|
286
|
-
|
|
287
|
-
new_user.card = card.VirtualCard(new_user,
|
|
288
|
-
softhsm2_conf=hsm_conf.path)
|
|
289
|
-
else:
|
|
290
|
-
raise NotImplementedError("Other card type than 'virtual' does"
|
|
291
|
-
"not supported yet")
|
|
292
|
-
|
|
293
|
-
new_user.card.create()
|
|
294
297
|
self.users.append(new_user)
|
|
295
|
-
|
|
296
298
|
dump_to_json(new_user)
|
|
297
|
-
|
|
298
299
|
return new_user
|
|
299
300
|
|
|
300
|
-
def
|
|
301
|
+
def setup_card(self, card_dict: dict, force: bool = False):
|
|
301
302
|
"""
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
from corresponding CA.
|
|
303
|
+
Create card object. Card object should contain its root CA cert as it
|
|
304
|
+
represents general card (i.e. including physical read-only cards).
|
|
305
305
|
|
|
306
|
-
:param
|
|
307
|
-
:
|
|
306
|
+
:param card_dict: Dictionary containing card attributes
|
|
307
|
+
:type card_dict: dict
|
|
308
|
+
:param force: If its true and card directory exists it will be removed
|
|
309
|
+
:type force: bool
|
|
308
310
|
"""
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
if not user_.card:
|
|
312
|
-
raise exceptions.SCAutolibException(
|
|
313
|
-
f"Card for the user {user_.username} is not initialized")
|
|
311
|
+
card_dir: Path = Path("/root/cards", card_dict["name"])
|
|
312
|
+
card_dir.mkdir(parents=True, exist_ok=True)
|
|
314
313
|
|
|
315
|
-
if
|
|
316
|
-
|
|
314
|
+
if force and card_dir.exists():
|
|
315
|
+
rmtree(card_dir)
|
|
317
316
|
|
|
318
|
-
if
|
|
319
|
-
|
|
320
|
-
|
|
317
|
+
if card_dict["card_type"] == CardType.physical:
|
|
318
|
+
new_card = card.PhysicalCard(card_dict, card_dir=card_dir)
|
|
319
|
+
elif card_dict["card_type"] == CardType.virtual:
|
|
320
|
+
hsm_conf = self.prepare_softhsm_config(card_dir)
|
|
321
|
+
new_card = card.VirtualCard(card_dict, softhsm2_conf=hsm_conf.path,
|
|
322
|
+
card_dir=card_dir)
|
|
323
|
+
# card needs to know some details of its user
|
|
324
|
+
new_card.user = self.link_user_to_card(new_card)
|
|
325
|
+
if new_card.user.user_type == UserType.local:
|
|
326
|
+
new_card.cnf = self.prepare_user_cnf(new_card)
|
|
327
|
+
if force:
|
|
328
|
+
self.revoke_certs(new_card)
|
|
329
|
+
new_card.create()
|
|
330
|
+
else:
|
|
331
|
+
raise NotImplementedError("Other card types than 'physical' and "
|
|
332
|
+
"'virtual' are not supported")
|
|
333
|
+
|
|
334
|
+
dump_to_json(new_card)
|
|
335
|
+
return new_card
|
|
336
|
+
|
|
337
|
+
def link_user_to_card(self, card: card.VirtualCard):
|
|
338
|
+
for card_user in self.users:
|
|
339
|
+
if card_user.username == card.cardholder:
|
|
340
|
+
return card_user
|
|
341
|
+
|
|
342
|
+
def prepare_softhsm_config(self, card_dir: Path = None):
|
|
343
|
+
"""Prepare SoftHSM2 config for virtual card"""
|
|
344
|
+
filepath = card_dir.joinpath("sofhtsm2.conf")
|
|
345
|
+
hsm_conf = file.SoftHSM2Conf(filepath, card_dir=card_dir)
|
|
346
|
+
hsm_conf.create()
|
|
347
|
+
hsm_conf.save()
|
|
348
|
+
return hsm_conf
|
|
349
|
+
|
|
350
|
+
def prepare_user_cnf(self, card: card.VirtualCard):
|
|
351
|
+
"""Prepare user openssl cnf"""
|
|
352
|
+
cnf_path = card.card_dir.joinpath(f"{card.cardholder}.cnf")
|
|
353
|
+
cnf = file.OpensslCnf(filepath=cnf_path, conf_type="user",
|
|
354
|
+
replace=[card.cardholder, card.CN])
|
|
355
|
+
cnf.create()
|
|
356
|
+
cnf.save()
|
|
357
|
+
return cnf.path
|
|
358
|
+
|
|
359
|
+
def revoke_certs(self, card: card.VirtualCard):
|
|
360
|
+
if card.cert and card.cert.exists():
|
|
361
|
+
if card.user.user_type == UserType.local:
|
|
362
|
+
self.local_ca.revoke_cert(card.cert)
|
|
363
|
+
else:
|
|
364
|
+
self.ipa_ca.revoke_cert(card.cert)
|
|
365
|
+
|
|
366
|
+
def enroll_card(self, card: card.VirtualCard):
|
|
367
|
+
"""
|
|
368
|
+
Enroll the card - i.e. upload keys and certs to card. If private key
|
|
369
|
+
and/or the certificate do not exist, new one's would be requested
|
|
370
|
+
from corresponding CA.
|
|
371
|
+
|
|
372
|
+
:param card: card object
|
|
373
|
+
:type card: card.VirtualCard
|
|
374
|
+
"""
|
|
375
|
+
logger.debug(f"Starting enrollment of the card {card.name}")
|
|
376
|
+
if not card:
|
|
377
|
+
raise exceptions.SCAutolibException(
|
|
378
|
+
f"Card {card.name} is not initialized")
|
|
321
379
|
|
|
322
|
-
|
|
380
|
+
if not card.key.exists():
|
|
381
|
+
_gen_private_key(card.key)
|
|
323
382
|
|
|
383
|
+
if not card.cert.exists():
|
|
384
|
+
csr = card.gen_csr()
|
|
324
385
|
ca = self.ipa_ca \
|
|
325
|
-
if isinstance(
|
|
326
|
-
|
|
386
|
+
if isinstance(card.user, user.IPAUser) else self.local_ca
|
|
387
|
+
card.cert = ca.request_cert(csr, card.cardholder, card.cert)
|
|
327
388
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
dump_to_json(user_.card)
|
|
331
|
-
# Update the dump for current user to link it with the card
|
|
332
|
-
dump_to_json(user_)
|
|
389
|
+
card.enroll()
|
|
390
|
+
dump_to_json(card)
|
|
333
391
|
|
|
334
392
|
def cleanup(self):
|
|
393
|
+
# FIXME Card.delete is not yet implemented, sssd_conf.clean is broken,
|
|
394
|
+
# authselect.cleanup is not implemented, local_ca.cleanup is not
|
|
395
|
+
# sufficient
|
|
335
396
|
"""
|
|
336
397
|
Clean the system after setup. This method restores the SSSD config file,
|
|
337
398
|
deletes created users with cards, remove CA's (local and/or IPA Client)
|
|
@@ -339,18 +400,18 @@ class Controller:
|
|
|
339
400
|
self.sssd_conf.clean()
|
|
340
401
|
|
|
341
402
|
for user_file in LIB_DUMP_USERS.iterdir():
|
|
342
|
-
usr = user.
|
|
343
|
-
usr.
|
|
344
|
-
|
|
403
|
+
usr = user.User.load(user_file)
|
|
404
|
+
usr.delete_user()
|
|
405
|
+
for card_file in LIB_DUMP_CARDS.iterdir():
|
|
345
406
|
if card_file.exists():
|
|
346
|
-
card.Card.load(card_file
|
|
407
|
+
card.Card.load(card_file).delete() # TODO implement card.delete
|
|
347
408
|
|
|
348
409
|
if self.local_ca:
|
|
349
|
-
self.local_ca.cleanup()
|
|
410
|
+
self.local_ca.cleanup() # FIXME restore sssd_auth_ca_db.pem file
|
|
350
411
|
if self.ipa_ca:
|
|
351
412
|
self.ipa_ca.cleanup()
|
|
352
413
|
|
|
353
|
-
self.authselect.cleanup()
|
|
414
|
+
self.authselect.cleanup() # TODO implement authselect.cleanup
|
|
354
415
|
|
|
355
416
|
@staticmethod
|
|
356
417
|
def _validate_configuration(conf: dict, params: {} = None) -> dict:
|
|
@@ -376,7 +437,8 @@ class Controller:
|
|
|
376
437
|
# Specify general schema for whole config file
|
|
377
438
|
schema = Schema({"root_passwd": Use(str),
|
|
378
439
|
"ca": schema_cas,
|
|
379
|
-
"users": [schema_user]
|
|
440
|
+
"users": [schema_user],
|
|
441
|
+
"cards": [schema_card]})
|
|
380
442
|
|
|
381
443
|
return schema.validate(conf)
|
|
382
444
|
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from enum import Enum, auto
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class OSVersion(Enum):
|
|
5
|
+
"""
|
|
6
|
+
Enumeration for Linux versions. Used for more convenient checks.
|
|
7
|
+
"""
|
|
8
|
+
Fedora = 1
|
|
9
|
+
RHEL_9 = 2
|
|
10
|
+
RHEL_8 = 3
|
|
11
|
+
CentOS_8 = 4
|
|
12
|
+
CentOS_9 = 5
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class CardType(str, Enum):
|
|
16
|
+
physical = "physical"
|
|
17
|
+
virtual = "virtual"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class UserType(str, Enum):
|
|
21
|
+
local = "local"
|
|
22
|
+
ipa = "ipa"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class CAType(str, Enum):
|
|
26
|
+
local = "local"
|
|
27
|
+
custom = "custom"
|
|
28
|
+
ipa = "IPA"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class ReturnCode(Enum):
|
|
32
|
+
"""
|
|
33
|
+
Enum for return codes
|
|
34
|
+
"""
|
|
35
|
+
SUCCESS = 0
|
|
36
|
+
MISSING_CA = auto()
|
|
37
|
+
FAILURE = auto()
|
|
38
|
+
ERROR = auto()
|
|
39
|
+
EXCEPTION = auto()
|
|
40
|
+
UNKNOWN = auto()
|