Skip to content

Commit b1a7e39

Browse files
committed
Introduce the gssapi-console utilitiy
This utility provides an interactive Python session that automatically sets up and tears down a self-contained krb5 realm. It does this through the use of the `gssapi.tests.k5test` module. The created `K5Realm` class is accessible inside the session as `REALM`, provides methods for manipulating the realm setup (such as creating principals, kinit-ing, etc). Additionally, `gssapi` and `gssapi.raw as gb` are automatically imported. Just like the normal Python interpreter, when no file is passed, interactive mode is used. Otherwise if a file is passed, the file is simply run in the context of the realm, unless the `-i` option is used. Comma-separated boolean options can also be passed to the realm constructor via the `--realm-args` option, in the form of `key=(true|false)`.
1 parent e101a79 commit b1a7e39

File tree

1 file changed

+142
-0
lines changed

1 file changed

+142
-0
lines changed

gssapi-console.py

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
#!/usr/bin/env python
2+
3+
# interactive console with easy access to krb5 test harness stuff
4+
5+
import code
6+
import os
7+
import sys
8+
import copy
9+
10+
from gssapi.tests import k5test
11+
12+
13+
READLINE_SRC = """
14+
# python startup file
15+
import readline
16+
import rlcompleter
17+
import atexit
18+
import os
19+
20+
completer = rlcompleter.Completer(globals())
21+
readline.set_completer(completer.complete)
22+
23+
# tab completion
24+
readline.parse_and_bind('Control-space: complete')
25+
# history file
26+
histfile = os.path.join(os.environ['HOME'], '.pythonhistory')
27+
try:
28+
readline.read_history_file(histfile)
29+
except IOError:
30+
pass
31+
32+
atexit.register(readline.write_history_file, histfile)
33+
del os, histfile, readline, rlcompleter
34+
"""
35+
36+
BANNER = """GSSAPI Interactive console
37+
Python {ver} on {platform}
38+
Type "help", "copyright", "credits" or "license" for more information about Python.
39+
40+
mechansim: {mech}, realm: {realm}, user: {user}, host: {host}
41+
(functions for controlling the realm available in `REALM`)"""
42+
43+
44+
class GSSAPIConsole(code.InteractiveConsole):
45+
def __init__(self, use_readline=True, realm_args={}, *args, **kwargs):
46+
code.InteractiveConsole.__init__(self, *args, **kwargs)
47+
48+
self.realm = self._create_realm(realm_args)
49+
self.locals['REALM'] = self.realm
50+
51+
self.runsource('import gssapi')
52+
self.runsource('import gssapi.raw as gb')
53+
54+
if use_readline:
55+
self._add_readline()
56+
57+
if os.environ.get('LD_LIBRARY_PATH'):
58+
self.realm.env['LD_LIBRARY_PATH'] = os.environ['LD_LIBRARY_PATH']
59+
60+
def _create_realm(self):
61+
return None
62+
63+
def _add_readline(self):
64+
res = self.runsource(READLINE_SRC, '<readline setup>', 'exec')
65+
66+
67+
class Krb5Console(GSSAPIConsole):
68+
def _create_realm(self, realm_args):
69+
return k5test.K5Realm(**realm_args)
70+
71+
72+
MECH_MAP = {'krb5': Krb5Console}
73+
74+
75+
import argparse
76+
parser = argparse.ArgumentParser(description='An interactive Python console with krb5 setup')
77+
parser.add_argument('file', metavar='FILE', nargs='?',
78+
help='a file to run', default=None)
79+
parser.add_argument('-i', default=False, dest='force_interactive',
80+
action='store_const', const=True,
81+
help='Force interactive mode when running with a file')
82+
parser.add_argument('--realm-args', default=None,
83+
help='A comma-separated list of key=(true|false) values to '
84+
'pass to the realm constructor')
85+
parser.add_argument('--mech', default='krb5',
86+
help='Which environment to setup up '
87+
'(supports krb5 [default])')
88+
89+
PARSED_ARGS = parser.parse_args()
90+
91+
92+
if PARSED_ARGS.mech not in MECH_MAP:
93+
sys.exit('The %s environment is not supported by the '
94+
'GSSAPI console' % PARSED_ARGS.mech)
95+
96+
97+
realm_args = {}
98+
if PARSED_ARGS.realm_args:
99+
for arg in PARSED_ARGS.realm_args.split(','):
100+
key, raw_val = arg.split('=')
101+
realm_args[key] = (raw_val.lower() == 'true')
102+
103+
console = MECH_MAP[PARSED_ARGS.mech](realm_args=realm_args)
104+
SAVED_ENV = None
105+
106+
try:
107+
# create the banner
108+
banner_text = BANNER.format(ver=sys.version, platform=sys.platform,
109+
mech=PARSED_ARGS.mech,
110+
realm=console.realm.realm,
111+
user=console.realm.user_princ,
112+
host=console.realm.host_princ)
113+
114+
# import the env
115+
SAVED_ENV = copy.deepcopy(os.environ)
116+
for k,v in console.realm.env.items():
117+
os.environ[k] = v
118+
119+
INTER = True
120+
# run the interactive interpreter
121+
if PARSED_ARGS.file is not None:
122+
if not PARSED_ARGS.force_interactive:
123+
INTER = False
124+
125+
with open(PARSED_ARGS.file) as src:
126+
console.runsource(src.read(), src.name, 'exec')
127+
128+
if INTER:
129+
console.interact(banner_text)
130+
131+
except (KeyboardInterrupt, EOFError):
132+
pass
133+
finally:
134+
# restore the env
135+
if SAVED_ENV is not None:
136+
for k in copy.deepcopy(os.environ):
137+
if k in SAVED_ENV:
138+
os.environ[k] = SAVED_ENV[k]
139+
else:
140+
del os.environ[k]
141+
142+
console.realm.stop()

0 commit comments

Comments
 (0)