Skip to content

Commit 105fccd

Browse files
Merge pull request #368 from bhunut-adobe/feature/exclude_unmapped
Added new parameter: --adobe-users all|mapped|group
2 parents 6e94edd + 31a3a01 commit 105fccd

File tree

9 files changed

+131
-18
lines changed

9 files changed

+131
-18
lines changed

docs/en/user-manual/command_parameters.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ specific behavior in various situations.
3939
| `--adobe-only-user-list` _filename_ | Specifies a file from which a list of users will be read. This list is used as the definitive list of "Adobe only" user accounts to be acted upon. One of the `--adobe-only-user-action` directives must also be specified and its action will be applied to user accounts in the list. The `--users` option is disallowed if this option is present: only account removal actions can be processed. |
4040
| `--config-file-encoding` _encoding_name_ | Optional. Specifies the character encoding for the contents of the configuration files themselves. This includes the main configuration file, "user-sync-config.yml" as well as other configuration files it may reference. Default is `utf8` for User Sync 2.2 and later and `ascii` for earlier versions.<br />Character encoding in the user source data (whether csv or ldap) is declared by the connector configurations, and that encoding can be different than the encoding used for the configuration files (e.g., you could have a latin-1 configuration file but a CSV source file that uses utf-8 encoding).<br />The available encodings are dependent on the Python version used; see the documentation [here for Python 2.7](https://docs.python.org/2.7/library/codecs.html#standard-encodings) or [here for Python 3.6](https://docs.python.org/3.6/library/codecs.html#standard-encodings) for more information. |
4141
| `--strategy sync`<br />`--strategy push` | Available in release 2.2 and later. Optional. Default operating mode is `--strategy sync`. Controls whether User Sync reads user information from Adobe and compares to the directory information and then issues updates to Adobe, or simply pushes the directory input to Adobe without considering the existing user information on Adobe. `sync` is the default and the subject of the description of most of this documentation. `push` is useful when there is a large number of users on the Adobe side (>30,000) and known additions or changes to a small number of users are desired, and the list of those users is available in a csv file or a specific directory group.<br />If `--strategy push` is specified, `--adobe-only-user-action` cannot be specified as the determination of adobe-only users is not made.<br/>`--strategy push` will create new users, modify their group memberships for mapped groups only (if `--process-groups` is present), update user information (if `--update-user-info` is present), and will not remove users from the organization or delete their accounts. See [Handling Push Notifications](usage_scenarios.md#handling-push-notifications) for information on how to remove users via push notifications. |
42-
| `--connector ldap`<br />`--connector okta`<br />`--connector csv` _filename_ | Available in release 2.3 and later. Optional. Specifies the directory connector to be used (defaults to LDAP). If you specify the use of a CSV input file with this argument, then you cannot also specify one with `--users`, but you can then specify other `--users` options (such as `mapped` or `group`) for use with the CSV file. (The Okta connector does not support `--users all`, so you must specify a `--users` option of `mapped` or `group` if you use the Okta connector.)
42+
| `--connector ldap`<br />`--connector okta`<br />`--connector csv` _filename_ | Available in release 2.3 and later. Optional. Specifies the directory connector to be used (defaults to LDAP). If you specify the use of a CSV input file with this argument, then you cannot also specify one with `--users`, but you can then specify other `--users` options (such as `mapped` or `group`) for use with the CSV file. (The Okta connector does not support `--users all`, so you must specify a `--users` option of `mapped` or `group` if you use the Okta connector.) |
43+
| `--adobe-users all`<br />`--adobe-users mapped`<br />`--adobe-users group` _grp1,grp2_ | Available in release 2.4 and later. Optional. Specify the adobe users to be selected for sync. The default is all meaning all users found in Adobe Admin Console. Specifying group interprets the argument as a comma-separated list of groups (product profile or user-group) in the console, and only users in those groups are selected. Specifying mapped is the same as specifying group with all the adobe groups listed in the group mapping in the configuration file.
4344
{: .bordertablestyle }
4445

4546
As of version 2.3 of User Sync, the values of most command-line parameters can also be specified in the main configuration file, in an optional section called `invocation_defaults`. Here is an example use of that section:

docs/en/user-manual/usage_scenarios.md

+39-14
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ This section provides detailed instructions for each of these scenarios.
4343
## Update users and group memberships
4444

4545
This is the most typical and common type of invocation. User Sync
46-
finds all changes to user information and to group membership information
46+
finds all changes to user information and to group membership information
4747
on the enterprise
4848
side. It syncs the Adobe side by adding, updating, and removing
4949
users and user group and product configuration memberships.
@@ -89,7 +89,7 @@ parameter.
8989
### View result
9090

9191
When the synchronization succeeds, the Adobe Admin Console is
92-
updated. After this command is executed, your user list and
92+
updated. After this command is executed, your user list and
9393
product configuration user list in the
9494
Admin Console shows that a user with a Federated identity has
9595
been added to the “Default Acrobat Pro DC configuration.”
@@ -168,7 +168,7 @@ download in `examples/csv inputs - user and remove lists/`.
168168
./user-sync --users file user_list.csv
169169
```
170170

171-
Syncing from a file can be used in two situations. First, Adobe users can be managed
171+
Syncing from a file can be used in two situations. First, Adobe users can be managed
172172
using a spreadsheet. The spreadsheet lists users, the groups they are in, and
173173
information about them. Second, if the enterprise directory can provide push notifications
174174
for updates, these notifications can be placed in a csv file and used to drive
@@ -183,7 +183,7 @@ users from the Adobe side.
183183
If you want to handle removals separately, you can instruct
184184
the tool to flag users that no longer exist in the enterprise
185185
directory but still exist on the Adobe side. The
186-
`--adobe-only-user-action write-file exiting-users.csv` parameter
186+
`--adobe-only-user-action write-file exiting-users.csv` parameter
187187
writes out the list of user who
188188
are flagged for removal to a CSV file.
189189

@@ -238,24 +238,49 @@ on the list generated in a prior run of User Sync.
238238
./user-sync --adobe-only-user-list users-to-delete.csv --adobe-only-user-action delete
239239
```
240240

241+
### Limit Adobe users scope for syncing
242+
By supplying `adobe-users ...` argument you have an ability to control which Adobe users to be pull into User Sync Tool to be process for sync.
243+
With this argument you can specify limit by group name (`--adobe-users groups "..."`) or limit by adobe groups in group mapping in the configuration file (`--adobe-users mapped`)
244+
245+
When not specifying `adobe-users groups | mapped` User Sync Tool will automatically default to all:
246+
```sh
247+
./user-sync –c user-sync-config.yml --adobe-users all
248+
```
249+
250+
### Limit Adobe users to Adobe groups.
251+
252+
This action limited Adobe users scope to specified group. Group can be either product profile or user-group in the Adobe Admin Console.
253+
254+
```sh
255+
./user-sync –c user-sync-config.yml --adobe-users groups "group1, group2, group3"
256+
```
257+
258+
### Limit Adobe users to mapped Adobe groups
259+
260+
This action is the same as specifying `--adobe-users groups "..."`, where `...` is all the Adobe groups in the group mapping in the configuration file.
261+
262+
```sh
263+
./user-sync –c user-sync-config.yml --adobe-users mapped
264+
```
265+
241266
## Handling Push Notifications
242267

243268
If your directory system can generate notifications of updates you can use User Sync to
244-
process those updates incrementally. The technique shown in this section can also be
245-
used to process immediate updates where an administrator has updated a user or group of
246-
users and wants to push just those updates immediately into Adobe's user management
247-
system. Some scripting may be required to transform the information coming from the
248-
push notification to a csv format suitable for input to User Sync, and to separate
269+
process those updates incrementally. The technique shown in this section can also be
270+
used to process immediate updates where an administrator has updated a user or group of
271+
users and wants to push just those updates immediately into Adobe's user management
272+
system. Some scripting may be required to transform the information coming from the
273+
push notification to a csv format suitable for input to User Sync, and to separate
249274
deletions from other updates, which mush be handled separately in User Sync.
250275

251-
Create a file, say, `updated_users.csv` with the user update format illustrated in
252-
the `users-file.csv` example file in the folder `csv inputs - user and remove lists`.
276+
Create a file, say, `updated_users.csv` with the user update format illustrated in
277+
the `users-file.csv` example file in the folder `csv inputs - user and remove lists`.
253278
This is a basic csv file with columns for firstname, lastname, and so on.
254279

255280
firstname,lastname,email,country,groups,type,username,domain
256281
John,Smith,[email protected],US,"AdobeCC-All",enterpriseID
257282
Jane,Doe,[email protected],US,"AdobeCC-All",federatedID
258-
283+
259284
This file is then provided to User Sync:
260285

261286
```sh
@@ -274,8 +299,8 @@ This will handle deletions based on the notification and no other actions will b
274299

275300
## Action Summary
276301

277-
At the end of the invocation, an action summary will be printed to the log (if the level is INFO or DEBUG).
278-
The summary provides statistics accumulated during the run.
302+
At the end of the invocation, an action summary will be printed to the log (if the level is INFO or DEBUG).
303+
The summary provides statistics accumulated during the run.
279304
The statistics collected include:
280305

281306
- **Total number of Adobe users:** The total number of Adobe users in your admin console

examples/config files - basic/user-sync-config.yml

+4
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,10 @@ invocation_defaults:
319319
adobe_only_user_action: preserve
320320
# For argument --adobe-only-user-list, the default is empty (no value).
321321
adobe_only_user_list:
322+
# For argument --adobe-users, the default is 'all'.
323+
# if you want to specify group manually. Valid value format is
324+
# ['group', 'groupA,groupB']
325+
adobe_users: all
322326
# For argument --connector, the default is 'ldap'.
323327
connector: ldap
324328
# For argument --process-groups, the default is False (don't process).

tests/fixture/user-sync-config.yml

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ logging:
2525
invocation_defaults:
2626
adobe_only_user_action: preserve
2727
adobe_only_user_list: null
28+
adobe_users: all
2829
connector: ldap
2930
process_groups: No
3031
strategy: sync

tests/test_config.py

+26
Original file line numberDiff line numberDiff line change
@@ -136,3 +136,29 @@ def test_twostep_config(tmp_config_files, modify_ldap_config, caplog):
136136
assert 'two_steps_lookup' in options
137137
assert 'group_member_attribute_name' in options['two_steps_lookup']
138138
assert options['two_steps_lookup']['group_member_attribute_name'] == 'member'
139+
140+
141+
def test_adobe_users_config(tmp_config_files, modify_root_config):
142+
(root_config_file, _, _) = tmp_config_files
143+
args = app.process_args(['-c', root_config_file])
144+
145+
# test default
146+
config_loader = ConfigLoader(args)
147+
options = config_loader.load_invocation_options()
148+
assert 'adobe_users' in options
149+
assert options['adobe_users'] == ['all']
150+
151+
# test default invocation
152+
modify_root_config(['invocation_defaults', 'adobe_users'], "mapped")
153+
config_loader = ConfigLoader(args)
154+
options = config_loader.load_invocation_options()
155+
assert 'adobe_users' in options
156+
assert options['adobe_users'] == ['mapped']
157+
158+
# test command line param
159+
modify_root_config(['invocation_defaults', 'adobe_users'], "all")
160+
args = app.process_args(['-c', root_config_file, '--adobe-users', 'mapped'])
161+
config_loader = ConfigLoader(args)
162+
options = config_loader.load_invocation_options()
163+
assert 'adobe_users' in options
164+
assert options['adobe_users'] == ['mapped']

user_sync/app.py

+7
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,13 @@ def process_args(args=None):
154154
"users by also including --adobe-only-user-action and one of its arguments",
155155
metavar='input_path',
156156
dest='adobe_only_user_list')
157+
parser.add_argument('--adobe-users',
158+
help="specify the adobe users to pull from UMAPI. Legal values are 'all' (the default), "
159+
"'group names' (one or more specified groups), 'mapped' (all groups listed in "
160+
"the configuration file)",
161+
nargs="+",
162+
metavar='all|mapped|group',
163+
dest='adobe_users')
157164
parser.add_argument('--connector',
158165
help='specify a connector to use; default is LDAP (or CSV if --users file is specified)',
159166
nargs='+',

user_sync/config.py

+28
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ class ConfigLoader(object):
4747
invocation_defaults = {
4848
'adobe_only_user_action': ['preserve'],
4949
'adobe_only_user_list': None,
50+
'adobe_users': ['all'],
5051
'connector': ['ldap'],
5152
'process_groups': False,
5253
'strategy': 'sync',
@@ -249,6 +250,29 @@ def load_invocation_options(self):
249250
(username_filter_pattern, e))
250251
options['username_filter_regex'] = compiled_expression
251252

253+
# --adobe-users
254+
if self.args['adobe_users'] is not None:
255+
adobe_users_spec = options['adobe_users'] = self.args['adobe_users']
256+
elif options['adobe_users'] is not None:
257+
adobe_users_spec = options['adobe_users']
258+
else:
259+
adobe_users_spec = None
260+
261+
if adobe_users_spec is not None:
262+
adobe_users_action = user_sync.helper.normalize_string(adobe_users_spec[0])
263+
if adobe_users_action == 'all':
264+
options['adobe_group_mapped'] = False
265+
elif adobe_users_action == 'mapped':
266+
options['adobe_group_mapped'] = True
267+
elif adobe_users_action == 'group':
268+
if len(adobe_users_spec) != 2:
269+
raise AssertionException(
270+
'You must specify the groups to read when using the adobe-users "group" option')
271+
options['adobe_group_filter'] = []
272+
for group in adobe_users_spec[1].split(','):
273+
options['adobe_group_filter'].append(user_sync.rules.AdobeGroup.create(group))
274+
else:
275+
raise AssertionException('Unknown option "%s" for adobe-users' % adobe_users_action)
252276
return options
253277

254278
def get_logging_config(self):
@@ -552,6 +576,10 @@ def get_rule_options(self):
552576
if options.get('directory_group_mapped'):
553577
options['directory_group_filter'] = set(six.iterkeys(self.directory_groups))
554578

579+
# set the adobe group filter from the mapping, if requested.
580+
if options.get('adobe_group_mapped') is True:
581+
options['adobe_group_filter'] = set(user_sync.rules.AdobeGroup.iter_groups())
582+
555583
return options
556584

557585
def create_umapi_options(self, connector_config_sources):

user_sync/connector/umapi.py

+6-2
Original file line numberDiff line numberDiff line change
@@ -135,10 +135,14 @@ def __init__(self, name, caller_options):
135135
def get_users(self):
136136
return list(self.iter_users())
137137

138-
def iter_users(self):
138+
def iter_users(self, in_group=None):
139139
users = {}
140140
try:
141-
for u in umapi_client.UsersQuery(self.connection):
141+
if in_group:
142+
u_query = umapi_client.UsersQuery(self.connection, in_group=in_group)
143+
else:
144+
u_query = umapi_client.UsersQuery(self.connection)
145+
for u in u_query:
142146
email = u['email']
143147
if not (email in users):
144148
users[email] = u

user_sync/rules.py

+18-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import logging
2323
import six
2424
import re
25+
from itertools import chain
2526

2627
import user_sync.connector.umapi
2728
import user_sync.error
@@ -37,6 +38,7 @@ class RuleProcessor(object):
3738
# rule processing option defaults
3839
# these are in alphabetical order! Always add new ones that way!
3940
default_options = {
41+
'adobe_group_filter': None,
4042
'after_mapping_hook': None,
4143
'default_country_code': None,
4244
'delete_strays': False,
@@ -850,14 +852,21 @@ def update_umapi_users_for_connector(self, umapi_info, umapi_connector):
850852
if self.will_process_strays:
851853
self.add_stray(umapi_info.get_name(), None)
852854

855+
if self.options['adobe_group_filter'] is not None:
856+
umapi_users = self.get_umapi_user_in_groups(umapi_info, umapi_connector, self.options['adobe_group_filter'])
857+
else:
858+
umapi_users = umapi_connector.iter_users()
853859
# Walk all the adobe users, getting their group data, matching them with directory users,
854860
# and adjusting their attribute and group data accordingly.
855-
for umapi_user in umapi_connector.iter_users():
861+
for umapi_user in umapi_users:
856862
# get the basic data about this user; initialize change markers to "no change"
857863
user_key = self.get_umapi_user_key(umapi_user)
858864
if not user_key:
859865
self.logger.warning("Ignoring umapi user with empty user key: %s", umapi_user)
860866
continue
867+
if umapi_info.get_umapi_user(user_key) is not None:
868+
self.logger.debug("Ignoring umapi user. This user has already been processed: %s", umapi_user)
869+
continue
861870
umapi_info.add_umapi_user(user_key, umapi_user)
862871
attribute_differences = {}
863872
current_groups = self.normalize_groups(umapi_user.get('groups'))
@@ -920,6 +929,14 @@ def map_email_override(self, umapi_user):
920929
if '@' in username and username != email:
921930
self.email_override[username] = email
922931

932+
@staticmethod
933+
def get_umapi_user_in_groups(umapi_info, umapi_connector, groups):
934+
umapi_users_iters = []
935+
for group in groups:
936+
if group.get_umapi_name() == umapi_info.get_name():
937+
umapi_users_iters.append(umapi_connector.iter_users(in_group=group.get_group_name()))
938+
return chain.from_iterable(umapi_users_iters)
939+
923940
def is_umapi_user_excluded(self, in_primary_org, user_key, current_groups):
924941
if in_primary_org:
925942
self.primary_user_count += 1

0 commit comments

Comments
 (0)