Skip to content

Commit 06f1784

Browse files
authored
Merge pull request #212 from adobe-apiplatform/v2
prepare for 2.1.1rc1 build
2 parents 9f23393 + 70e7532 commit 06f1784

18 files changed

+795
-703
lines changed

Makefile

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
RM := rm -rf
2+
MKDIR := mkdir
23
python_ldap_requirements := misc/build/python-ldap-requirements.txt
34

45
ifeq ($(OS),Windows_NT)
56
output_file_extension = .pex
67
rm_path := $(shell python -c "import distutils.spawn; print(distutils.spawn.find_executable('rm'))")
78
ifeq ($(rm_path),None)
89
RM := rmdir /S /Q
10+
endif
11+
mkdir_path := $(shell python -c "import distutils.spawn; print(distutils.spawn.find_executable('mkdir'))")
12+
ifeq ($(mkdir_path),None)
13+
MKDIR := md
914
endif
1015
python_arch := $(shell python -c "import platform; print platform.architecture()[0]")
1116
ifeq ($(python_arch),64bit)
@@ -19,7 +24,8 @@ output_filename = user-sync
1924
pex:
2025
pip install --upgrade pip
2126
pip install pex requests wheel
22-
pip wheel -w wheelhouse -r misc/build/requirements.txt -r $(python_ldap_requirements)
27+
pip wheel -w wheelhouse -r $(python_ldap_requirements)
28+
-$(MKDIR) wheelhouse
2329
-$(RM) $(output_dir)
2430
pex \
2531
-v -o $(output_dir)/$(output_filename)$(output_file_extension) -m user_sync.app \

RELEASE_NOTES.md

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,21 @@
1-
# Release Notes for User Sync Tool Version 2.1
1+
# Release Notes for User Sync Tool Version 2.1.1
22

3-
These notes apply to v2.1 of 2017-05-12.
3+
These notes apply to v2.1.1rc1 of 2017-06-06.
44

55
## New Features
66

7-
1. We now have full Unicode support. See [Issue 167](https://github.com/adobe-apiplatform/user-sync.py/issues/167) for details.
8-
2. We now support secure handling for all credential settings and credential files. See [Issue 159](https://github.com/adobe-apiplatform/user-sync.py/issues/159) for design discussion, and read [the docs](https://adobe-apiplatform.github.io/user-sync.py/) for associated config changes.
7+
There are no new features in this release; bug fixes only.
98

109
## Bug Fixes
1110

12-
Fixes for [Issue 181](https://github.com/adobe-apiplatform/user-sync.py/issues/181) and [Issue 189](https://github.com/adobe-apiplatform/user-sync.py/issues/189).
11+
There is one fix for some obscure Unicode edge cases (that were found only by code inspection): [Issue 167](https://github.com/adobe-apiplatform/user-sync.py/issues/167).
12+
13+
User Sync no longer crashes if a user's LDAP email address is present but empty: [Issue 201](https://github.com/adobe-apiplatform/user-sync.py/issues/201).
14+
15+
The proper packages were not present for secure credential storage on Linux platforms: [Issue 199](https://github.com/adobe-apiplatform/user-sync.py/issues/199).
16+
17+
Still to come: a fix for secure key storage on Windows: [Issue 198](https://github.com/adobe-apiplatform/user-sync.py/issues/198).
1318

1419
## Compatibility with Prior Versions
1520

16-
This version is fully backward-compatible with version 2.0. There may be subtle behavioral changes due to bug fixes around support for non-Ascii characters. There are also new configuration file options and a new command line argument that didn't exist in 2.0.
21+
This version is fully backward-compatible with version 2.1.
Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +0,0 @@
1-
python-ldap==2.4.25

misc/build/requirements.txt

Lines changed: 0 additions & 5 deletions
This file was deleted.

setup.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
1919
# SOFTWARE.
2020

21+
import sys
22+
2123
from setuptools import setup
2224

2325
version_namespace = {}
@@ -45,8 +47,17 @@
4547
'PyYAML',
4648
'umapi-client>=2.5',
4749
'psutil',
48-
'keyring'
50+
'keyring',
4951
],
52+
extras_require={
53+
':sys_platform=="linux" or sys_platform=="linux2"':[
54+
'secretstorage',
55+
'dbus-python'
56+
],
57+
':sys_platform=="win32"':[
58+
'pywin32-ctypes==0.0.1'
59+
]
60+
},
5061
setup_requires=['nose>=1.0'],
5162
tests_require=[
5263
'mock',

user_sync/app.py

Lines changed: 52 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import user_sync.config
3030
import user_sync.connector.directory
3131
import user_sync.connector.umapi
32+
import user_sync.helper
3233
import user_sync.lockfile
3334
import user_sync.rules
3435
from user_sync.error import AssertionException
@@ -37,6 +38,21 @@
3738
LOG_STRING_FORMAT = '%(asctime)s %(process)d %(levelname)s %(name)s - %(message)s'
3839
LOG_DATE_FORMAT = '%Y-%m-%d %H:%M:%S'
3940

41+
# file global logger, defined early so later functions can refer to it.
42+
logger = logging.getLogger('main')
43+
44+
45+
def init_console_log():
46+
handler = logging.StreamHandler(sys.stdout)
47+
handler.setFormatter(logging.Formatter(LOG_STRING_FORMAT, LOG_DATE_FORMAT))
48+
root_logger = logging.getLogger()
49+
root_logger.addHandler(handler)
50+
root_logger.setLevel(logging.DEBUG)
51+
return handler
52+
53+
# file global console_log_handler, defined early so later functions can refer to it.
54+
console_log_handler = init_console_log()
55+
4056

4157
def process_args():
4258
parser = argparse.ArgumentParser(description='User Sync from Adobe')
@@ -64,9 +80,9 @@ def process_args():
6480
'side is updated to match.',
6581
action='store_true', dest='update_user_info')
6682
parser.add_argument('--process-groups',
67-
help='if the membership in mapped groups differs between the enterprise directory and Adobe sides, '
83+
help='if membership in mapped groups differs between the enterprise directory and Adobe sides, '
6884
'the group membership is updated on the Adobe side so that the memberships in mapped '
69-
'groups matches those on the enterprise directory side.',
85+
'groups match those on the enterprise directory side.',
7086
action='store_true', dest='manage_groups')
7187
parser.add_argument('--adobe-only-user-action',
7288
help="specify what action to take on Adobe users that don't match users from the "
@@ -94,19 +110,10 @@ def process_args():
94110
return parser.parse_args()
95111

96112

97-
def init_console_log():
98-
console_log_handler = logging.StreamHandler(sys.stdout)
99-
console_log_handler.setFormatter(logging.Formatter(LOG_STRING_FORMAT, LOG_DATE_FORMAT))
100-
root_logger = logging.getLogger()
101-
root_logger.addHandler(console_log_handler)
102-
root_logger.setLevel(logging.DEBUG)
103-
return console_log_handler
104-
105-
106113
def init_log(logging_config):
107-
'''
114+
"""
108115
:type logging_config: user_sync.config.DictConfig
109-
'''
116+
"""
110117
builder = user_sync.config.OptionsBuilder(logging_config)
111118
builder.set_bool_value('log_to_file', False)
112119
builder.set_string_value('file_log_directory', 'logs')
@@ -123,66 +130,66 @@ def init_log(logging_config):
123130
}
124131

125132
console_log_level = level_lookup.get(options['console_log_level'])
126-
if (console_log_level == None):
133+
if console_log_level is None:
127134
console_log_level = logging.INFO
128135
logger.log(logging.WARNING, 'Unknown console log level: %s setting to info' % options['console_log_level'])
129136
console_log_handler.setLevel(console_log_level)
130137

131-
if options['log_to_file'] == True:
138+
if options['log_to_file']:
132139
unknown_file_log_level = False
133140
file_log_level = level_lookup.get(options['file_log_level'])
134-
if (file_log_level == None):
141+
if file_log_level is None:
135142
file_log_level = logging.INFO
136143
unknown_file_log_level = True
137144
file_log_directory = options['file_log_directory']
138145
if not os.path.exists(file_log_directory):
139146
os.makedirs(file_log_directory)
140147

141148
file_path = os.path.join(file_log_directory, datetime.date.today().isoformat() + ".log")
142-
fileHandler = logging.FileHandler(file_path)
143-
fileHandler.setLevel(file_log_level)
144-
fileHandler.setFormatter(logging.Formatter(LOG_STRING_FORMAT, LOG_DATE_FORMAT))
145-
logging.getLogger().addHandler(fileHandler)
149+
file_handler = logging.FileHandler(file_path)
150+
file_handler.setLevel(file_log_level)
151+
file_handler.setFormatter(logging.Formatter(LOG_STRING_FORMAT, LOG_DATE_FORMAT))
152+
logging.getLogger().addHandler(file_handler)
146153
if unknown_file_log_level:
147154
logger.log(logging.WARNING, 'Unknown file log level: %s setting to info' % options['file_log_level'])
148155

149156

150157
def begin_work(config_loader):
151-
'''
158+
"""
152159
:type config_loader: user_sync.config.ConfigLoader
153-
'''
160+
"""
154161

155162
directory_groups = config_loader.get_directory_groups()
156163
primary_umapi_config, secondary_umapi_configs = config_loader.get_umapi_options()
157164
rule_config = config_loader.get_rule_options()
158165

159166
# process mapped configuration after the directory groups have been loaded, as mapped setting depends on this.
160-
if (rule_config['directory_group_mapped']):
167+
if rule_config['directory_group_mapped']:
161168
rule_config['directory_group_filter'] = set(directory_groups.iterkeys())
162169

163170
# make sure that all the adobe groups are from known umapi connector names
164171
referenced_umapi_names = set()
165172
for groups in directory_groups.itervalues():
166173
for group in groups:
167174
umapi_name = group.umapi_name
168-
if (umapi_name != user_sync.rules.PRIMARY_UMAPI_NAME):
175+
if umapi_name != user_sync.rules.PRIMARY_UMAPI_NAME:
169176
referenced_umapi_names.add(umapi_name)
170177
referenced_umapi_names.difference_update(secondary_umapi_configs.iterkeys())
171178

172-
if (len(referenced_umapi_names) > 0):
179+
if len(referenced_umapi_names) > 0:
173180
raise AssertionException('Adobe groups reference unknown umapi connectors: %s' % referenced_umapi_names)
174181

175182
directory_connector = None
176183
directory_connector_options = None
177184
directory_connector_module_name = config_loader.get_directory_connector_module_name()
178-
if (directory_connector_module_name != None):
185+
if directory_connector_module_name is not None:
179186
directory_connector_module = __import__(directory_connector_module_name, fromlist=[''])
180187
directory_connector = user_sync.connector.directory.DirectoryConnector(directory_connector_module)
181188
directory_connector_options = config_loader.get_directory_connector_options(directory_connector.name)
182189

183190
config_loader.check_unused_config_keys()
184191

185-
if (directory_connector != None and directory_connector_options != None):
192+
if directory_connector is not None and directory_connector_options is not None:
186193
# specify the default user_identity_type if it's not already specified in the options
187194
if 'user_identity_type' not in directory_connector_options:
188195
directory_connector_options['user_identity_type'] = rule_config['new_account_type']
@@ -198,7 +205,7 @@ def begin_work(config_loader):
198205
umapi_connectors = user_sync.rules.UmapiConnectors(umapi_primary_connector, umapi_other_connectors)
199206

200207
rule_processor = user_sync.rules.RuleProcessor(rule_config)
201-
if (len(directory_groups) == 0 and rule_processor.will_manage_groups()):
208+
if len(directory_groups) == 0 and rule_processor.will_manage_groups():
202209
logger.warn('no groups mapped in config file')
203210
rule_processor.run(directory_groups, directory_connector, umapi_connectors)
204211

@@ -213,14 +220,14 @@ def create_config_loader(args):
213220

214221

215222
def create_config_loader_options(args):
216-
'''
223+
"""
217224
This is where all the command-line arguments get set as options in the main config
218225
so that it appears as if they were loaded as part of the main configuration file.
219226
If you add an option that is supposed to be set from the command line here, you
220227
had better make sure you add it to the ones read in get_rule_options.
221228
:param args: the command-line args as parsed
222229
:return: the configured options for the config loader.
223-
'''
230+
"""
224231
config_options = {
225232
'delete_strays': False,
226233
'directory_connector_module_name': None,
@@ -241,17 +248,17 @@ def create_config_loader_options(args):
241248
# --users
242249
users_args = args.users
243250
users_action = None if not users_args else user_sync.helper.normalize_string(users_args.pop(0))
244-
if (users_action == None or users_action == 'all'):
251+
if users_action is None or users_action == 'all':
245252
config_options['directory_connector_module_name'] = 'user_sync.connector.directory_ldap'
246-
elif (users_action == 'file'):
253+
elif users_action == 'file':
247254
if len(users_args) == 0:
248255
raise AssertionException('Missing file path for --users %s [file_path]' % users_action)
249256
config_options['directory_connector_module_name'] = 'user_sync.connector.directory_csv'
250257
config_options['directory_connector_overridden_options'] = {'file_path': users_args.pop(0)}
251-
elif (users_action == 'mapped'):
258+
elif users_action == 'mapped':
252259
config_options['directory_connector_module_name'] = 'user_sync.connector.directory_ldap'
253260
config_options['directory_group_mapped'] = True
254-
elif (users_action == 'group'):
261+
elif users_action == 'group':
255262
if len(users_args) == 0:
256263
raise AssertionException('Missing groups for --users %s [groups]' % users_action)
257264
config_options['directory_connector_module_name'] = 'user_sync.connector.directory_ldap'
@@ -260,7 +267,7 @@ def create_config_loader_options(args):
260267
raise AssertionException('Unknown argument --users %s' % users_action)
261268

262269
username_filter_pattern = args.username_filter_pattern
263-
if (username_filter_pattern):
270+
if username_filter_pattern:
264271
try:
265272
compiled_expression = re.compile(r'\A' + username_filter_pattern + r'\Z', re.IGNORECASE)
266273
except Exception as e:
@@ -272,19 +279,19 @@ def create_config_loader_options(args):
272279
adobe_action_args = args.adobe_only_user_action
273280
if adobe_action_args is not None:
274281
adobe_action = None if not adobe_action_args else user_sync.helper.normalize_string(adobe_action_args.pop(0))
275-
if (adobe_action == None or adobe_action == 'preserve'):
282+
if adobe_action is None or adobe_action == 'preserve':
276283
pass # no option settings needed
277-
elif (adobe_action == 'exclude'):
284+
elif adobe_action == 'exclude':
278285
config_options['exclude_strays'] = True
279-
elif (adobe_action == 'write-file'):
286+
elif adobe_action == 'write-file':
280287
if not adobe_action_args:
281288
raise AssertionException('Missing file path for --adobe-only-user-action %s [file_path]' % adobe_action)
282289
config_options['stray_list_output_path'] = adobe_action_args.pop(0)
283-
elif (adobe_action == 'delete'):
290+
elif adobe_action == 'delete':
284291
config_options['delete_strays'] = True
285-
elif (adobe_action == 'remove'):
292+
elif adobe_action == 'remove':
286293
config_options['remove_strays'] = True
287-
elif (adobe_action == 'remove-adobe-groups'):
294+
elif adobe_action == 'remove-adobe-groups':
288295
config_options['disentitle_strays'] = True
289296
else:
290297
raise AssertionException('Unknown argument --adobe-only-user-action %s' % adobe_action)
@@ -305,11 +312,11 @@ def create_config_loader_options(args):
305312

306313

307314
def log_parameters(args):
308-
'''
315+
"""
309316
Log the invocation parameters to make it easier to diagnose problem with customers
310317
:param args: namespace
311318
:return: None
312-
'''
319+
"""
313320
logger.info('------- Invocation parameters -------')
314321
logger.info(' '.join(sys.argv))
315322
logger.debug('-------- Internal parameters --------')
@@ -349,7 +356,7 @@ def main():
349356
logger.critical("A different User Sync process is currently running.")
350357

351358
except AssertionException as e:
352-
if (not e.is_reported()):
359+
if not e.is_reported():
353360
logger.critical(e.message)
354361
e.set_reported()
355362
except:
@@ -359,12 +366,9 @@ def main():
359366
pass
360367

361368
finally:
362-
if (run_stats != None):
369+
if run_stats is not None:
363370
run_stats.log_end(logger)
364371

365372

366-
console_log_handler = init_console_log()
367-
logger = logging.getLogger('main')
368-
369373
if __name__ == '__main__':
370374
main()

0 commit comments

Comments
 (0)