建立識別資訊連接器

根據預設,Google Cloud Search 只會辨識儲存在 Google Cloud Directory 中的 Google 身分 (使用者和群組)。身分連接器可用於將貴機構的身分同步至 Google Cloud Search 使用的 Google 身分。

Google 提供下列選項,方便您開發身分連接器:

  • Identity Connector SDK。這個選項適用於使用 Java 程式設計語言編寫程式的開發人員。Identity Connector SDK 是 REST API 的包裝函式,可讓您快速建立連接器。如要使用 SDK 建立識別資訊連接器,請參閱「使用 Identity Connector SDK 建立識別資訊連接器」。

  • 低階 REST API 和 API 程式庫。這些選項適用於可能不會以 Java 程式設計或程式碼集更適合 REST API 或程式庫的開發人員。如要使用 REST API 建立身分識別連接器,請參閱「Directory API:使用者帳戶」,瞭解如何對應使用者,以及參閱「Cloud Identity 說明文件」,瞭解如何對應群組。

使用 Identity Connector SDK 建立身分連接器

一般身分連接器會執行下列工作:

  1. 設定連接器。
  2. 從貴機構身分識別系統擷取所有使用者,並傳送給 Google,以便與 Google 身分進行同步。
  3. 從貴機構的身分識別系統中擷取所有群組,並傳送至 Google,以便與 Google 身分進行同步。

設定依附元件

您必須在建構檔案中加入特定依附元件,才能使用 SDK。按一下下方分頁標籤,即可查看建構環境的依附元件:

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 提供的設定參數,所有連接器都會使用這些參數。您必須在設定檔中宣告下列 Google 提供的參數:

  • 針對內容連接器,您必須宣告 api.sourceIdapi.serviceAccountPrivateKeyFile,因為這些參數會指出存放區的位置,以及存取存放區所需的私密金鑰。
  • 針對身分連接器,您必須宣告 api.identitySourceId,因為這個參數可識別外部身分來源的位置。如果您要同步處理使用者,也必須將 api.customerId 宣告為貴企業 Google Workspace 帳戶的專屬 ID。

除非您想覆寫 Google 提供的其他參數的預設值,否則不需要在設定檔中宣告這些參數。如要進一步瞭解 Google 提供的設定參數 (例如如何產生特定 ID 和金鑰),請參閱「Google 提供的設定參數」。

您也可以定義專屬的存放區參數,以便在設定檔中使用。

將設定檔傳遞至連接器

設定系統屬性 config,將設定檔傳遞至連接器。您可以在啟動連接器時,使用 -D 引數設定屬性。舉例來說,下列指令會使用 MyConfig.properties 設定檔啟動連接器:

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

如果沒有這個引數,SDK 會嘗試存取名為 connector-config.properties 的預設設定檔。

使用範本類別建立完整的同步識別資訊連接器

Identity Connector SDK 包含 FullSyncIdentityConnector 範本類別,可用於將身分資料存放區中的所有使用者和群組同步至 Google 身分。本節說明如何使用 FullSyncIdentityConnector 範本,從非 Google 身分識別碼存放區執行使用者和群組的完整同步處理作業。

本節說明文件會參照 IdentityConnecorSample.java 範例中的程式碼片段。這個範例會讀取兩個 CSV 檔案中的使用者和群組 ID,並將這些 ID 與 Google ID 同步。

實作連接器的進入點

連接器的進入點為 main() 方法。這個方法的主要工作是建立 Application 類別的例項,並叫用其 start() 方法來執行連接器。

在呼叫 application.start() 之前,請使用 IdentityApplication.Builder 類別將 FullSyncIdentityConnector 範本例項化。FullSyncIdentityConnector 會接受 Repository 物件,您將實作該物件的各項方法。下列程式碼片段說明如何實作 main() 方法:

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();
}

在幕後,SDK 會在連接器的 main() 方法呼叫 Application.build 後,呼叫 initConfig() 方法。initConfig() 方法會執行下列工作:

  1. 呼叫 Configuation.isInitialized() 方法,確保 Configuration 尚未初始化。
  2. 使用 Google 提供的鍵/值組合,初始化 Configuration 物件。每個鍵/值組合都會儲存在 Configuration 物件中的 ConfigValue 物件中。

實作 Repository 介面

Repository 物件的唯一用途,就是將存放區身分與 Google 身分進行同步。使用範本時,您只需在 Repository 介面中覆寫特定方法,即可建立身分連接器。針對 FullTraversalConnector,您可能會覆寫下列方法:

  • init() 方法。如要執行任何身分存放區設定和初始化作業,請覆寫 `init() 方法。

  • listUsers() 方法。如要將身分存放區中的所有使用者與 Google 使用者同步,請覆寫 listUsers() 方法。

  • listGroups() 方法。如要將身分資料存放區中的所有群組與 Google 群組保持同步,請覆寫 listGroups() 方法。

  • (選用) close() 方法。如果您需要執行存放區清理作業,請覆寫 close() 方法。系統會在關閉連接器時呼叫這個方法一次。

取得自訂設定參數

在處理連接器設定時,您需要從 Configuration 物件取得任何自訂參數。這項工作通常會在 Repository 類別的 init() 方法中執行。

Configuration 類別提供多種方法,可從設定中取得不同資料類型。每個方法都會傳回 ConfigValue 物件。接著,您將使用 ConfigValue 物件的 get() 方法擷取實際值。以下程式碼片段說明如何從 Configuration 物件擷取 userMappingCsvPathgroupMappingCsvPath 值:

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(),從您的身分資料存放區擷取所有使用者的對應項目。listUsers() 方法會接受代表要同步的最後一個身分的檢查點。如果程序中斷,檢查點可用於繼續同步處理。針對存放區中的每位使用者,您會在 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 物件的 Iterator,具體來說是 CheckpointCloseableIterable。您可以使用 CheckpointClosableIterableImpl.Builder 類別建構並傳回迭代器。以下程式碼片段說明如何將每個對應項目封裝至清單,並從該清單建立迭代器:

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

取得群組

覆寫 listGroups(),即可從身分存放區擷取所有群組及其成員。listGroups() 方法會接受代表要同步的最後一個身分的查核點。如果程序中斷,檢查點可用於繼續同步處理。針對存放區中的每位使用者,您會在 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 物件的 Iterator,具體來說是 CheckpointCloseableIterable。您可以使用 CheckpointClosableIterableImpl.Builder 類別建構並傳回迭代器。以下程式碼片段說明如何將每個群組和成員封裝至清單,並從該清單建構迭代器:

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

後續步驟

以下是您可以採取的後續步驟: