Skip to content

Commit 4a74e13

Browse files
committed
Make createuser work for new installations and add basic coverage.
Allow user creation to provision the user_db_home directory as well as the file itself which is the situation after config generation is run to completion for a new installation. Cover the very basic operation of createuser ensuring that a user db and lock file are correctly create upon a call to create a test user.
1 parent 787f546 commit 4a74e13

File tree

6 files changed

+116
-5
lines changed

6 files changed

+116
-5
lines changed

mig/server/createuser.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,7 @@ def usage(name='createuser.py'):
9191
""" % {'name': name, 'cert_warn': cert_warn})
9292

9393

94-
if '__main__' == __name__:
95-
(args, app_dir, db_path) = init_user_adm()
94+
def main(args, cwd, configuration=None, db_path=keyword_auto):
9695
conf_path = None
9796
auth_type = 'custom'
9897
expire = None
@@ -190,8 +189,11 @@ def usage(name='createuser.py'):
190189
if verbose:
191190
print('using configuration from MIG_CONF (or default)')
192191

193-
configuration = get_configuration_object(config_file=conf_path)
192+
if configuration is None:
193+
configuration = get_configuration_object(config_file=conf_path)
194+
194195
logger = configuration.logger
196+
195197
# NOTE: we need explicit db_path lookup here for load_user_dict call
196198
if db_path == keyword_auto:
197199
db_path = default_db_path(configuration)
@@ -326,3 +328,7 @@ def usage(name='createuser.py'):
326328
if verbose:
327329
print('Cleaning up tmp file: %s' % user_file)
328330
os.remove(user_file)
331+
332+
if __name__ == '__main__':
333+
(args, cwd, db_path) = init_user_adm()
334+
main(args, cwd, db_path=db_path)

mig/shared/accountstate.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
from __future__ import print_function
3333
from __future__ import absolute_import
3434

35+
from past.builtins import basestring
3536
import os
3637
import time
3738

mig/shared/base.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import re
3737

3838
# IMPORTANT: do not import any other MiG modules here - to avoid import loops
39+
from mig.shared.compat import PY2
3940
from mig.shared.defaults import default_str_coding, default_fs_coding, \
4041
keyword_all, keyword_auto, sandbox_names, _user_invisible_files, \
4142
_user_invisible_dirs, _vgrid_xgi_scripts, cert_field_order, csrf_field, \
@@ -294,7 +295,9 @@ def canonical_user(configuration, user_dict, limit_fields):
294295
if key == 'full_name':
295296
# IMPORTANT: we get utf8 coded bytes here and title() treats such
296297
# chars as word termination. Temporarily force to unicode.
297-
val = force_utf8(force_unicode(val).title())
298+
val = force_unicode(val).title()
299+
if PY2:
300+
val = force_utf8(val)
298301
elif key == 'email':
299302
val = val.lower()
300303
elif key == 'country':

mig/shared/compat.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,13 @@ def _unicode_string_to_utf8_bytes(unicode_string):
6161
return codecs.encode(unicode_string, 'utf8')
6262

6363

64+
def _unicode_string_to_escaped_unicode(unicode_string):
65+
"""Convert utf8 bytes to escaped unicode string."""
66+
67+
utf8_bytes = dn_utf8_bytes = codecs.encode(unicode_string, 'utf8')
68+
return codecs.decode(utf8_bytes, 'unicode_escape')
69+
70+
6471
def ensure_native_string(string_or_bytes):
6572
"""Given a supplied input which can be either a string or bytes
6673
return a representation providing string operations while ensuring that

mig/shared/useradm.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@
3030
from __future__ import print_function
3131
from __future__ import absolute_import
3232

33+
from past.builtins import basestring
3334
from email.utils import parseaddr
35+
import codecs
3436
import datetime
3537
import fnmatch
3638
import os
@@ -44,6 +46,7 @@
4446
from mig.shared.base import client_id_dir, client_dir_id, client_alias, \
4547
get_client_id, extract_field, fill_user, fill_distinguished_name, \
4648
is_gdp_user, mask_creds, sandbox_resource
49+
from mig.shared.compat import _unicode_string_to_escaped_unicode
4750
from mig.shared.conf import get_configuration_object
4851
from mig.shared.configuration import Configuration
4952
from mig.shared.defaults import user_db_filename, keyword_auto, ssh_conf_dir, \
@@ -466,8 +469,24 @@ def create_user_in_db(configuration, db_path, client_id, user, now, authorized,
466469
flock = None
467470
user_db = {}
468471
renew = default_renew
472+
473+
retry_lock = False
469474
if do_lock:
475+
try:
476+
flock = lock_user_db(db_path)
477+
except FileNotFoundError as e:
478+
user_db_home = os.path.dirname(db_path)
479+
480+
if not os.path.exists(db_path) and not os.path.exists(user_db_home):
481+
os.mkdir(user_db_home)
482+
retry_lock = True
483+
else:
484+
raise Exception("Failed to lock user DB: '%s'" % db_path)
485+
486+
if retry_lock:
470487
flock = lock_user_db(db_path)
488+
if not flock:
489+
raise Exception("Failed to lock user DB: '%s'" % db_path)
471490

472491
if not os.path.exists(db_path):
473492
# Auto-create missing user DB if either auto_create_db or force is set
@@ -862,7 +881,7 @@ def create_user_in_fs(configuration, client_id, user, now, renew, force, verbose
862881
# match in htaccess
863882

864883
dn_plain = info['distinguished_name']
865-
dn_enc = dn_plain.encode('string_escape')
884+
dn_enc = _unicode_string_to_escaped_unicode(dn_plain)
866885

867886
def upper_repl(match):
868887
"""Translate hex codes to upper case form"""

tests/test_mig_server_createuser.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# -*- coding: utf-8 -*-
2+
#
3+
# --- BEGIN_HEADER ---
4+
#
5+
# test_mig_server-createuser - unit tests for the migrid createuser CLI
6+
# Copyright (C) 2003-2024 The MiG Project by the Science HPC Center at UCPH
7+
#
8+
# This file is part of MiG.
9+
#
10+
# MiG is free software: you can redistribute it and/or modify
11+
# it under the terms of the GNU General Public License as published by
12+
# the Free Software Foundation; either version 2 of the License, or
13+
# (at your option) any later version.
14+
#
15+
# MiG is distributed in the hope that it will be useful,
16+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
17+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18+
# GNU General Public License for more details.
19+
#
20+
# You should have received a copy of the GNU General Public License
21+
# along with this program; if not, write to the Free Software
22+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
23+
# USA.
24+
#
25+
# --- END_HEADER ---
26+
#
27+
28+
"""Unit tests for the migrid createuser CLI"""
29+
30+
from __future__ import print_function
31+
import os
32+
import sys
33+
34+
from tests.support import MIG_BASE, TEST_OUTPUT_DIR, MigTestCase, testmain
35+
36+
from mig.server.createuser import main as createuser
37+
38+
class TestBooleans(MigTestCase):
39+
def before_each(self):
40+
configuration = self.configuration
41+
test_user_db_home = os.path.join(configuration.state_path, 'user_db_home')
42+
try:
43+
os.rmdir(test_user_db_home)
44+
except:
45+
pass
46+
self.expected_user_db_home = test_user_db_home
47+
48+
def _provide_configuration(self):
49+
return 'testconfig'
50+
51+
def test_user_db_is_created_and_user_is_added(self):
52+
args = [
53+
"-r",
54+
"Test User",
55+
"Test Org",
56+
"NA",
57+
"DK",
58+
"dummy-user",
59+
"This is the create comment",
60+
"password"
61+
]
62+
createuser(args, TEST_OUTPUT_DIR, configuration=self.configuration)
63+
64+
# presence of user home
65+
path_kind = MigTestCase._absolute_path_kind(self.expected_user_db_home)
66+
self.assertEqual(path_kind, 'dir')
67+
68+
# presence of user db
69+
expected_user_db_file = os.path.join(self.expected_user_db_home, 'MiG-users.db')
70+
path_kind = MigTestCase._absolute_path_kind(expected_user_db_file)
71+
self.assertEqual(path_kind, 'file')
72+
73+
74+
if __name__ == '__main__':
75+
testmain()

0 commit comments

Comments
 (0)