1

我目前正在构建自己的Openidm 连接器,用于使用基于 ScriptedRest2DJ 示例的Groovy 框架连接器来配置外部系统。

我正在实现searchScript.groovy文件,以便在目标系统上搜索资源(用户),并且我想在我的请求中传递源系统的当前用户 UID。

这是 SearchScript.groovy 源代码:

import groovyx.net.http.RESTClient
import org.apache.http.client.HttpClient
import org.forgerock.openicf.connectors.scriptedrest.ScriptedRESTConfiguration
import org.forgerock.openicf.misc.scriptedcommon.OperationType
import org.identityconnectors.common.logging.Log
import org.identityconnectors.framework.common.objects.Attribute
import org.identityconnectors.framework.common.objects.AttributeUtil
import org.identityconnectors.framework.common.objects.Name
import org.identityconnectors.framework.common.objects.ObjectClass
import org.identityconnectors.framework.common.objects.OperationOptions
import org.identityconnectors.framework.common.objects.SearchResult
import org.identityconnectors.framework.common.objects.Uid
import org.identityconnectors.framework.common.objects.filter.Filter
import org.identityconnectors.framework.common.objects.AttributesAccessor

import static groovyx.net.http.Method.GET
import static groovyx.net.http.ContentType.JSON;

// imports used for CREST based REST APIs
import org.forgerock.openicf.misc.crest.CRESTFilterVisitor
import org.forgerock.openicf.misc.crest.VisitorParameter

def operation = operation as OperationType
def configuration = configuration as ScriptedRESTConfiguration
def httpClient = connection as HttpClient
def connection = customizedConnection as RESTClient
def filter = filter as Filter
def log = log as Log
def objectClass = objectClass as ObjectClass
def options = options as OperationOptions
def resultHandler = handler

log.info("Entering " + operation + " Script")

switch (objectClass) {
    case ObjectClass.ACCOUNT:
        // Search for a specific user in Alfresco
        // http://docs.alfresco.com/community/references/RESTful-PersonPersonGet.html
        def searchResult = connection.request(GET, JSON) { req ->
            uri.path = 'people'

            headers.Accept = 'application/json'

            response.success = { resp, json ->
                json.people.each() { value ->
                    resultHandler {
                        uid value.userName
                        id value.userName
                        attribute 'email', value?.email
                        attribute 'lastName', value?.lastName
                        attribute 'userName', value?.userName
                        attribute 'firstName', value?.firstName
                        attribute 'enabled', value?.enabled
                        //attribute ('groups', *(value?.groups))
                    }
                }
                json
            }
        }

        return new SearchResult(null, -1) // no remaining results
}

您如何访问脚本中的源值?我已经测试了 Uid、Id、Name、... 没有成功。

谢谢您的帮助

4

1 回答 1

2

For your SearchScript to do more than simply return all users, you need to make use of the "filter" object. You have it declared in your script, but you aren't using it - this is the object that has all of the details the connector needs to know what sort of "Search" this script should be doing on the backend (via REST in this case). Filter objects are essentially tree structures, and their expected use is based on the "Visitor Pattern."

The scriptedrest2dj sample that you are basing your code on shows the use of the CRESTFilterVisitor, which is a special one that is particularly helpful for working with OpenDJ (the target of that sample). For a more general-purpose visitor implementation, I suggest looking at sample3's SearchScript.groovy. Sample3 is an example of working with a SQL backend, but the visitor shown there can be used to generate any type of query string (such as one you might pass to your REST service).

From sample3's filter code, this returns a regular map of values representing the filter tree:

def query = filter.accept(MapFilterVisitor.INSTANCE, null)

Here is the structure of this map for simple values (eg. field eq "value") :

[ 
    "operation": "EQUALS|CONTAINS|STARTSWITH|ENDSWITH",
    "not": "true|false",
    "left": "fieldName",
    "right": "value"
]

"left" and "right" here can be thought of as sides of an equation. In the case of : 'Id eq "bob"' left will be "Id" and right will be "bob". "operation" would be "EQUALS".

And here is the structure for more complex expressions (eg. simpleExpr AND simpleExpr):

[ 
    "operation": "AND|OR",
    "left": "simpleExpression|complexExpression",
    "right": "simpleExpression|complexExpression"
]

As you can see here, you can have arbitrarily-complex boolean expressions represented within this map structure. Depending on how much complexity you care to support in your particular case, you can choose to support a fully-recursive expression builder (such as is shown in sample3) or you can simply support the core cases.

Here are some examples of the sorts of operation your script should be able to handle:

GET /openidm/system/scriptedrest/account?_queryId=query-all-ids

  • this is basically what you have now. When doing this operation, the "filter" object will be null and so you don't need to filter anything at all

GET /openidm/system/scriptedrest/account/bob

  • this is a "read" operation, but it is also implemented via the SearchScript. "Read" is exactly the same thing as a simple filter for a specific __UID__ value. The quick and dirty way to accomplish this would be to convert your filter object into a map (like I described) and then assume the map is only going to contain a filter searching for a specific __UID__, like so:

    def uid = query.get('right')

Then you can pass this into your REST call (probably by appending it to the uri.path variable).

Ideally, your search script should be able to handle more complex filters, like so:

GET /openidm/system/scriptedrest/account?_queryFilter=/email eq "bob@bob.com"

  • this is a more generic search for records with particular values. Your REST backend will have to support such things, and your filter query builder will have to be able to walk through the tree in order to build up the appropriate query string. Depending on what you need to do with this connector, it may not be necessary.
于 2015-03-24T19:07:53.447 回答