def api_update_subject(self, subject, api_id, attributes): """ Make API request to update remote account. Returns the HTTP response. """ log = self.log log.debug("entered api_update_subject()") resp_code = yield self.change_subject_status_(api_id, active=True) if resp_code != 204: raise Exception("API call to activate subject returned HTTP status {}".format(resp_code)) prefix = self.url_prefix url = "{}/api/User/{}".format(prefix, api_id) headers = { 'Accept': ['application/json'], 'Content-Type': ['application/json'], } surname = attributes.get("sn", [""])[0] givenname = attributes.get("givenName", [""])[0] props = { 'firstName': givenname, 'lastName': surname, } serialized = json.dumps(props) body = StringProducer(serialized.encode('utf-8')) log.debug("url: {url}", url=url) log.debug("headers: {headers}", headers=headers) log.debug("body: {body}", body=serialized) resp = yield self.make_authenticated_api_call( 'PUT', url, data=body, headers=headers) returnValue(resp)
def api_deprovision_subject(self, api_id): """ Make the API call require to deprovision the subject identified by `api_id`. """ log = self.log http_client = self.http_client prefix = self.url_prefix url = "{0}/users/{1}".format(prefix, api_id) headers = { 'Accept': ['application/json'], 'Content-Type': ['application/json'], } props = { 'accountEnabled': False, } serialized = json.dumps(props) body = StringProducer(serialized.encode('utf-8')) log.debug("url: {url}", url=url) log.debug("headers: {headers}", headers=headers) log.debug("body: {body}", body=serialized) resp = yield self.make_authenticated_api_call("PATCH", url, headers=headers, data=body) resp_code = resp.code try: content = yield resp.content() except Exception as ex: pass if resp_code != 204: raise Exception( "API call to deprovision subject returned HTTP status {0}". format(resp_code)) returnValue(None)
def api_add_subject(self, subject, attributes): """ Use the API to add subjects. Returns the API ID of the newly created remote account or None. If None is returned, the API ID will not be cached and require a lookup on future use. """ log = self.log log.debug("Entered: api_add_subject().") prefix = self.url_prefix url = "{0}/users".format(prefix) headers = { 'Accept': ['application/json'], 'Content-Type': ['application/json'], } surname = attributes.get("sn", [""])[0] givenname = attributes.get("givenName", [""])[0] displayname = "{0}, {1}".format(surname, givenname) upn = "{0}@{1}".format(subject, self.domain) immutable_id = attributes.get("bannerLNumber", [None])[0] if immutable_id is None: raise Exception( "No immutable ID found for subject '{0}'!".format(subject)) props = { 'accountEnabled': True, 'displayName': displayname, 'givenName': givenname, 'surname': surname, 'userPrincipalName': upn, 'passwordProfile': { "forceChangePasswordNextSignIn": False, "password": generate_password(), }, 'mailNickname': subject, 'usageLocation': 'US', 'onPremisesImmutableId': immutable_id, } serialized = json.dumps(props) body = StringProducer(serialized.encode('utf-8')) log.debug("url: {url}", url=url) log.debug("headers: {headers}", headers=headers) log.debug("body: {body}", body=serialized) resp = yield self.make_authenticated_api_call('POST', url, data=body, headers=headers) resp_code = resp.code log.debug("Add-subject API response code: {code}", code=resp_code) if resp_code != 201: content = yield resp.content() log.error("API response {code}: {content}", code=resp_code, content=content) raise Exception("API returned status {0}".format(resp_code)) else: parsed = yield resp.json() api_id = parsed["id"] returnValue(api_id)
def api_add_subject(self, subject, attributes): """ Use the API to add subjects. Returns the API ID of the newly created remote account or None. If None is returned, the API ID will not be cached and require a lookup on future use. """ log = self.log assert not attributes is None, "api_add_subject(): `attributes` is None!" func_name = 'api_add_subject()' log.debug("Entered: {func_name}", func_name=func_name) prefix = self.url_prefix url = "{}/users".format(prefix) headers = { 'Accept': ['application/json'], 'Content-Type': ['application/json'], } surname = attributes.get("sn", [""])[0] givenname = attributes.get("displayName", [""])[0] if givenname == "": givenname = attributes.get("givenName", [""])[0] email = attributes.get("mail", [""])[0] user_info = { 'email': email, 'type': self.new_user_type, 'first_name': givenname, 'last_name': surname, } props = { 'action': 'ssoCreate', 'user_info': user_info, } serialized = json.dumps(props) body = StringProducer(serialized.encode('utf-8')) log.debug("url: {url}", url=url) log.debug("headers: {headers}", headers=headers) log.debug("body: {body}", body=serialized) resp = yield self.make_authenticated_api_call('POST', url, data=body, headers=headers) resp_code = resp.code log.debug("{func_name}: Add-subject API response code: {code}", func_name=func_name, code=resp_code) if resp_code != 201: log.warn( "There was an issue adding subject `{subject}` with attributes: {attributes}.", subject=subject, attributes=attributes, ) content = yield resp.content() raise Exception("{}: API returned status {}".format( func_name, resp_code)) parsed = yield resp.json() api_id = self.get_match_value_from_remote_account(parsed) returnValue(api_id)
def api_remove_license_from_subject(self, subject_id, license_info): """ API call to remove a license from a user. """ log = self.log sku = license_info["sku"] prefix = self.url_prefix url = "{0}/users/{1}/assignLicense".format(prefix, subject_id) headers = { 'Accept': ['application/json'], 'Content-Type': ['application/json'], } props = { 'removeLicenses': [sku], 'addLicenses': [], } serialized = json.dumps(props) body = StringProducer(serialized.encode('utf-8')) log.debug("url: {url}", url=url) log.debug("headers: {headers}", headers=headers) log.debug("body: {body}", body=serialized) resp = yield self.make_authenticated_api_call('POST', url, data=body, headers=headers) resp_code = resp.code log.debug("Remove-license API response code: {code}", code=resp_code) content = yield resp.content() try: parsed = json.loads(content) except json.JSONDecodeError as ex: parsed = None is_error = False if resp_code != 200: is_error = True if (resp_code == 400) and (not parsed is None): error = parsed.get("error", {}) message = error.get("message", None) if message == "User does not have a corresponding license.": is_error = False if is_error: log.error("API response {code}: {content}", code=resp_code, content=content) raise Exception("API returned status {0}".format(resp_code))
def api_deprovision_subject(self, api_id): """ Make the API call require to deprovision the subject identified by `api_id`. """ log = self.log http_client = self.http_client prefix = self.url_prefix url = "{0}/users/{1}".format(prefix, api_id) headers = { 'Accept': ['application/json'], 'Content-Type': ['application/json'], } props = { 'status': 'disabled', } serialized = json.dumps(props) body = StringProducer(serialized.encode('utf-8')) log.debug("url: {url}", url=url) log.debug("headers: {headers}", headers=headers) log.debug("body: {body}", body=serialized) resp = yield self.make_authenticated_api_call("PUT", url, headers=headers, data=body) resp_code = resp.code if resp_code == 400: #Invalidate cache self.invalidate_cached_subject_api(api_id) raise Exception( "api_deprovision_subject: Client error using API ID '{}'. Cached subject API ID has been invalidated." .format(api_id)) content = "" try: content = yield resp.content() except Exception as ex: pass if resp_code != 200: content = '\n{}'.format(content) raise Exception( "API call to deprovision subject returned HTTP status {}.{}". format(resp_code, content)) returnValue(None)
def api_add_license_to_subject(self, subject_id, license_info): """ API call to add a license to a user. """ log = self.log sku = license_info["sku"] disabled_products = license_info.get("disabled_products", []) prefix = self.url_prefix url = "{0}/users/{1}/assignLicense".format(prefix, subject_id) headers = { 'Accept': ['application/json'], 'Content-Type': ['application/json'], } props = { 'addLicenses': [{ "disabledPlans": disabled_products, "skuId": sku, }], 'removeLicenses': [], } serialized = json.dumps(props) body = StringProducer(serialized.encode('utf-8')) log.debug("url: {url}", url=url) log.debug("headers: {headers}", headers=headers) log.debug("body: {body}", body=serialized) resp = yield self.make_authenticated_api_call('POST', url, data=body, headers=headers) resp_code = resp.code log.debug("Add-license API response code: {code}", code=resp_code) if resp_code != 200: content = yield resp.content() log.error("API response {code}: {content}", code=resp_code, content=content) raise Exception("API returned status {0}".format(resp_code)) else: parsed = yield resp.json() api_id = parsed["id"] returnValue(api_id)
def api_add_subject_to_group(self, subject_id, target_group_id): """ Make an authenticated API call to add the remote subject ID to the remote group ID. Should raise on error on failure. """ log = self.log func_name = 'api_add_subject_to_group()' log.debug("Entered: {func_name}", func_name=func_name) prefix = self.url_prefix url = "{}/groups/{}/members".format(prefix, target_group_id) headers = { 'Accept': ['application/json'], 'Content-Type': ['application/json'], } props = { "members": [{ "id": subject_id, }], } serialized = json.dumps(props) body = StringProducer(serialized.encode('utf-8')) log.debug("url: {url}", url=url) log.debug("headers: {headers}", headers=headers) log.debug("body: {body}", body=serialized) resp = yield self.make_authenticated_api_call('POST', url, data=body, headers=headers) resp_code = resp.code if resp_code != 201: content = yield resp.content() #log.error( # "{func_name}: API response {code}: {content}", # func_name=func_name, # code=resp_code, # content=content) raise Exception("{}: API returned status {}\n{}".format( func_name, resp_code, content)) parsed = yield resp.json()
def api_set_account_status_(self, status, api_id): """ Call the Zoom API to set an account status. """ log = self.log func_name = 'api_set_account_status_()' if status == "deactivate": allowed_response_codes = (204, 404) else: allowed_response_codes = (204, ) http_client = self.http_client prefix = self.url_prefix url = "{}/users/{}/status".format(prefix, api_id) headers = { 'Accept': ['application/json'], 'Content-Type': ['application/json'], } props = { 'action': status, } serialized = json.dumps(props) body = StringProducer(serialized.encode('utf-8')) log.debug("url: {url}", url=url) log.debug("headers: {headers}", headers=headers) log.debug("body: {body}", body=serialized) resp = yield self.make_authenticated_api_call("PUT", url, headers=headers, data=body) resp_code = resp.code try: content = yield resp.content() except Exception as ex: pass if resp_code not in allowed_response_codes: raise Exception( "{}: API call to set account status to '{}' returned HTTP status {}\n{}" .format(func_name, status, resp_code, content)) returnValue(None)
def api_update_subject(self, subject, api_id, attributes): """ Make API request to update remote account. Returns the HTTP response. """ log = self.log prefix = self.url_prefix url = "{0}/users/{1}".format(prefix, api_id) headers = { 'Accept': ['application/json'], 'Content-Type': ['application/json'], } surname = attributes.get("sn", [""])[0] givenname = attributes.get("givenName", [""])[0] displayname = "{0}, {1}".format(surname, givenname) upn = "{0}@{1}".format(subject, self.domain) immutable_id = attributes.get("bannerLNumber", [None])[0] props = { 'accountEnabled': True, 'displayName': displayname, 'givenName': givenname, 'surname': surname, 'userPrincipalName': upn, 'mailNickname': subject, 'usageLocation': 'US', } if not immutable_id is None: props["onPremisesImmutableId"] = immutable_id serialized = json.dumps(props) body = StringProducer(serialized.encode('utf-8')) log.debug("url: {url}", url=url) log.debug("headers: {headers}", headers=headers) log.debug("body: {body}", body=serialized) resp = yield self.make_authenticated_api_call('PATCH', url, data=body, headers=headers) returnValue(resp)
def api_update_subject(self, subject, api_id, attributes): """ Make API request to update remote account. Returns the HTTP response. """ log = self.log assert not attributes is None, "api_update_subject(): `attributes` is None!" # If you are updating a subject, it must be active (i.e. provisioned). # Therefor, it's state in Zoom must be set to active. yield self.api_activate_account_(api_id) prefix = self.url_prefix url = "{}/users/{}".format(prefix, api_id) headers = { 'Accept': ['application/json'], 'Content-Type': ['application/json'], } surname = attributes.get("sn", [""])[0] givenname = attributes.get("displayName", [""])[0] if givenname == "": givenname = attributes.get("givenName", [""])[0] new_user_type = self.new_user_type props = { 'first_name': givenname, 'last_name': surname, } if self.update_license: log.debug("Updating license type.") props["type"] = new_user_type serialized = json.dumps(props) body = StringProducer(serialized.encode('utf-8')) log.debug("url: {url}", url=url) log.debug("headers: {headers}", headers=headers) log.debug("body: {body}", body=serialized) resp = yield self.make_authenticated_api_call('PATCH', url, data=body, headers=headers) returnValue(resp)
def api_update_subject(self, subject, api_id, attributes): """ Make API request to update remote account. Returns the HTTP response. """ log = self.log prefix = self.url_prefix url = "{0}/users/{1}".format(prefix, api_id) headers = { 'Accept': ['application/json'], 'Content-Type': ['application/json'], } organization = self.organization username = "******".format(subject.lower(), organization.lower()) surname = attributes.get("sn", [""])[0] givenname = attributes.get("givenName", [""])[0] displayname = attributes.get("displayName", [""])[0] firstname = displayname or givenname email = attributes.get("mail", [""])[0] props = { 'firstName': firstname, 'lastName': surname, 'email': email, 'username': username, 'status': 'active', } serialized = json.dumps(props) body = StringProducer(serialized.encode('utf-8')) log.debug("url: {url}", url=url) log.debug("headers: {headers}", headers=headers) log.debug("body: {body}", body=serialized) resp = yield self.make_authenticated_api_call('PUT', url, data=body, headers=headers) returnValue(resp)
def api_add_subject(self, subject, attributes): """ Use the API to add subjects. Returns the API ID of the newly created remote account or None. If None is returned, the API ID will not be cached and require a lookup on future use. """ log = self.log log.debug("Entered: api_add_subject().") prefix = self.url_prefix url = "{0}/users".format(prefix) headers = { 'Accept': ['application/json'], 'Content-Type': ['application/json'], } organization = self.organization username = "******".format(subject.lower(), organization.lower()) surname = attributes.get("sn", [""])[0] givenname = attributes.get("givenName", [""])[0] displayname = attributes.get("displayName", [""])[0] firstname = displayname or givenname email = attributes.get("mail", [""])[0] props = { 'username': username, 'password': generate_password(), 'firstName': firstname, 'lastName': surname, 'userType': self.new_user_type, 'email': email, 'language': self.language, } serialized = json.dumps(props) body = StringProducer(serialized.encode('utf-8')) log.debug("url: {url}", url=url) log.debug("headers: {headers}", headers=headers) log.debug("body: {body}", body=serialized) resp = yield self.make_authenticated_api_call('POST', url, data=body, headers=headers) resp_code = resp.code log.debug("Add-subject API response code: {code}", code=resp_code) if resp_code != 200: content = yield resp.content() log.error("API response {code}: {content}", code=resp_code, content=content) raise Exception("API returned status {0}".format(resp_code)) else: parsed = yield resp.json() log.debug("Parse API result is: {parsed}", parsed=parsed) if not "result" in parsed: raise Exception("Error in `api_add_subject()`: {}".format( json.dumps(parsed))) result = parsed['result'] if not "id" in result: raise Exception("Error in `api_add_subject()`: {}".format( json.dumps(parsed))) api_id = result["id"] returnValue(api_id)