From 7055cea3c2a664f9fe10bb6e75712d4f660aa5d3 Mon Sep 17 00:00:00 2001 From: William Lupton Date: Fri, 26 Feb 2021 11:14:28 +0000 Subject: [PATCH 1/3] Allow search() to request user/group attributes Also prevent repr() from including the password. --- crowd.py | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/crowd.py b/crowd.py index 14c114d..deb56d9 100755 --- a/crowd.py +++ b/crowd.py @@ -31,11 +31,11 @@ class CrowdServer(object): The ``ssl_verify`` parameter controls how and if certificates are verified. If ``True``, the SSL certificate will be verified. A CA_BUNDLE path can also be provided. - + The ``client_cert`` tuple (cert,key) specifies a SSL client certificate and key files. """ - def __init__(self, crowd_url, app_name, app_pass, ssl_verify=True, + def __init__(self, crowd_url, app_name, app_pass, ssl_verify=True, timeout=None, client_cert=None): self.crowd_url = crowd_url self.app_name = app_name @@ -51,9 +51,10 @@ def __init__(self, crowd_url, app_name, app_pass, ssl_verify=True, def __str__(self): return "Crowd Server at %s" % self.crowd_url + # don't include the password! def __repr__(self): - return "" % \ - (self.crowd_url, self.app_name, self.app_pass) + return "" % \ + (self.crowd_url, self.app_name) def _build_session(self, content_type='json'): headers = { @@ -652,7 +653,8 @@ def get_memberships(self): memberships[group] = {u'users': users, u'groups': groups} return memberships - def search(self, entity_type, property_name, search_string, start_index=0, max_results=99999): + def search(self, entity_type, property_name, search_string, + start_index=0, max_results=99999, with_attributes=False): """Performs a user search using the Crowd search API. https://developer.atlassian.com/display/CROWDDEV/Crowd+REST+Resources#CrowdRESTResources-SearchResource @@ -663,35 +665,33 @@ def search(self, entity_type, property_name, search_string, start_index=0, max_r search_string: the string to search for. start_index: starting index of the results (default: 0) max_results: maximum number of results returned (default: 99999) + with_attributes: whether also to return entity attributes Returns: json results: Returns search results. """ - params = { - "entity-type": entity_type, - "expand": entity_type, - "property-search-restriction": { - "property": {"name": property_name, "type": "STRING"}, - "match-mode": "CONTAINS", - "value": search_string, - } - } + # optionally return attributes (might expect .attributes + # but that doesn't work and this does) + expand = entity_type + if with_attributes: + expand += ",attributes" params = { 'entity-type': entity_type, - 'expand': entity_type, + 'expand': expand, 'start-index': start_index, 'max-results': max_results } + # Construct XML payload of the form: # # # email # STRING # - # EXACTLY_MATCHES + # CONTAINS # bob@example.net # From ed3f1f499467a9ae98bb0c7d558fa86180b329ff Mon Sep 17 00:00:00 2001 From: William Lupton Date: Fri, 1 Oct 2021 13:45:22 +0100 Subject: [PATCH 2/3] Don't assume that supplied user attribute value is a scalar Allow it to be a list or tuple. --- crowd.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/crowd.py b/crowd.py index deb56d9..3ddb912 100755 --- a/crowd.py +++ b/crowd.py @@ -435,16 +435,15 @@ def set_user_attribute(self, username, attribute, value, raise_on_error=False): """Set an attribute on a user :param username: The username on which to set the attribute :param attribute: The name of the attribute to set - :param value: The value of the attribute to set + :param value: The value of the attribute to set (can be a list or tuple) :return: True on success, False on failure. """ + values = value if isinstance(value, (list, tuple)) else [value] data = { 'attributes': [ { 'name': attribute, - 'values': [ - value - ] + 'values': values }, ] } From 506ca5e58c56ea46f7c66d3d84dd34c7f9026988 Mon Sep 17 00:00:00 2001 From: William Lupton Date: Mon, 6 Jun 2022 15:09:21 +0100 Subject: [PATCH 3/3] Fix Crowd URL to work with Data Center --- crowd.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crowd.py b/crowd.py index 3ddb912..3e86856 100755 --- a/crowd.py +++ b/crowd.py @@ -40,7 +40,9 @@ def __init__(self, crowd_url, app_name, app_pass, ssl_verify=True, self.crowd_url = crowd_url self.app_name = app_name self.app_pass = app_pass - self.rest_url = crowd_url.rstrip("/") + "/rest/usermanagement/1" + # XXX the leading '/crowd' is necessary for Crowd Data Center (should + # add logic to auto-determine the need for it) + self.rest_url = crowd_url.rstrip("/") + "/crowd/rest/usermanagement/1" self.ssl_verify = ssl_verify self.client_cert = client_cert self.timeout = timeout