创建身份连接器

默认情况下,Google Cloud Search 仅识别 Google Cloud Directory 中的 Google 身份。使用身份连接器将企业身份与 Cloud Search 使用的 Google 身份同步。

Google 提供了以下选项来开发身份连接器:

使用身份连接器 SDK 创建身份连接器

通常,身份连接器可执行以下任务:

  1. 配置连接器。
  2. 从您的身份系统中检索用户,并将相关信息发送给 Google。
  3. 从您的身份系统中检索群组,并将它们发送给 Google。

设置依赖项

在 build 文件中添加这些依赖项。

Maven

<dependency>
  <groupId>com.google.enterprise.cloudsearch</groupId>
  <artifactId>google-cloudsearch-identity-connector-sdk</artifactId>
  <version>v1-0.0.3</version>
</dependency>

Gradle

compile group: 'com.google.enterprise.cloudsearch',
        name: 'google-cloudsearch-identity-connector-sdk',
        version: 'v1-0.0.3'

创建连接器配置

每个连接器都使用一个配置文件来存储参数,例如代码库 ID。将参数定义为键值对,例如 api.sourceId=1234567890abcdef

Google Cloud Search SDK 包含 Google 提供的适用于所有连接器的参数。您必须在配置文件中声明以下内容:

  • 内容连接器:声明 api.sourceIdapi.serviceAccountPrivateKeyFile。这些参数用于标识您的代码库以及访问所需的私钥。
  • 身份连接器:声明 api.identitySourceId 以标识您的外部身份源。对于用户同步,还要声明 api.customerId(您的 Google Workspace 账号的唯一 ID)。

仅声明其他由 Google 提供的参数,以替换其默认值。 如需详细了解如何生成 ID 和密钥,请参阅 Google 提供的参数

您还可以在配置文件中定义代码库专属参数。

将配置文件传递给连接器

设置 config 系统属性以传递配置文件。启动连接器时使用 -D 实参。例如:

java -classpath myconnector.jar -Dconfig=MyConfig.properties MyConnector

如果您省略此实参,SDK 会尝试使用本地目录中名为 connector-config.properties 的文件。

使用模板类创建完全同步的身份连接器

该 SDK 包含一个 FullSyncIdentityConnector 模板,用于同步代码库中的所有用户和群组。本部分将介绍如何使用该功能。

本部分引用了 IdentityConnectorSample.java 示例中的代码,该示例可从 CSV 文件中读取身份。

实现连接器入口点

入口点是 main() 方法。它会创建一个 Application 实例并调用 start() 来运行连接器。

在调用 application.start() 之前,请使用 IdentityApplication.Builder 实例化 FullSyncIdentityConnector 模板。

IdentityConnectorSample.java
/**
 * This sample connector uses the Cloud Search SDK template class for a full
 * sync connector. In the full sync case, the repository is responsible
 * for providing a snapshot of the complete identity mappings and
 * group rosters. This is then reconciled against the current set
 * of mappings and groups in Cloud Directory.
 *
 * @param args program command line arguments
 * @throws InterruptedException thrown if an abort is issued during initialization
 */
public static void main(String[] args) throws InterruptedException {
  Repository repository = new CsvRepository();
  IdentityConnector connector = new FullSyncIdentityConnector(repository);
  IdentityApplication application = new IdentityApplication.Builder(connector, args).build();
  application.start();
}

在您的 main() 方法调用 Application.build() 后,SDK 会调用 initConfig()initConfig() 方法:

  1. 确保 Configuration 尚未初始化。
  2. 使用 Google 提供的键值对初始化 Configuration 对象。

实现 Repository 接口

Repository 对象用于将代码库身份同步到 Google 身份。使用模板时,您只需重写某些方法。对于 FullSyncIdentityConnector,请替换以下方法:

获取自定义配置参数

Configuration 对象(通常在 init() 方法中)检索自定义参数。以下代码段展示了如何检索 CSV 路径:

IdentityConnectorSample.java
/**
 * Initializes the repository once the SDK is initialized.
 *
 * @param context Injected context, contains convenienve methods
 *                for building users & groups
 * @throws IOException if unable to initialize.
 */
@Override
public void init(RepositoryContext context) throws IOException {
  log.info("Initializing repository");
  this.context = context;
  userMappingCsvPath = Configuration.getString(
      "sample.usersFile", "users.csv").get().trim();
  groupMappingCsvPath = Configuration.getString(
      "sample.groupsFile", "groups.csv").get().trim();
}

如需获取和解析包含多个值的参数,请使用 Configuration 类的一个类型解析器将数据解析为离散区块。在示例教程中,我们会看到连接器中的以下代码段使用 getMultiValue 方法获取一个包含多个 GitHub 代码库名称的列表:

GithubRepository.java
ConfigValue<List<String>> repos = Configuration.getMultiValue(
    "github.repos",
    Collections.emptyList(),
    Configuration.STRING_PARSER);

获取所有用户的映射

替换 listUsers() 以检索用户映射。此方法接受检查点,以便在同步中断时恢复同步。对于每位用户:

  1. 获取 Google 身份与外部身份之间的映射。
  2. 将该映射对打包到 listUsers() 返回的迭代器中。

获取用户映射

此代码段演示了如何从 CSV 文件中检索身份映射:

IdentityConnectorSample.java
/**
 * Retrieves all user identity mappings for the identity source. For the
 * full sync connector, the repository must provide a complete snapshot
 * of the mappings. This is reconciled against the current mappings
 * in Cloud Directory. All identity mappings returned here are
 * set in Cloud Directory. Any previously mapped users that are omitted
 * are unmapped.
 *
 * The connector does not create new users. All users are assumed to
 * exist in Cloud Directory.
 *
 * @param checkpoint Saved state if paging over large result sets. Not used
 *                   for this sample.
 * @return Iterator of user identity mappings
 * @throws IOException if unable to read user identity mappings
 */
@Override
public CheckpointCloseableIterable<IdentityUser> listUsers(byte[] checkpoint)
    throws IOException {
  List<IdentityUser> users = new ArrayList<>();
  try (Reader in = new FileReader(userMappingCsvPath)) {
    // Read user mappings from CSV file
    CSVParser parser = CSVFormat.RFC4180
        .withIgnoreSurroundingSpaces()
        .withIgnoreEmptyLines()
        .withCommentMarker('#')
        .parse(in);
    for (CSVRecord record : parser.getRecords()) {
      // Each record is in form: "primary_email", "external_id"
      String primaryEmailAddress = record.get(0);
      String externalId = record.get(1);
      if (primaryEmailAddress.isEmpty() || externalId.isEmpty()) {
        // Skip any malformed mappings
        continue;
      }
      log.info(() -> String.format("Adding user %s/%s",
          primaryEmailAddress, externalId));

      // Add the identity mapping
      IdentityUser user = context.buildIdentityUser(
          primaryEmailAddress, externalId);
      users.add(user);
    }
  }
  // ...
}

将用户映射打包到迭代器中

listUsers() 方法会返回一个 IdentityUser 对象的 CheckpointCloseableIterable

IdentityConnectorSample.java
CheckpointCloseableIterable<IdentityUser> iterator =
  new CheckpointCloseableIterableImpl.Builder<IdentityUser>(users)
      .setHasMore(false)
      .setCheckpoint((byte[])null)
      .build();

获取群组

替换 listGroups() 以检索群组及其成员。此方法接受检查点。对于每个群组:

  1. 获取群组及其成员。
  2. 将它们打包到 listGroups() 返回的迭代器中。

获取群组身份

此代码段演示了如何从 CSV 文件中检索群组和成员:

IdentityConnectorSample.java
/**
 * Retrieves all group rosters for the identity source. For the
 * full sync connector, the repository must provide a complete snapshot
 * of the rosters. This is reconciled against the current rosters
 * in Cloud Directory. All groups and members  returned here are
 * set in Cloud Directory. Any previously created groups or members
 * that are omitted are removed.
 *
 * @param checkpoint Saved state if paging over large result sets. Not used
 *                   for this sample.
 * @return Iterator of group rosters
 * @throws IOException if unable to read groups
 */    @Override
public CheckpointCloseableIterable<IdentityGroup> listGroups(byte[] checkpoint)
    throws IOException {
  List<IdentityGroup> groups = new ArrayList<>();
  try (Reader in = new FileReader(groupMappingCsvPath)) {
    // Read group rosters from CSV
    CSVParser parser = CSVFormat.RFC4180
        .withIgnoreSurroundingSpaces()
        .withIgnoreEmptyLines()
        .withCommentMarker('#')
        .parse(in);
    for (CSVRecord record : parser.getRecords()) {
      // Each record is in form: "group_id", "member"[, ..., "memberN"]
      String groupName = record.get(0);
      log.info(() -> String.format("Adding group %s", groupName));
      // Parse the remaining columns as group memberships
      Supplier<Set<Membership>> members = new MembershipsSupplier(record);
      IdentityGroup group = context.buildIdentityGroup(groupName, members);
      groups.add(group);
    }
  }
  // ...

}

将群组和成员打包到迭代器中

listGroups() 方法会返回一个 IdentityGroup 对象的 CheckpointCloseableIterable

IdentityConnectorSample.java
CheckpointCloseableIterable<IdentityGroup> iterator =
   new CheckpointCloseableIterableImpl.Builder<IdentityGroup>(groups)
      .setHasMore(false)
      .setCheckpoint((byte[])null)
      .build();

后续步骤