概述

OIM对于用户数据有较为完善的方案,可以做到根据策略自动推送,字段更新自动推送等,详细方案可以参考之前的文章,相比用户数据,组织数据确没有这么便利,很多需求需要自开发,本文系统性介绍关于组织,在OIM中如何做到生产级别的可用性。

连接器

连接器方案上首选Webservice的连接器,因为接口可以自开发,扩展性强,可以根据业务定制接口,但遗憾的是Webservice Connector并不支持组织,而且没有官方的12c版本,导致不能使用12c的新特性。但幸运的是,可以通过更改资源类型让Webservice Connector支持组织数据,具体步骤可以参考之前的这篇文章,这里就不再赘述。

当然,你也可以选择自开发连接器,这个不在本文的讨论范围内,可以参考官方文档

定时任务

在用户数据同步中,可以通过策略+角色实现自动推送,组织数据跟用户数据不同的是,组织数据是树结构,子节点的行为一般要和父节点保持一致,因此组织数据做不到自动推送的效果,我们需要开发一个定时器定时去做推送。OIM有提供定时调度功能,也支持自定义定时器。

依赖包

你需要从oim的服务器将依赖包下载下来,如果只是开发定时器,不涉及到其他oim功能,可以只下载下面这个jar包:

${ORACLE_HOME}/idm/server/client/oimclient.jar

比如:

/u01/app/oracle/Oracle/Middleware/Oracle_Home/idm/server/client/oimclient.jar

开发时可以作为项目依赖,建议使用maven对jar包进行管理,这样打包的时候比较方便,可以通过以下命令将jar包加入本地maven库中

mvn install:install-file -Dfile="/you/path/oimclient.jar" -DgroupId=com.oracle -DartifactId=oimclient -Dversion=12c -Dpackaging=jar
pom.xml
<dependency>
    <groupId>com.oracle</groupId>
    <artifactId>oimclient</artifactId>
    <version>12c</version>
    <scope>provided</scope>
</dependency>
provided表示该依赖由运行时环境提供,打包的时候不会打进去,当开发完上传到服务器时,无需将依赖一起上传。

开发

public class ExecuteOrgSyncTask extends TaskSupport {
    private static final Logger logger = new Logger("ExecuteOrgSyncTask");
    @Override
    public void execute(HashMap hashMap) throws Exception {
        logger.info("############# 组织同步程序开始运行#############");
        String org = null;
        if (hashMap != null) {
            for (Object item : hashMap.keySet()) {
                logger.info("key===>%s value====>%s", item, hashMap.get(item));
            }
            org = (String) hashMap.get("Org");
        } else {
            logger.info("无参数");
        }
        
        OIMOrgSync sync = new OIMOrgSync();
        sync.perform(org);
    }
    @Override
    public HashMap getAttributes() {
        return null;
    }
    @Override
    public void setAttributes() {
    }
}

定时器只需继承oracle.iam.scheduler.vo.TaskSupport类即可,实现execute方法,该方法由一个参数,表示由OIM传入定时器的参数,定时器可以指定参数,这个下面会讲到。

然后你需要创建一个plugin.xml文件,名称是固定的。文件内容参考以下格式

<?xml version="1.0" encoding="UTF-8"?>
<oimplugins xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <plugins pluginpoint="oracle.iam.scheduler.vo.TaskSupport">
        <plugin pluginclass="com.faw.oim.sync.schedule.ExecuteOrgSyncTask" version="1.0" name="ExecuteOrgSyncTask"/>
    </plugins>
</oimplugins>

只需根据具体情况修改pluginclassname参数即可。

开发完将你的代码打包好,创建一个目录,将jar包和plugin.xml文件放在目录中,然后把两个文件压缩成一个zip文件。如果你是mac,可以直接参考以下命令

$ ll
total 7688
-rw-r--r--  1 asan  staff  2055961 Apr 29 15:04 oim-organization-sync-1.0.jar
-rw-r--r--  1 asan  staff      315 Apr 29 14:35 plugin.xml
$ zip oim-organization-sync.zip *
$ll
-rw-r--r--  1 asan  staff  2055961 Apr 29 15:04 oim-organization-sync-1.0.jar
-rw-r--r--  1 asan  staff  1871069 Apr 29 15:04 oim-organization-sync.zip
-rw-r--r--  1 asan  staff      315 Apr 29 14:35 plugin.xml

将oim-organization-sync.zip文件上传到${ORACLE_HOME}/idm/server/plugins

/u01/app/oracle/Oracle/Middleware/Oracle_Home/idm/server/plugins

将oim-organization-sync-1.0.jar文件上传到${ORACLE_HOME}/idm/server/ScheduleTask

如果修改程序只需重新上传覆盖就行,不需要重启服务器

task

你需要准备一个task.xml的文件,文件内容参考如下:

<?xml version="1.0" encoding="UTF-8"?>
<xl-ddm-data version="2.0.1.0" user="XELSYSADM" database=""
             exported-date="1342018530943" description="OrgSyncTask">
    <scheduledTask repo-type="MDS" name="OrgSyncTask" mds-path="/db" mds-file="OrgSyncTask.xml">
        <completeXml>
            <scheduledTasks xmlns="http://xmlns.oracle.com/oim/scheduler">
                <task>
                    <name>OrgSyncTask</name>
                    <class>com.faw.oim.sync.schedule.ExecuteOrgSyncTask</class>
                    <description>OrgSyncTask</description>
                    <retry>5</retry>
                    <parameters>
                        <string-param required="true" helpText="指定组织">Org</string-param>
                    </parameters>
                </task>
            </scheduledTasks>
        </completeXml>
    </scheduledTask>
</xl-ddm-data>
OrgSyncTask是任务名称,你只需替换即可。如果需要定义参数,可以在parameters下增加参数,支持的参数格式可以参考以下:
<xsd:complexType name="parameters">
	<xsd:sequence>
	<xsd:choice minOccurs="0" maxOccurs="unbounded">
		<xsd:element name="string-param" type="string-param" minOccurs="0" maxOccurs="unbounded"/>
		<xsd:element name="boolean-param" type="boolean-param" minOccurs="0" maxOccurs="unbounded"/>
		<xsd:element name="number-param" type="number-param" minOccurs="0" maxOccurs="unbounded"/>
		<xsd:element name="it-res-param" type="it-res-param" minOccurs="0" maxOccurs="unbounded"/>
	</xsd:choice>
	</xsd:sequence>
</xsd:complexType>

登录sysadmin(http://{host}:{port}/sysadmin),点击System Configuration->Import,选择上面创建的文件,点击Next

确认信息没问题后,点击Import导入

这样就完成了任务的定义

如果有修改,可以重新上传覆盖就行,但根据经验,上传很容易失败,你可以统一把上面的name修改成其他的就可以。但这样做的话之前的job需要重新定义。

job

定义好task后,就可以创建任务了,点击system Configuration->Scheduler,点击创建的图标,创建job

输入任务相关信息,选择上面创建的Task

有几个点需要注意

  • Start Date需要选择晚于当前日期,不然无法创建
  • Retries 这个不是错误重试,而且在指定周期内最多运行次数,不能为0!!
  • Schedule Type可以选择轮询方案,支持cron基本就支持了大部分场景

创建job后,可以点击Run Now立即运行

根据经验,Run Now要点击两次才能生效一次,原因未知。

组织推送

有了定时器,就可以定时调用程序,接下来就是要写推送的程序,我们期望达到的效果是**当父组织指定了推送的系统后,所有的子组织能够自动关联上父组织关联的系统**,这些都要靠代码来实现。有三个问题需要解决

  • 数据从哪里来
  • 如何计算需要关联的组织数据
  • 如何将组织关联上系统

数据从哪里来

OIM中组织数据可以从三个地方来

  • java SDK
  • rest api
  • 数据库

java SDK和rest api功能有限,可以获取组织数据,但无法获取组织关联的系统数据,而且效率不高,在数据库中,涉及到组织有以下几张表

  • ACT:存储所有组织数据,OIM中将组织称为ACT
  • OBJ:存储资源数据,可以理解为关联的系统
  • OBI:实例-资源关系表
  • OIO:实例-组织关系表

可以通过以下sql查询所有组织

SELECT act_key, act_name, parent_key
FROM act
WHERE parent_key IS NOT NULL
	AND act_status = 'Active'
	AND act_cust_type <> 'System'
查询组织关联的系统
SELECT act.act_key, act.act_name, act.parent_key, obj.obj_key
FROM act act, obj obj, OBI obi, oio oio
WHERE act.act_key = oio.act_key(+)
	AND oio.obi_key = obi.obi_key
	AND obi.obj_key = obj.obj_key
	AND obj.obj_type = 'Generic'
	AND act.act_cust_type <> 'System'
	AND act.act_status = 'Active'
ORDER BY act_key ASC

于是我们拿到了所需的数据,接下来就是要根据这些数据计算相关逻辑。

计算

这个问题可以简化为,如果父组织为false,所有的子组织也都要为false,反之亦然,麻烦在于组织是树结构,需要根据parent不断查找父组织,一般的做法就是在java中实现一个树的数据结构,通过该数据结构进行操作,这样未免显得过于麻烦,我们写的是业务代码,而不是算法代码,这边提供了一种容易理解的方案:

假设数据是这样的

组织编号   父组织编号 
1           0         
2           1         
3           1         
4           2         
5           3         
6           4         
7           5

树结构应该是这样的

0
└── 1
    ├── 2
    │   └── 4
    │       └── 6
    └── 3
        └── 5
            └── 7

如果平铺开,并且给每个加上一个全路径数据,就变成这样

组织编号   父组织编号  路径
1           0         /0/1/
2           1         /0/1/2/
3           1         /0/1/3/
4           2         /0/1/2/4/
5           3         /0/1/3/5/
6           4         /0/1/2/4/6/
7           5         /0/1/2/5/7/

然后根据路径进行排序(上面已经是排好序),比如我们找1的子组织,那么1的路径是/0/1/,所有路径以/0/1/开头的都是1的子组织,比如我们找5的子组织,那么所有以/0/1/3/5/开头的都是5的子组织,通过路径将树结构转换为平铺结构,方便数据的处理。

关联系统

java SDK有提供关联系统的API,在tcOrganizationOperationsIntf中有以下api

long provisionObject(long plOrgKey,
                     long plObjectKey)
              throws tcAPIException,
                     tcObjectNotFoundException,
                     tcProvisioningNotAllowedException,
                     tcOrganizationNotFoundException,
                     tcAPIException

试过也确实可以关联上,但无法自动填充数据,尝试多种方法后,依然无法填充数据,遂放弃。在oim的rest api文档里,关于组织的api只有两个获取数据的接口,因此一度以为无法实现,但在通过sysadmin操作时,意外的发现sysadmin调用的是文档里未曾记录的api,于是仿照sysadmin调用的步骤,在工具里面调用了下,发现可行(Oracle产品实施,有时候需要一点运气)。

在通过sysadmin操作时,系统会调用4个接口,通过这4个接口配合来实现关联系统,填充数据。

  • iam/governance/adminservice/api/v1/resources/获取系统列表
  • /iam/governance/adminservice/api/v1/resources/后台创建实例,并返回实例号
  • /iam/governance/adminservice/api/v1/processforms/获取填充数据
  • /iam/governance/adminservice/api/v1/processforms/填充数据,和上面一个接口不同的是,获取填充数据是GET请求,填充数据是PATCH请求

因为我们通过sql已经拿到系统的编号了,所以第一接口是不需要调用的,简单封装了一个请求类,流程如下:

/**
 * 通过调用oim rest api执行创建object动作
 * ps:通过java api可以创建但无法自动预配
 *
 * @param actKey
 * @param objKey
 */
public void createOrgObject(String actKey, String objKey) {
    String s1 = String.format("{\"orgid\":\"%s\",\"operation\":\"provision\"}", actKey);
    //1. 创建表单
    String r1 = Http.build()
            .baseURL(API_BASEURL)
            .username(ADMIN)
            .contentType("application/json")
            .password(PASSWORD)
            .addHeader("X-Requested-By", "asd")
            .uri("resources/" + objKey)
            .body(s1)
            .patch();
    String processInstanceKey = JSON.parseObject(r1).getString("processInstanceKey");
    //2. 预填充表单
    String r2 = Http.build()
            .baseURL(API_BASEURL)
            .username(ADMIN)
            .contentType("application/json")
            .password(PASSWORD)
            .addQueryParam("processInstanceKey", processInstanceKey)
            .addQueryParam("resourceId", objKey)
            .addHeader("X-Requested-By", "asd")
            .uri("processforms")
            .body(s1)
            .get();
    JSONObject j1 = JSONObject.parseObject(r2);
    JSONObject j2 = new JSONObject();
    j2.put("formKey", j1.getString("formKey"));
    j2.put("processInstanceKey", processInstanceKey);
    j2.put("formFields", j1.getJSONArray("formFields"));
    //3. 提交表单
    String r3 = Http.build()
            .baseURL(API_BASEURL)
            .username(ADMIN)
            .contentType("application/json")
            .password(PASSWORD)
            .addQueryParam("processInstanceKey", processInstanceKey)
            .addQueryParam("resourceId", String.valueOf(objKey))
            .addHeader("X-Requested-By", "asd")
            .uri("processforms")
            .body(j2.toJSONString())
            .patch();
    JSONObject j3 = JSONObject.parseObject(r3);
    logger.info("actKey==>%s processInstanceKey==>%s", actKey, j3.getString("processInstanceKey"));
}

自动更新推送

自动更新推送是指,期望组织数据发生变更后,能自动将数据推送到系统中,这个在用户数据中是可以实现的,组织数据跟用户数据类似。

  • 登录design console,如果你不懂怎么登录design console,可以参考之前的文章
  • 进入Administration->Lookup Definition,搜索Lookup.ACT_PROCESS_TRIGGERS

oim标准支持这三个字段的自动更新推送,三个字段分别为

  • ACT_CUST_TYPE:组织类型
  • ACT_NAME:组织名称
  • PARENT_KEY:父组织

复制后面的Decode的值,这个值就是后面配Task的名称。

  • 进入Process Management->Process Definition,搜索系统资源名称
  • Tasks页面点击Add,Task Name输入上面复制的Decode字段,勾选相关选项,点击工具栏的保存
  • 在Response页面增加一行响应信息 SUCCESS:C,保存
  • 在Integration页面,点击Add新增,找到adpRETURNTEXTVALUE
  • 双击下图红框位置编辑变量参数
  • textValue选择Organizition Definition->Organizition Name,点击保存
根据经验,点击保存返回的时候,选择的adapter消失了,这里是个bug,重新选择adpRETURNTEXTVALUE就可以了
  • apter return value选择Process Data->字段,点击保存
  • 两个参数的Mapped都为Y即可
  • 关闭窗口后,在Tasks界面可以看到新添加的映射
  • 登录self service修改组织名称进行测试,正常的话可以在Resource History中看到记录

其他字段可以按照同样的方式进行配置

Trackback

no comment untill now

Add your comment now