Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

intermediate work on #159 #172

Merged
merged 4 commits into from
May 2, 2017
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion examples/config files - basic/3 connector-ldap.yml
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,19 @@
# You must specify all four of these settings. Consult with your
# enterprise directory administrators to get suitable values.
# These access credentials are sensitive and must be protected.
username: "LDAP username goes here"
username: "LDAP or Credential Manager username goes here"
password: "LDAP password goes here"
host: "LDAP host URL goes here. e.g. ldap://ldap.example.com"
base_dn: "defines the base DN. e.g. DC=example,DC=com"

#(optional)
credential_manager:
#This will pull credential from Windows Credential Manager in Control Panel.
#value: windows_credential_manager
type: windows_credential_manager
#service_name is Required for Windows Credential Manger
service_name: "Internet or Network Address field in Credential Manager"

# (optional) user_identity_type (default is inherited from main configuration)
# user_identity_type specifies a default identity type for when directory users
# are created on the Adobe side (one of adobeID, enterpriseID, federatedID).
Expand Down
1 change: 1 addition & 0 deletions misc/build/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ pycrypto
PyYAML
psutil
umapi-client>=2.0.2
keyring
3 changes: 2 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,9 @@
'pycrypto',
'python-ldap==2.4.25',
'PyYAML',
'umapi-client>=2.2',
'umapi-client>=2.3',
'psutil',
'keyring'
],
setup_requires=['nose>=1.0'],
tests_require=[
Expand Down
79 changes: 28 additions & 51 deletions user_sync/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@

import user_sync.identity_type
import user_sync.rules
from user_sync import credential_manager
from user_sync.error import AssertionException

DEFAULT_MAIN_CONFIG_FILENAME = 'user-sync-config.yml'
Expand Down Expand Up @@ -139,20 +138,10 @@ def get_directory_connector_options(self, connector_name):
'''
options = {}
connectors_config = self.get_directory_connector_configs()
if (connectors_config != None):
if connectors_config is not None:
connector_item = connectors_config.get_list(connector_name, True)
options = self.get_dict_from_sources(connector_item)

options = self.combine_dicts([options, self.options['directory_connector_overridden_options']])
# credentials are None, a dict, or a config filename to read to get a dict
credentials = credential_manager.get_credentials(credential_manager.DIRECTORY_CREDENTIAL_TYPE,
connector_name,
config=options,
config_loader = self)
if isinstance(credentials, types.StringTypes):
credentials = ConfigFileLoader.load_other_config(credentials)
if isinstance(credentials, dict):
options = self.combine_dicts([options, credentials])
return options

def get_directory_groups(self):
Expand All @@ -164,9 +153,9 @@ def get_directory_groups(self):
raise AssertionException("Your main configuration file is still in v1 format. Please convert it to v2.")
groups_config = None
directory_config = self.main_config.get_dict_config('directory_users', True)
if (directory_config != None):
if directory_config is not None:
groups_config = directory_config.get_list_config('groups', True)
if (groups_config == None):
if groups_config is None:
return adobe_groups_by_directory_group

for item in groups_config.iter_dict_configs():
Expand Down Expand Up @@ -215,7 +204,6 @@ def get_dict_from_sources(self, sources):
Given a list of config file paths, return the dictionary composed of all the contents
of those config files, or None if the list is empty
:param sources: a list of strings
:param owner: a string to use in error messages if we can't find a config file.
:rtype dict
'''
if not sources:
Expand Down Expand Up @@ -249,10 +237,10 @@ def combine_dicts(dicts):
'''
result = {}
for dict_item in dicts:
if (isinstance(dict_item, dict)):
if isinstance(dict_item, dict):
for dict_key, dict_item in dict_item.iteritems():
result_item = result.get(dict_key)
if (isinstance(result_item, dict) and isinstance(dict_item, dict)):
if isinstance(result_item, dict) and isinstance(dict_item, dict):
result_item.update(dict_item)
else:
result[dict_key] = dict_item
Expand Down Expand Up @@ -357,19 +345,6 @@ def get_rule_options(self):
def create_umapi_options(self, connector_config_sources):
options = self.get_dict_from_sources(connector_config_sources)
options['test_mode'] = self.options['test_mode']
enterprise_section = options.get('enterprise')
if isinstance(enterprise_section, dict):
org_id = enterprise_section.get('org_id')
if (org_id != None):
# credentials are None, a dict, or a config filename to read to get a dict
credentials = credential_manager.get_credentials(credential_manager.UMAPI_CREDENTIAL_TYPE,
org_id,
config = enterprise_section,
config_loader = self)
if isinstance(credentials, types.StringTypes):
credentials = ConfigFileLoader.load_other_config(credentials)
if isinstance(credentials, dict):
options['enterprise'] = self.combine_dicts([enterprise_section, credentials])
return options

def check_unused_config_keys(self):
Expand Down Expand Up @@ -420,30 +395,31 @@ def create_assertion_error(self, message):
return AssertionException("%s in: %s" % (message, self.get_full_scope()))

def describe_types(self, types_to_describe):
if (types_to_describe == types.StringTypes):
if types_to_describe == types.StringTypes:
result = self.describe_types(types.StringType)
elif (isinstance(types_to_describe, tuple)):
elif isinstance(types_to_describe, tuple):
result = []
for type_to_describe in types_to_describe:
result.extend(self.describe_types(type_to_describe))
else:
result = [types_to_describe.__name__]
return result

def report_unused_values(self, logger, optional_configs = []):
def report_unused_values(self, logger, optional_configs=None):
optional_configs = [] if optional_configs is None else optional_configs
has_error = False
for config in self.iter_configs():
messages = config.describe_unused_values()
if (len(messages) > 0):
if (config in optional_configs):
if len(messages) > 0:
if config in optional_configs:
log_level = logging.WARNING
else:
log_level = logging.ERROR
has_error = True
for message in messages:
logger.log(log_level, message)

if (has_error):
if has_error:
raise AssertionException('Detected unused keys that are not ignorable.')

def describe_unused_values(self):
Expand All @@ -465,7 +441,7 @@ def iter_values(self, allowed_types):
'''
index = 0
for item in self.value:
if (not isinstance(item, allowed_types)):
if not isinstance(item, allowed_types):
reported_types = self.describe_types(allowed_types)
raise self.create_assertion_error("Value should be one of these types: %s for index: %s" % (reported_types, index))
index += 1
Expand All @@ -475,7 +451,7 @@ def iter_dict_configs(self):
index = 0
for value in self.iter_values(dict):
config = self.find_child_config(index)
if (config == None):
if config is None:
config = DictConfig("[%s]" % index, value)
self.add_child(config)
yield config
Expand All @@ -500,17 +476,17 @@ def iter_keys(self):

def iter_unused_keys(self):
for key in self.iter_keys():
if (key not in self.accessed_keys):
if key not in self.accessed_keys:
yield key

def get_dict_config(self, key, none_allowed = False):
'''
:rtype DictConfig
'''
result = self.find_child_config(key)
if (result == None):
if result is None:
value = self.get_dict(key, none_allowed)
if (value != None):
if value is not None:
result = DictConfig(key, value)
self.add_child(result)
return result
Expand All @@ -530,7 +506,7 @@ def get_bool(self, key, none_allowed = False):

def get_list(self, key, none_allowed = False):
value = self.get_value(key, None, none_allowed)
if (value != None and not isinstance(value, list)):
if value is not None and not isinstance(value, list):
value = [value]
return value

Expand All @@ -539,9 +515,9 @@ def get_list_config(self, key, none_allowed = False):
:rtype ListConfig
'''
result = self.find_child_config(key)
if (result == None):
if result is None:
value = self.get_list(key, none_allowed)
if (value != None):
if value is not None:
result = ListConfig(key, value)
self.add_child(result)
return result
Expand All @@ -554,18 +530,18 @@ def get_value(self, key, allowed_types, none_allowed = False):
'''
self.accessed_keys.add(key)
result = self.value.get(key)
if (result == None):
if (not none_allowed):
if result is None:
if not none_allowed:
raise self.create_assertion_error("Value not found for key: %s" % key)
elif (allowed_types != None and not isinstance(result, allowed_types)):
elif allowed_types is not None and not isinstance(result, allowed_types):
reported_types = self.describe_types(allowed_types)
raise self.create_assertion_error("Value should be one of these types: %s for key: %s" % (reported_types, key))
return result

def describe_unused_values(self):
messages = []
unused_keys = list(self.iter_unused_keys())
if (len(unused_keys) > 0):
if len(unused_keys) > 0:
messages.append("Found unused keys: %s in: %s" % (unused_keys, self.get_full_scope()))
return messages

Expand Down Expand Up @@ -717,8 +693,9 @@ def process_path_value(cls, val, must_exist, can_have_subdict):
does the relative path processing for a value from the dictionary,
which can be a string, a list of strings, or a list of strings
and "tagged" strings (sub-dictionaries whose values are strings)
:param key: the key whose value we are processing, for error messages
:param val: the value we are processing, for error messages
:param must_exist: whether there must be a value
:param can_have_subdict: whether the value can be a tagged string
'''
if isinstance(val, types.StringTypes):
return cls.relative_path(val, must_exist)
Expand Down Expand Up @@ -793,7 +770,7 @@ def set_value(self, key, allowed_types, default_value):
'''
value = default_value
config = self.default_config
if (config != None and config.has_key(key)):
if config is not None and config.has_key(key):
value = config.get_value(key, allowed_types, False)
self.options[key] = value

Expand All @@ -809,7 +786,7 @@ def require_value(self, key, allowed_types):
:type key: str
'''
config = self.default_config
if (config == None):
if config is None:
raise AssertionException("No config found.")
self.options[key] = value = config.get_value(key, allowed_types)
return value
12 changes: 5 additions & 7 deletions user_sync/connector/directory_csv.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,15 +68,13 @@ def __init__(self, caller_options):
builder.set_string_value('logger_name', CSVDirectoryConnector.name)
builder.require_string_value('file_path')
options = builder.get_options()

# identity type for new users if not specified in column
self.user_identity_type = user_sync.identity_type.parse_identity_type(options['user_identity_type'])

self.options = options
self.logger = logger = user_sync.connector.helper.create_logger(options)
self.logger = logger = user_sync.connector.helper.create_logger(options)
logger.debug('CSV initialized with options: %s', options)
caller_config.report_unused_values(logger)

logger.debug('Initialized with options: %s', options)
# identity type for new users if not specified in column
self.user_identity_type = user_sync.identity_type.parse_identity_type(options['user_identity_type'])

def load_users_and_groups(self, groups, extended_attributes):
'''
Expand Down Expand Up @@ -129,7 +127,7 @@ def get_column_name(key):
email = self.get_column_value(row, email_column_name)
if email is None or email.find('@') < 0:
logger.warning('Missing or invalid email at row: %d; skipping', line_read)
continue;
continue

user = users.get(email)
if user is None:
Expand Down
Loading