From 316e395f61700adb655ae8988671510500e42593 Mon Sep 17 00:00:00 2001 From: pamodaaw Date: Fri, 17 Jul 2020 21:47:23 +0530 Subject: [PATCH 1/3] Add the update functionality for provisioned user --- .../Office365ConnectorConstants.java | 1 + .../Office365ProvisioningConnector.java | 89 +++++++++++++++---- 2 files changed, 75 insertions(+), 15 deletions(-) diff --git a/components/org.wso2.carbon.identity.outbound.provisioning.connector.office365/src/main/java/org/wso2/carbon/identity/outbound/provisioning/connector/office365/Office365ConnectorConstants.java b/components/org.wso2.carbon.identity.outbound.provisioning.connector.office365/src/main/java/org/wso2/carbon/identity/outbound/provisioning/connector/office365/Office365ConnectorConstants.java index 43cedc9..d419b5f 100644 --- a/components/org.wso2.carbon.identity.outbound.provisioning.connector.office365/src/main/java/org/wso2/carbon/identity/outbound/provisioning/connector/office365/Office365ConnectorConstants.java +++ b/components/org.wso2.carbon.identity.outbound.provisioning.connector.office365/src/main/java/org/wso2/carbon/identity/outbound/provisioning/connector/office365/Office365ConnectorConstants.java @@ -36,6 +36,7 @@ public class Office365ConnectorConstants { public static final String OFFICE365_MEMBERSHIP_VALUE = "office-365-membership-rule-value"; public static final String WSO2_ROLE_CLAIM = "http://wso2.org/claims/role"; + public static final String WSO2_USERNAME_CLAIM = "http://wso2.org/claims/username"; public static final String OFFICE365_BASE_URL = "https://login.microsoftonline.com"; public static final String OFFICE365_TOKEN_ENDPOINT = "/oauth2/v2.0/token"; diff --git a/components/org.wso2.carbon.identity.outbound.provisioning.connector.office365/src/main/java/org/wso2/carbon/identity/outbound/provisioning/connector/office365/Office365ProvisioningConnector.java b/components/org.wso2.carbon.identity.outbound.provisioning.connector.office365/src/main/java/org/wso2/carbon/identity/outbound/provisioning/connector/office365/Office365ProvisioningConnector.java index 2a60df5..bab2c10 100644 --- a/components/org.wso2.carbon.identity.outbound.provisioning.connector.office365/src/main/java/org/wso2/carbon/identity/outbound/provisioning/connector/office365/Office365ProvisioningConnector.java +++ b/components/org.wso2.carbon.identity.outbound.provisioning.connector.office365/src/main/java/org/wso2/carbon/identity/outbound/provisioning/connector/office365/Office365ProvisioningConnector.java @@ -27,6 +27,7 @@ import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpDelete; +import org.apache.http.client.methods.HttpPatch; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpRequestBase; import org.apache.http.entity.StringEntity; @@ -177,10 +178,57 @@ protected String createUser(ProvisioningEntity provisioningEntity) throws Identi return provisionedId; } - protected void updateUser(ProvisioningEntity provisioningEntity) { + /** + * Call the update user endpoint of Azure AD and update the user properties. + * + * @param provisioningEntity user to be provisioned + * @throws IdentityProvisioningException if the user can not be created in the Azure AD + */ + protected void updateUser(ProvisioningEntity provisioningEntity) throws IdentityProvisioningException { + + String provisionedUserId = provisioningEntity.getIdentifier().getIdentifier(); + + try (CloseableHttpClient httpclient = HttpClientBuilder.create().useSystemProperties().build()) { + + JSONObject user = buildUserAsJson(provisioningEntity); + String updateUserEndpoint = Office365ConnectorConstants.OFFICE365_USER_ENDPOINT + '/' + provisionedUserId; + HttpPatch patchRequest = new HttpPatch(updateUserEndpoint); + setAuthorizationHeader(patchRequest); + + StringEntity requestBody = new StringEntity(user.toString()); + requestBody.setContentType(Office365ConnectorConstants.CONTENT_TYPE_APPLICATION_JSON); + patchRequest.setEntity(requestBody); + patchRequest.setHeader(Office365ConnectorConstants.CONTENT_TYPE, Office365ConnectorConstants + .CONTENT_TYPE_APPLICATION_JSON); + + try (CloseableHttpResponse response = httpclient.execute(patchRequest)) { + + if (response.getStatusLine().getStatusCode() == HttpStatus.SC_NO_CONTENT) { + if (log.isDebugEnabled()) { + log.debug("Successfully updated the provisioned user with id " + provisionedUserId + " in " + + "the Azure Active Directory."); + } + } else { + JSONObject jsonResponse = new JSONObject(new JSONTokener(new InputStreamReader( + response.getEntity().getContent()))); + String errorMessage = jsonResponse.getJSONObject("error").getString("message"); + + log.error("Received response status code: " + response.getStatusLine().getStatusCode() + " " + + response.getStatusLine().getReasonPhrase() + " with the message '" + errorMessage + + "' while updating the user with id " + provisionedUserId + " in the Azure Active " + + "Directory."); - log.warn("Update user is not implemented."); - // TODO: 8/14/18 Implement update user logic + if (log.isDebugEnabled()) { + log.debug("The response received from server : " + jsonResponse.toString()); + } + } + } catch (IOException | JSONException e) { + throw new IdentityProvisioningException("Error while executing the update operation in user " + + "provisioning.", e); + } + } catch (IOException e) { + log.error("Error while closing HttpClient."); + } } /** @@ -361,29 +409,31 @@ private JSONObject buildUserAsJson(ProvisioningEntity provisioningEntity) throws if (ruleAttributeClaim.isEmpty() && !ruleAttributeName.isEmpty()) { ruleAttributeClaim = Office365ConnectorConstants.WSO2_ROLE_CLAIM; } - String displayName = requiredAttributes.get(displayNameClaim); - String mailNickName = requiredAttributes.get(mailNickNameClaim); - String immutableId = requiredAttributes.get(immutableIdClaim); - String upn = requiredAttributes.get(upnClaim); - String ruleAttributeValue = requiredAttributes.get(ruleAttributeClaim); + String displayName = getUserClaimValue(requiredAttributes, displayNameClaim, provisioningEntity); + String mailNickName = getUserClaimValue(requiredAttributes, mailNickNameClaim, provisioningEntity); + String immutableId = getUserClaimValue(requiredAttributes, immutableIdClaim, provisioningEntity); + String upn = getUserClaimValue(requiredAttributes, upnClaim, provisioningEntity); + String ruleAttributeValue = getUserClaimValue(requiredAttributes, ruleAttributeClaim, provisioningEntity); if (displayName == null || mailNickName == null || immutableId == null || upn == null) { throw new IdentityProvisioningException("One or more of the mandatory user attributes: display name, mail" + - " " + - "nickname, immutable id, user principal name do not have a value."); + " nickname, immutable id, user principal name do not have a value."); } else { - // Create a json object corresponding to the attributes of the user in the request. - JSONObject passwordProfile = new JSONObject(); - passwordProfile.put(Office365ConnectorConstants.FORCE_CHANGE_PASSWORD, false); - passwordProfile.put(Office365ConnectorConstants.PASSWORD, generateRandomPassword()); + // Create a json object corresponding to the attributes of the user in the request. JSONObject user = new JSONObject(); user.put(Office365ConnectorConstants.ACCOUNT_ENABLED, true); user.put(Office365ConnectorConstants.OFFICE365_DISPLAY_NAME, displayName); user.put(Office365ConnectorConstants.OFFICE365_EMAIL_NICKNAME, mailNickName); user.put(Office365ConnectorConstants.OFFICE365_UPN, getDomainSpecificUpn(upn)); user.put(Office365ConnectorConstants.OFFICE365_IMMUTABLE_ID, immutableId); - user.put(Office365ConnectorConstants.PASSWORD_PROFILE, passwordProfile); + + if (ProvisioningOperation.POST == provisioningEntity.getOperation()) { + JSONObject passwordProfile = new JSONObject(); + passwordProfile.put(Office365ConnectorConstants.FORCE_CHANGE_PASSWORD, false); + passwordProfile.put(Office365ConnectorConstants.PASSWORD, generateRandomPassword()); + user.put(Office365ConnectorConstants.PASSWORD_PROFILE, passwordProfile); + } if (!ruleAttributeName.isEmpty()) { user.put(ruleAttributeName, ruleAttributeValue); } @@ -395,6 +445,15 @@ private JSONObject buildUserAsJson(ProvisioningEntity provisioningEntity) throws } } + private String getUserClaimValue(Map attributes, String requiredClaim, ProvisioningEntity entity) { + + if (Office365ConnectorConstants.WSO2_USERNAME_CLAIM.equals(requiredClaim)) { + return entity.getEntityName(); + } else { + return attributes.get(requiredClaim); + } + } + private void setAuthorizationHeader(HttpRequestBase httpMethod) throws IdentityProvisioningException { String accessToken = getAccessToken(); From 5f91c1b8696aca408281caed8f37a978f3e00383 Mon Sep 17 00:00:00 2001 From: pamodaaw Date: Wed, 22 Jul 2020 21:43:59 +0530 Subject: [PATCH 2/3] Add the stack trace for error logs --- .../office365/Office365ProvisioningConnector.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/components/org.wso2.carbon.identity.outbound.provisioning.connector.office365/src/main/java/org/wso2/carbon/identity/outbound/provisioning/connector/office365/Office365ProvisioningConnector.java b/components/org.wso2.carbon.identity.outbound.provisioning.connector.office365/src/main/java/org/wso2/carbon/identity/outbound/provisioning/connector/office365/Office365ProvisioningConnector.java index bab2c10..12d3bd9 100644 --- a/components/org.wso2.carbon.identity.outbound.provisioning.connector.office365/src/main/java/org/wso2/carbon/identity/outbound/provisioning/connector/office365/Office365ProvisioningConnector.java +++ b/components/org.wso2.carbon.identity.outbound.provisioning.connector.office365/src/main/java/org/wso2/carbon/identity/outbound/provisioning/connector/office365/Office365ProvisioningConnector.java @@ -173,7 +173,7 @@ protected String createUser(ProvisioningEntity provisioningEntity) throws Identi log.debug("Returning provisioned user's ID: " + provisionedId); } } catch (IOException e) { - log.error("Error while closing HttpClient."); + log.error("Error while closing HttpClient.", e); } return provisionedId; } @@ -227,7 +227,7 @@ protected void updateUser(ProvisioningEntity provisioningEntity) throws Identity "provisioning.", e); } } catch (IOException e) { - log.error("Error while closing HttpClient."); + log.error("Error while closing HttpClient.", e); } } @@ -275,7 +275,7 @@ protected void deleteUser(ProvisioningEntity provisioningEntity) throws Identity "provisioning", e); } } catch (IOException e) { - log.error("Error while closing HttpClient."); + log.error("Error while closing HttpClient.", e); } } @@ -325,7 +325,7 @@ protected void deleteUserPermanently(ProvisioningEntity provisioningEntity) thro } } catch (IOException e) { - log.error("Error while closing HttpClient."); + log.error("Error while closing HttpClient.", e); } } @@ -383,7 +383,7 @@ private String getAccessToken() throws IdentityProvisioningException { throw new IdentityProvisioningException("Error while obtaining the access token from the response.", e); } } catch (IOException e) { - log.error("Error while closing HttpClient."); + log.error("Error while closing HttpClient.", e); } return accessToken; } From 1865c009e9b2a87009731413334e27410b1c46b5 Mon Sep 17 00:00:00 2001 From: pamodaaw Date: Tue, 18 Aug 2020 20:17:00 +0530 Subject: [PATCH 3/3] Handle a possible null pointer --- .../Office365ProvisioningConnector.java | 66 +++++++++++++------ 1 file changed, 46 insertions(+), 20 deletions(-) diff --git a/components/org.wso2.carbon.identity.outbound.provisioning.connector.office365/src/main/java/org/wso2/carbon/identity/outbound/provisioning/connector/office365/Office365ProvisioningConnector.java b/components/org.wso2.carbon.identity.outbound.provisioning.connector.office365/src/main/java/org/wso2/carbon/identity/outbound/provisioning/connector/office365/Office365ProvisioningConnector.java index 12d3bd9..33ef8f4 100644 --- a/components/org.wso2.carbon.identity.outbound.provisioning.connector.office365/src/main/java/org/wso2/carbon/identity/outbound/provisioning/connector/office365/Office365ProvisioningConnector.java +++ b/components/org.wso2.carbon.identity.outbound.provisioning.connector.office365/src/main/java/org/wso2/carbon/identity/outbound/provisioning/connector/office365/Office365ProvisioningConnector.java @@ -154,11 +154,19 @@ protected String createUser(ProvisioningEntity provisioningEntity) throws Identi jsonResponse.toString()); } } else { - String errorMessage = jsonResponse.getJSONObject("error").getString("message"); - log.error("Received response status code: " + response.getStatusLine().getStatusCode() + " " - + response.getStatusLine().getReasonPhrase() + " with the message '" + errorMessage + - "' while creating the user " + user.getString(Office365ConnectorConstants.OFFICE365_UPN) + - " in the Azure Active Directory."); + if (jsonResponse.has("error")) { + String errorMessage = jsonResponse.getJSONObject("error").getString("message"); + log.error("Received response status code: " + response.getStatusLine().getStatusCode() + " " + + response.getStatusLine().getReasonPhrase() + " with the message '" + errorMessage + + "' while creating the user " + user.getString(Office365ConnectorConstants + .OFFICE365_UPN) + " in the Azure Active Directory."); + + } else { + log.error("Received response status code: " + response.getStatusLine().getStatusCode() + " " + + response.getStatusLine().getReasonPhrase() + " while creating the user " + user + .getString(Office365ConnectorConstants.OFFICE365_UPN) + " in the Azure Active " + + "Directory."); + } if (log.isDebugEnabled()) { log.debug("The response received from server : " + jsonResponse.toString()); @@ -211,12 +219,18 @@ protected void updateUser(ProvisioningEntity provisioningEntity) throws Identity } else { JSONObject jsonResponse = new JSONObject(new JSONTokener(new InputStreamReader( response.getEntity().getContent()))); - String errorMessage = jsonResponse.getJSONObject("error").getString("message"); - log.error("Received response status code: " + response.getStatusLine().getStatusCode() + " " - + response.getStatusLine().getReasonPhrase() + " with the message '" + errorMessage + - "' while updating the user with id " + provisionedUserId + " in the Azure Active " + - "Directory."); + if (jsonResponse.has("error")) { + String errorMessage = jsonResponse.getJSONObject("error").getString("message"); + log.error("Received response status code: " + response.getStatusLine().getStatusCode() + " " + + response.getStatusLine().getReasonPhrase() + " with the message '" + errorMessage + + "' while updating the user with id " + provisionedUserId + " in the Azure Active " + + "Directory."); + } else { + log.error("Received response status code: " + response.getStatusLine().getStatusCode() + " " + + response.getStatusLine().getReasonPhrase() + " while updating the user with id " + + provisionedUserId + " in the Azure Active " + "Directory."); + } if (log.isDebugEnabled()) { log.debug("The response received from server : " + jsonResponse.toString()); @@ -259,12 +273,18 @@ protected void deleteUser(ProvisioningEntity provisioningEntity) throws Identity } else { JSONObject jsonResponse = new JSONObject(new JSONTokener(new InputStreamReader( response.getEntity().getContent()))); - String errorMessage = jsonResponse.getJSONObject("error").getString("message"); - log.error("Received response status code: " + response.getStatusLine().getStatusCode() + " " - + response.getStatusLine().getReasonPhrase() + " with the message '" + errorMessage + - "' while deleting the user with id " + provisionedUserId + " from the Azure Active " + - "Directory."); + if (jsonResponse.has("error")) { + String errorMessage = jsonResponse.getJSONObject("error").getString("message"); + log.error("Received response status code: " + response.getStatusLine().getStatusCode() + " " + + response.getStatusLine().getReasonPhrase() + " with the message '" + errorMessage + + "' while deleting the user with id " + provisionedUserId + " from the Azure Active " + + "Directory."); + } else { + log.error("Received response status code: " + response.getStatusLine().getStatusCode() + " " + + response.getStatusLine().getReasonPhrase() + " while deleting the user with id " + + provisionedUserId + " from the Azure Active Directory."); + } if (log.isDebugEnabled()) { log.debug("The response received from server : " + jsonResponse.toString()); @@ -307,12 +327,18 @@ protected void deleteUserPermanently(ProvisioningEntity provisioningEntity) thro } else { JSONObject jsonResponse = new JSONObject(new JSONTokener(new InputStreamReader( response.getEntity().getContent()))); - String errorMessage = jsonResponse.getJSONObject("error").getString("message"); - log.error("Received response status code: " + response.getStatusLine().getStatusCode() + " " - + response.getStatusLine().getReasonPhrase() + " with the message '" + errorMessage + - "' while permanently removing the user with id " + provisionedUserId + - " in the Azure Active Directory."); + if (jsonResponse.has("error")) { + String errorMessage = jsonResponse.getJSONObject("error").getString("message"); + log.error("Received response status code: " + response.getStatusLine().getStatusCode() + " " + + response.getStatusLine().getReasonPhrase() + " with the message '" + errorMessage + + "' while permanently removing the user with id " + provisionedUserId + + " in the Azure Active Directory."); + } else { + log.error("Received response status code: " + response.getStatusLine().getStatusCode() + " " + + response.getStatusLine().getReasonPhrase() + " while permanently removing the user" + + " with id " + provisionedUserId + " in the Azure Active Directory."); + } if (log.isDebugEnabled()) { log.debug("The response received from server : " + jsonResponse.toString());