From 60834dd93d287a6b1192b4eb0cd12cee2680a242 Mon Sep 17 00:00:00 2001 From: Ethan Kromhout Date: Thu, 26 Sep 2019 12:40:54 -0400 Subject: [PATCH] Using id for __NAME__ temporarily until we get username, also added sample configuration tested for importing accounts from the people API --- connector-federation-manager/pom.xml | 4 +- .../samples/connector-federation-manager.xml | 395 ++++++++++++++++++ .../FederationManagerConnector.java | 128 ++++-- 3 files changed, 492 insertions(+), 35 deletions(-) create mode 100644 connector-federation-manager/samples/connector-federation-manager.xml diff --git a/connector-federation-manager/pom.xml b/connector-federation-manager/pom.xml index be0ee68..45a2572 100644 --- a/connector-federation-manager/pom.xml +++ b/connector-federation-manager/pom.xml @@ -20,14 +20,14 @@ 4.0.0 - polygon + connector-parent com.evolveum.polygon 1.5.0.0 edu.unc.polygon connector-federation-manager - .1-SNAPSHOT + .2-SNAPSHOT jar Federation Manager Connector diff --git a/connector-federation-manager/samples/connector-federation-manager.xml b/connector-federation-manager/samples/connector-federation-manager.xml new file mode 100644 index 0000000..48f62d2 --- /dev/null +++ b/connector-federation-manager/samples/connector-federation-manager.xml @@ -0,0 +1,395 @@ + + Federation Manager + + 2019-09-26T13:49:18.786Z + + http://midpoint.evolveum.com/xml/ns/public/gui/channels-3#user + + + 2019-09-26T13:48:52.780Z + + + modify + c:ResourceType + + + com.evolveum.midpoint.model.impl.lens.ChangeExecutor.executeDelta + success + 1000000000000000461 + + Federation Manager + + success + + http://midpoint.evolveum.com/xml/ns/public/gui/channels-3#user + + + 2019-09-26T13:49:02.271Z + + + modify + c:ResourceType + + + com.evolveum.midpoint.model.impl.lens.ChangeExecutor.executeDelta + success + 1000000000000000531 + + Federation Manager + + success + + http://midpoint.evolveum.com/xml/ns/public/gui/channels-3#user + + + 2019-09-26T13:49:04.334Z + + + modify + c:ResourceType + + + com.evolveum.midpoint.model.impl.lens.ChangeExecutor.executeDelta + success + 1000000000000000578 + + Federation Manager + + success + + http://midpoint.evolveum.com/xml/ns/public/gui/channels-3#user + + + 2019-09-26T13:49:06.892Z + + + modify + c:ResourceType + + + com.evolveum.midpoint.model.impl.lens.ChangeExecutor.executeDelta + success + 1000000000000000625 + + Federation Manager + + success + + http://midpoint.evolveum.com/xml/ns/public/gui/channels-3#user + + + 2019-09-26T13:49:18.841Z + + + modify + c:ResourceType + + + com.evolveum.midpoint.model.impl.lens.ChangeExecutor.executeDelta + success + 1000000000000000749 + + Federation Manager + + success + + http://midpoint.evolveum.com/xml/ns/public/gui/channels-3#user + + 0 + + + up + + + + + c:connectorType + edu.unc.polygon.connector.federationManager.FederationManagerConnector + + + + + + true + X-API-KEY + https://fmdev.inc.testbed.tier.internet2.edu + 72038b582ca7535edd1e6f1a45402668 + + + + + 2019-09-26T13:49:20.391Z + 5ef692e6eaf161dc-171cde4a42e856e1 + + + + + + + + + + + icfs:uid + icfs:name + icfs:name + icfs:name + MESSAGE_OBJECT_CLASS___ACCOUNT__ + + + + + + + 120 + phonenumber + + + + + + + 130 + firstname + + + + + + + 140 + mobilenumber + + + + + + + 150 + email + + + + + + + 160 + organization_id + + + + + + + 170 + middlename + + + + + + + 180 + faxnumber + + + + + + + 190 + lastname + + + + + + + 200 + informalname + + + + + + + ConnId Name + 110 + __NAME__ + + + + + + + 210 + id + + + + + + + 220 + website + + + + + + + ConnId UID + 100 + read + + + + + + + + + + + account + default + true + MESSAGE_OBJECT_CLASS___ACCOUNT__ + + icfs:uid + + + $user/name + + + + + ri:email + + + $user/emailAddress + + + + + ri:firstname + + + $user/givenName + + + + + ri:lastname + + + $user/familyName + + + + + + + + 2019-09-26T13:49:20.386Z + 446a0fc7f58d15d6-939e1976a1f52a70 + + + + + + + connector + + + + + + + false + + + + + true + + + true + + connector + + + + true + + true + true + true + + + false + false + + + false + false + + + false + false + true + + + + true + false + + + + + + Account sync policy + MESSAGE_OBJECT_CLASS___ACCOUNT__ + account + default + c:UserType + true + + + polyStringNorm + c:name + + Matches using Name + declare namespace ri="http://midpoint.evolveum.com/xml/ns/public/resource/instance-3"; $account/attributes/icfs:name + + + + + linked + true + + + deleted + true + + http://midpoint.evolveum.com/xml/ns/public/model/action-3#deleteFocus + + + + unlinked + true + + http://midpoint.evolveum.com/xml/ns/public/model/action-3#link + + + + unmatched + true + + http://midpoint.evolveum.com/xml/ns/public/model/action-3#addFocus + + + + + + diff --git a/connector-federation-manager/src/main/java/edu/unc/polygon/connector/federationManager/FederationManagerConnector.java b/connector-federation-manager/src/main/java/edu/unc/polygon/connector/federationManager/FederationManagerConnector.java index fc99e70..326b967 100644 --- a/connector-federation-manager/src/main/java/edu/unc/polygon/connector/federationManager/FederationManagerConnector.java +++ b/connector-federation-manager/src/main/java/edu/unc/polygon/connector/federationManager/FederationManagerConnector.java @@ -63,13 +63,13 @@ * */ @ConnectorClass(displayNameKey = "connector.federation.manager.display", configurationClass = FederationManagerConfiguration.class) -public class FederationManagerConnector extends AbstractRestConnector implements TestOp, SchemaOp, PoolableConnector, SearchOp { +public class FederationManagerConnector extends AbstractRestConnector implements TestOp, SchemaOp, Connector, SearchOp { private static final Log LOG = Log.getLog(FederationManagerConnector.class); private static final String ATTR_ID = "id"; //__UID__ private static final String ATTR_ORGANIZATION_ID = "organization_id"; public static final String ATTR_MAIL = "email"; - public static final String ATTR_NAME = "email"; //hope to change this to username later __NAME__ + public static final String ATTR_NAME = "id"; //hope to change this to username later __NAME__ public static final String ATTR_FAX_NUMBER = "faxnumber"; public static final String ATTR_MOBILE_NUMBER = "mobilenumber"; public static final String ATTR_PHONE_NUMBER = "phonenumber"; @@ -120,10 +120,12 @@ public void test() { } +/* @Override public void checkAlive() { test(); } +*/ private void authHeader(HttpRequest request) { request.setHeader(configuration.getTokenName(), configuration.getTokenValue()); @@ -136,7 +138,7 @@ public Schema schema() { //Begin Account Objectclass Set accountAttributes = new HashSet(); AttributeInfoBuilder id = new AttributeInfoBuilder(); - id.setName("ATTR_ID"); + id.setName(ATTR_ID); id.setCreateable(true); id.setUpdateable(true); id.setReadable(true); @@ -144,7 +146,7 @@ public Schema schema() { id.setMultiValued(false); accountAttributes.add(id.build()); AttributeInfoBuilder email = new AttributeInfoBuilder(); - email.setName("ATTR_EMAIL"); + email.setName(ATTR_MAIL); email.setCreateable(true); email.setUpdateable(true); email.setReadable(true); @@ -152,7 +154,7 @@ public Schema schema() { email.setMultiValued(true); accountAttributes.add(email.build()); AttributeInfoBuilder faxnumber = new AttributeInfoBuilder(); - faxnumber.setName("ATTR_FAX_NUMBER"); + faxnumber.setName(ATTR_FAX_NUMBER); faxnumber.setCreateable(true); faxnumber.setUpdateable(true); faxnumber.setReadable(true); @@ -160,7 +162,7 @@ public Schema schema() { faxnumber.setMultiValued(false); accountAttributes.add(faxnumber.build()); AttributeInfoBuilder firstname = new AttributeInfoBuilder(); - firstname.setName("ATTR_FIRST_NAME"); + firstname.setName(ATTR_FIRST_NAME); firstname.setCreateable(true); firstname.setUpdateable(true); firstname.setReadable(true); @@ -168,7 +170,7 @@ public Schema schema() { firstname.setMultiValued(false); accountAttributes.add(firstname.build()); AttributeInfoBuilder informalname = new AttributeInfoBuilder(); - informalname.setName("ATTR_INFORMAL_NAME"); + informalname.setName(ATTR_INFORMAL_NAME); informalname.setCreateable(true); informalname.setUpdateable(true); informalname.setReadable(true); @@ -176,7 +178,7 @@ public Schema schema() { informalname.setMultiValued(false); accountAttributes.add(informalname.build()); AttributeInfoBuilder lastname = new AttributeInfoBuilder(); - lastname.setName("ATTR_LAST_NAME"); + lastname.setName(ATTR_LAST_NAME); lastname.setCreateable(true); lastname.setUpdateable(true); lastname.setReadable(true); @@ -184,7 +186,7 @@ public Schema schema() { lastname.setMultiValued(false); accountAttributes.add(lastname.build()); AttributeInfoBuilder middlename = new AttributeInfoBuilder(); - middlename.setName("ATTR_MIDDLE_NAME"); + middlename.setName(ATTR_MIDDLE_NAME); middlename.setCreateable(true); middlename.setUpdateable(true); middlename.setReadable(true); @@ -192,7 +194,7 @@ public Schema schema() { middlename.setMultiValued(false); accountAttributes.add(middlename.build()); AttributeInfoBuilder mobilenumber = new AttributeInfoBuilder(); - mobilenumber.setName("ATTR_MOBILE_NAME"); + mobilenumber.setName(ATTR_MOBILE_NUMBER); mobilenumber.setCreateable(true); mobilenumber.setUpdateable(true); mobilenumber.setReadable(true); @@ -200,7 +202,8 @@ public Schema schema() { mobilenumber.setMultiValued(false); accountAttributes.add(mobilenumber.build()); AttributeInfoBuilder organization_id = new AttributeInfoBuilder(); - organization_id.setName("ATTR_ORGANIZATION_ID"); + organization_id.setName(ATTR_ORGANIZATION_ID); + organization_id.setType(int.class); organization_id.setCreateable(true); organization_id.setUpdateable(true); organization_id.setReadable(true); @@ -208,7 +211,7 @@ public Schema schema() { organization_id.setMultiValued(false); accountAttributes.add(organization_id.build()); AttributeInfoBuilder phonenumber = new AttributeInfoBuilder(); - phonenumber.setName("ATTR_PHONE_NUMBER"); + phonenumber.setName(ATTR_PHONE_NUMBER); phonenumber.setCreateable(true); phonenumber.setUpdateable(true); phonenumber.setReadable(true); @@ -216,7 +219,7 @@ public Schema schema() { phonenumber.setMultiValued(false); accountAttributes.add(phonenumber.build()); AttributeInfoBuilder website = new AttributeInfoBuilder(); - website.setName("ATTR_WEBSITE"); + website.setName(ATTR_WEBSITE); website.setCreateable(true); website.setUpdateable(true); website.setReadable(true); @@ -240,7 +243,9 @@ public FilterTranslator createFilterTranslator(ObjectCl @Override public void executeQuery(ObjectClass oc, FederationManagerFilter filter, ResultsHandler handler, OperationOptions oo) { + LOG.info("starting executeQuery"); if ( oc.is(ObjectClass.ACCOUNT_NAME)) { + LOG.info("executeQuery ObjectClass.ACCOUNT_NAME"); /* Filtered searches aren't supported by this API yet, so we'll skip over these for now if (filter != null && filter.byUid != null) { //not sure if this use case exists yet @@ -257,27 +262,35 @@ else if (filter != null && filter.byEmailAddress != null) { //wide open search } */ + } + else { try { CloseableHttpClient client = HttpClients.createDefault(); - HttpRequestBase restGet; - restGet = new HttpGet(configuration.getServiceAddress() + "/siteadmin/api/people"); - authHeader(restGet); - //CloseableHttpResponse getResponse = client.execute(restGet); - //processResponseErrors(getResponse); - JSONArray searchResponseJson = new JSONArray(); - searchResponseJson = callRequestArray(restGet); - FileWriter file9 = new FileWriter("/tmp/file9.txt"); - file9.write(searchResponseJson.toString()); - System.out.println("Successfully Copied JSON Object to File..."); - System.out.println("\nJSON Object: " + searchResponseJson); - file9.flush(); - file9.close(); - for ( int i = 0; i < searchResponseJson.length(); i++ ) { - JSONObject uidSearchResponseJson = searchResponseJson.getJSONObject(i); - LOG.info(">>> processing ID " + i); - handleUserId( handler, uidSearchResponseJson ); - } - + HttpRequestBase restGet; + restGet = new HttpGet(configuration.getServiceAddress() + "/siteadmin/api/people"); + authHeader(restGet); + //CloseableHttpResponse getResponse = client.execute(restGet); + //processResponseErrors(getResponse); + JSONObject searchResponseJson = new JSONObject(); + searchResponseJson = callRequest(restGet); + FileWriter file9 = new FileWriter("/tmp/file9.txt"); + file9.write(searchResponseJson.toString()); + System.out.println("Successfully Copied JSON Object to File..."); + System.out.println("\nJSON Object: " + searchResponseJson); + file9.flush(); + file9.close(); + JSONArray peopleArray = new JSONArray(); + if ( searchResponseJson.has("data") && searchResponseJson.get("data") != null ) { + peopleArray = searchResponseJson.getJSONArray("data"); + for ( int i = 0; i < peopleArray.length(); i++ ) { + JSONObject personJson = peopleArray.getJSONObject(i); + LOG.info(">>> processing ID " + i); + handleUserId( handler, personJson ); + } + } + else { + LOG.info("No data object found in response"); + } } catch (IOException IOE) { throw new ConnectorIOException(IOE.getMessage(), IOE); @@ -288,6 +301,51 @@ else if (filter != null && filter.byEmailAddress != null) { } + protected JSONObject callRequest(HttpRequestBase request) throws IOException { + // don't log request here - password field !!! + LOG.info("request URI: {0}", request.getURI()); + request.setHeader("Content-Type", CONTENT_TYPE); + CloseableHttpClient client = HttpClients.createDefault(); + authHeader(request); + CloseableHttpResponse response = client.execute(request); + LOG.info("response: "); + String responseText = EntityUtils.toString(response.getEntity()); + FileWriter file = new FileWriter("/tmp/file1.txt"); + file.write(responseText); + System.out.println("Successfully Copied JSON Object to File..."); + System.out.println("\nJSON Object: " + responseText); + file.flush(); + file.close(); + int statusCode = response.getStatusLine().getStatusCode(); + /* No status responses to handle yet + if (statusCode == 500) { + JSONObject errorResult = new JSONObject(responseText); + //JSONObject errorData = errorResult.getJSONObject("data"); + if (errorResult.getString("code").equals("existing_user_login")) { + closeResponse(response); + throw new AlreadyExistsException(jo.getString(ATTR_NAME)); + } + if (errorResult.getString("code").equals("existing_user_email")) { + closeResponse(response); + throw new AlreadyExistsException(jo.getString(ATTR_MAIL)); + } + } + if (statusCode == 404) { + JSONObject errorResult = new JSONObject(responseText); + //JSONObject errorData = errorResult.getJSONObject("data"); + if (errorResult.getString("code").equals("rest_user_invalid_id")) { + closeResponse(response); + throw new UnknownUidException(errorResult.toString()); + } + } + */ + processResponseErrors(response); + JSONObject result = new JSONObject (responseText); + LOG.info("response body: "); + closeResponse(response); + return result; + } + protected JSONArray callRequestArray(HttpRequestBase request) throws IOException { // don't log request here - password field !!! LOG.info("request URI: {0}", request.getURI()); @@ -340,7 +398,11 @@ private void handleUserId ( ResultsHandler handler, JSONObject uidSearchResponse if ( attributes.has(potentialAttribute) && attributes.get(potentialAttribute) != null && !JSONObject.NULL.equals(attributes.get(potentialAttribute)) ) { LOG.info(">>> found attribute " + potentialAttribute ); if ( potentialAttribute.equals(ATTR_NAME) ) { - builder.setName(attributes.getString(potentialAttribute)); + //builder.setName(attributes.getString(potentialAttribute)); + builder.setName(String.valueOf(uidSearchResponseJson.getInt(ATTR_ID))); //Temp work around to use "id" as __NAME__ until we get username + } + else if ( potentialAttribute.equals(ATTR_ORGANIZATION_ID) ) { + addAttr(builder, potentialAttribute, attributes.getInt(potentialAttribute)); } else { addAttr(builder, potentialAttribute, attributes.getString(potentialAttribute));