diff --git a/samples/resources/scriptedsql/BaseScript.groovy b/samples/resources/scriptedsql/BaseScript.groovy new file mode 100644 index 00000000000..f6039286bf3 --- /dev/null +++ b/samples/resources/scriptedsql/BaseScript.groovy @@ -0,0 +1,17 @@ +import org.identityconnectors.framework.common.objects.ObjectClass + +class BaseScript extends Script { + + public static final String GROUP_NAME = "Group" + + public static final ObjectClass GROUP = new ObjectClass(BaseScript.GROUP_NAME) + + public static final String ORGANIZATION_NAME = "Organization" + + public static final ObjectClass ORGANIZATION = new ObjectClass(BaseScript.ORGANIZATION_NAME) + + @Override + Object run() { + return null + } +} \ No newline at end of file diff --git a/samples/resources/scriptedsql/CreateScript.groovy b/samples/resources/scriptedsql/CreateScript.groovy new file mode 100644 index 00000000000..57045bf7654 --- /dev/null +++ b/samples/resources/scriptedsql/CreateScript.groovy @@ -0,0 +1,80 @@ +import groovy.sql.Sql +import org.forgerock.openicf.connectors.scriptedsql.ScriptedSQLConfiguration +import org.forgerock.openicf.misc.scriptedcommon.OperationType +import org.identityconnectors.common.logging.Log +import org.identityconnectors.framework.common.exceptions.ConnectorException +import org.identityconnectors.framework.common.objects.Attribute +import org.identityconnectors.framework.common.objects.ObjectClass +import org.identityconnectors.framework.common.objects.OperationOptions +import org.identityconnectors.framework.common.objects.Uid +import org.identityconnectors.framework.common.objects.AttributeUtil + +import java.sql.Connection + +def log = log as Log +def operation = operation as OperationType +def options = options as OperationOptions +def objectClass = objectClass as ObjectClass +def attributes = attributes as Set +def connection = connection as Connection +def id = id as String +def configuration = configuration as ScriptedSQLConfiguration + +log.info("Entering " + operation + " Script") + +def sql = new Sql(connection) + +switch (objectClass) { + case ObjectClass.ACCOUNT: + return handleAccount(sql) + case BaseScript.GROUP: + return handleGroup(sql) + case BaseScript.ORGANIZATION: + return handleOrganization(sql) + default: + throw new ConnectorException("Unknown object class " + objectClass) +} + +Uid handleAccount(Sql sql) { + def keys = sql.executeInsert("INSERT INTO Users (login, firstname,lastname,fullname,email,organization,password,disabled) values (?,?,?,?,?,?,?,?)", + [ + id, + AttributeUtil.getSingleValue("firstname"), + AttributeUtil.getSingleValue("lastname"), + AttributeUtil.getSingleValue("fullname"), + AttributeUtil.getSingleValue("email"), + AttributeUtil.getSingleValue("organization"), + // decrypt password + SecurityUtil.decrypt(AttributeUtil.getPasswordValue(attributes)), + // negate __ENABLE__ attribute + !(AttributeUtil.getSingleValue("__ENABLE__") as Boolean) + + //attributes.get("firstname") ? attributes.get("firstname").get(0) : "", + //attributes.get("lastname") ? attributes.get("lastname").get(0) : "", + //attributes.get("fullname") ? attributes.get("fullname").get(0) : "", + //attributes.get("email") ? attributes.get("email").get(0) : "", + //attributes.get("organization") ? attributes.get("organization").get(0) : "" + ]) + + return new Uid(keys[0][0]) +} + +Uid handleGroup(Sql sql) { + def keys = sql.executeInsert("INSERT INTO Groups (name,description) values (?,?)", + [ + id, + AttributeUtil.getSingleValue("description") + ]) + + return new Uid(keys[0][0]) +} + +Uid handleOrganization(Sql sql) { + def keys = sql.executeInsert("INSERT INTO Organizations (name,description) values (?,?)", + [ + id, + AttributeUtil.getSingleValue("description") + ]) + + return new Uid(keys[0][0]) +} \ No newline at end of file diff --git a/samples/resources/scriptedsql/SchemaScript.groovy b/samples/resources/scriptedsql/SchemaScript.groovy new file mode 100755 index 00000000000..80abd13c197 --- /dev/null +++ b/samples/resources/scriptedsql/SchemaScript.groovy @@ -0,0 +1,50 @@ +import org.forgerock.openicf.connectors.scriptedsql.ScriptedSQLConfiguration +import org.forgerock.openicf.misc.scriptedcommon.ICFObjectBuilder +import org.forgerock.openicf.misc.scriptedcommon.OperationType +import org.identityconnectors.common.logging.Log +import org.identityconnectors.framework.common.objects.ObjectClass +import org.identityconnectors.framework.common.objects.OperationOptionInfoBuilder +import org.identityconnectors.framework.spi.operations.SearchOp +import org.identityconnectors.framework.common.objects.OperationalAttributeInfos + +import static org.identityconnectors.framework.common.objects.AttributeInfo.Flags.MULTIVALUED + +def log = log as Log +def operation = operation as OperationType +def builder = builder as ICFObjectBuilder +def configuration = configuration as ScriptedSQLConfiguration + +log.info("Entering " + operation + " Script") + +builder.schema({ + objectClass { + type ObjectClass.ACCOUNT_NAME + attributes { + firstname() + lastname() + fullname() + email() + organization() + OperationalAttributeInfos.ENABLE + OperationalAttributeInfos.PASSWORD + OperationalAttributeInfos.LOCK_OUT + } + } + objectClass { + type BaseScript.GROUP + attributes { + name String.class, MULTIVALUED + description() + } + } + objectClass { + type BaseScript.ORGANIZATION + attributes { + name() + description() + } + } + + defineOperationOption OperationOptionInfoBuilder.buildPagedResultsOffset(), SearchOp + defineOperationOption OperationOptionInfoBuilder.buildPageSize(), SearchOp +}) \ No newline at end of file diff --git a/samples/resources/scriptedsql/SearchScript.groovy b/samples/resources/scriptedsql/SearchScript.groovy new file mode 100755 index 00000000000..abcb01700e5 --- /dev/null +++ b/samples/resources/scriptedsql/SearchScript.groovy @@ -0,0 +1,158 @@ +import groovy.sql.Sql +import org.forgerock.openicf.connectors.scriptedsql.ScriptedSQLConfiguration +import org.forgerock.openicf.misc.scriptedcommon.ICFObjectBuilder +import org.forgerock.openicf.misc.scriptedcommon.MapFilterVisitor +import org.forgerock.openicf.misc.scriptedcommon.OperationType +import org.identityconnectors.common.logging.Log +import org.identityconnectors.framework.common.exceptions.ConnectorException +import org.identityconnectors.framework.common.objects.ObjectClass +import org.identityconnectors.framework.common.objects.OperationOptions +import org.identityconnectors.framework.common.objects.ResultsHandler +import org.identityconnectors.framework.common.objects.SearchResult +import org.identityconnectors.framework.common.objects.filter.Filter + +import java.sql.Connection + +def log = log as Log +def operation = operation as OperationType +def options = options as OperationOptions +def objectClass = objectClass as ObjectClass +def configuration = configuration as ScriptedSQLConfiguration +def filter = filter as Filter +def connection = connection as Connection +def query = query as Closure +def handler = handler as ResultsHandler + +log.info("Entering " + operation + " Script") + +def sql = new Sql(connection) + +switch (objectClass) { + case ObjectClass.ACCOUNT: + handleAccount(sql) + break + case BaseScript.GROUP: + handleGroup(sql) + break + case BaseScript.ORGANIZATION: + handleOrganization(sql) + break + default: + throw new ConnectorException("Unknown object class " + objectClass) +} + +return new SearchResult() + +// ================================================================================= + +void handleAccount(Sql sql) { + Closure closure = { row -> + ICFObjectBuilder.co { + uid row.id as String + id row.login + attribute '__ENABLE__', !row.disabled + attribute 'fullname', row.fullname + attribute 'firstname', row.firstname + attribute 'lastname', row.lastname + attribute 'email', row.email + attribute 'organization', row.organization + } + } + + Map params = [:] + String where = buildWhereClause(filter, params, 'id', 'login') + + sql.eachRow(params, "SELECT * FROM Users " + where, closure); +} + + +void handleGroup(Sql sql) { + Closure closure = { row -> + ICFObjectBuilder.co { + uid row.id as String + id row.name + attribute 'description', row.description + } + } + + Map params = [:] + String where = buildWhereClause(filter, params, 'id', 'name') + + sql.eachRow(params, "SELECT * FROM Groups " + where, closure) +} + +void handleOrganization(Sql sql) { + Closure closure = { row -> + ICFObjectBuilder.co { + uid row.id as String + id row.name + attribute 'description', row.description + } + } + + Map params = [:] + String where = buildWhereClause(filter, params, 'id', 'name') + + sql.eachRow(params, "SELECT * FROM Organizations " + where, closure) +} + +static String buildWhereClause(Filter filter, Map sqlParams, String uidColumn, String nameColumn) { + if (filter == null) { + log.info("Returning empty where clause") + return "" + } + + Map query = filter.accept(MapFilterVisitor.INSTANCE, null) + + log.info("Building where clause, query {0}, uidcolumn {1}, nameColumn {2}", query, uidColumn, nameColumn) + + String columnName = uidColumn.replaceFirst("[\\w]+\\.", "") + + String left = query.get("left") + if (Uid.NAME.equals(left)) { + left = uidColumn + } else if (Name.NAME.equals(left)) { + left = nameColumn + } + + String right = query.get("right") + + String operation = query.get("operation") + switch (operation) { + case "CONTAINS": + right = '%' + right + '%' + break; + case "ENDSWITH": + right = '%' + right + break; + case "STARTSWITH": + right = right + '%' + break; + } + + sqlParams.put(columnName, right) + right = ":" + columnName + + def engine = new groovy.text.SimpleTemplateEngine() + + def whereTemplates = [ + CONTAINS : ' $left ${not ? "not " : ""}like $right', + ENDSWITH : ' $left ${not ? "not " : ""}like $right', + STARTSWITH : ' $left ${not ? "not " : ""}like $right', + EQUALS : ' $left ${not ? "<>" : "="} $right', + GREATERTHAN : ' $left ${not ? "<=" : ">"} $right', + GREATERTHANOREQUAL: ' $left ${not ? "<" : ">="} $right', + LESSTHAN : ' $left ${not ? ">=" : "<"} $right', + LESSTHANOREQUAL : ' $left ${not ? ">" : "<="} $right' + ] + + def wt = whereTemplates.get(operation) + def binding = [left: left, right: right, not: query.get("not")] + def template = engine.createTemplate(wt).make(binding) + def where = template.toString() + + log.info("Where clause: {0}, with parameters {1}", where, sqlParams) + + return where +} +