Criar um conector de identidade

Por padrão, o Google Cloud Search reconhece apenas identidades do Google armazenadas no Google Cloud Directory (usuários e grupos). Os conectores de identidade são usados para sincronizar as identidades da empresa com as identidades do Google usadas pelo Google Cloud Search.

O Google oferece as seguintes opções para desenvolver conectores de identidade:

  • O SDK do Identity Connector. Essa opção é para desenvolvedores que usam a linguagem de programação Java. O SDK do Identity Connector é um wrapper na API REST que permite criar conectores rapidamente. Para criar um conector de identidade com o SDK, consulte Criar um conector de identidade usando o SDK do Identity Connector.

  • Uma API REST de baixo nível e bibliotecas de API. Estas opções são para desenvolvedores que podem não estar programando em Java ou têm uma base de código que comporta melhor uma API REST ou uma biblioteca. Para criar um conector de identidade usando a API REST, consulte API Directory: contas de usuários para informações sobre mapeamento de usuários e Documentação do Cloud Identity para informações sobre mapeamento de grupos.

Criar um conector de identidade usando o SDK do Identity Connector

Um conector de identidade típico desempenha as seguintes tarefas:

  1. Configura o conector.
  2. Recupera todos os usuários do sistema de identidade empresarial e os envia ao Google para sincronização com as identidades do Google.
  3. Recupera todos os grupos do sistema de identidade empresarial e os envia ao Google para sincronização com as identidades do Google.

Configurar dependências

Você precisa incluir certas dependências no seu arquivo de build para usar o SDK. Clique em uma guia abaixo para conferir as dependências do ambiente de 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'

Criar a configuração do conector

Cada conector tem um arquivo de configuração que contém os parâmetros usados pelo conector, como o código do repositório. Eles são definidos como pares de chave-valor, como api.sourceId=1234567890abcdef.

O SDK do Google Cloud Search contém vários parâmetros de configuração fornecidos pelo Google que são usados por todos os conectores. É necessário declarar os parâmetros fornecidos pelo Google a seguir no arquivo de configuração:

  • No caso dos conectores de conteúdo, declare api.sourceId e api.serviceAccountPrivateKeyFile, já que esses parâmetros identificam o local do repositório e a chave privada necessária para acessá-lo.
  • No caso dos conectores de identidade, declare api.identitySourceId porque esse parâmetro identifica o local da origem de identidade externa. Se você estiver sincronizando usuários, também precisará declarar api.customerId como o ID exclusivo da conta do Google Workspace da sua empresa.

A menos que você queira modificar os valores padrão dos outros parâmetros fornecidos pelo Google, não é necessário declará-los no arquivo de configuração. Para mais informações sobre os parâmetros de configuração fornecidos pelo Google, por exemplo, como gerar determinados IDs e chaves, consulte Parâmetros de configuração fornecidos pelo Google.

Também é possível definir parâmetros específicos do repositório para usá-los no seu arquivo de configuração.

Transmitir o arquivo de configuração para o conector

Defina a propriedade do sistema config para transmitir o arquivo de configuração para o conector. É possível definir a propriedade usando o argumento -D ao iniciar o conector. Por exemplo, o comando a seguir inicia o conector com o arquivo de configuração MyConfig.properties:

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

Se esse argumento estiver ausente, o SDK vai tentar acessar um arquivo de configuração padrão chamado connector-config.properties.

Criar um conector de identidade de sincronização completo usando uma classe de modelo

O SDK do Identity Connector contém uma classe de modelo FullSyncIdentityConnector que pode ser usada para sincronizar todos os usuários e grupos do repositório de identidades com as identidades do Google. Esta seção explica como usar o modelo FullSyncIdentityConnector para executar uma sincronização completa de usuários e grupos em um repositório de identidade que não seja do Google.

Nesta seção da documentação, são usados snippets de código do exemplo IdentityConnecorSample.java. Essa amostra lê as identidades de usuários e grupos de dois arquivos CSV e as sincroniza com as identidades do Google.

Implementar o ponto de entrada do conector

O ponto de entrada de um conector é o método main(). A principal tarefa desse método é criar uma instância da classe Application e invocar o método start() para executar o conector.

Antes de chamar application.start(), use a classe IdentityApplication.Builder para instanciar o modelo FullSyncIdentityConnector. O FullSyncIdentityConnector aceita um objeto Repository que tenha métodos para implementar. O snippet de código abaixo mostra como implementar o método 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();
}

Em segundo plano, o SDK chama o método initConfig() depois que o método main() do conector chama Application.build. O método initConfig() executa as seguintes tarefas:

  1. Chama o método Configuation.isInitialized() para garantir que o Configuration não tenha sido inicializado.
  2. Inicializa um objeto Configuration com os pares de chave-valor fornecidos pelo Google. Cada par de chave-valor é armazenado em um objeto ConfigValue no objeto Configuration.

Implementar a interface Repository

O único objetivo do objeto Repository é executar a sincronização de identidades de repositório com as identidades do Google. Ao usar um modelo, você precisa modificar apenas determinados métodos na interface Repository para criar um conector de identidade. No caso de FullTraversalConnector, você provavelmente vai substituir os seguintes métodos:

  • O método init(). Para executar qualquer configuração e inicialização do repositório de identidades, modifique o método init().

  • O método listUsers(). Para sincronizar todos os usuários no repositório de identidades com usuários do Google, modifique o método listUsers().

  • O método listGroups(). Para sincronizar todos os grupos no repositório de identidades com os Grupos do Google, modifique o método listGroups().

  • (opcional) O método close(). Se você precisar realizar a limpeza do repositório, modifique o método close(). Esse método é chamado uma vez durante o encerramento do conector.

Receber parâmetros de configuração personalizados

Como parte da manipulação da configuração do conector, é necessário receber todos os parâmetros personalizados do objeto Configuration. Essa tarefa geralmente é realizada em um método init() da classe Repository.

A classe Configuration tem vários métodos para receber diferentes tipos de dados de uma configuração. Cada método retorna um objeto ConfigValue. Em seguida, use o método get() do objeto ConfigValue para extrair o valor real. O snippet a seguir mostra como recuperar os valores de userMappingCsvPath e groupMappingCsvPath de um objeto Configuration:

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

Para receber e analisar um parâmetro que contém vários valores, use um dos analisadores de tipo da classe Configuration para analisar os dados em blocos distintos. O snippet a seguir, do conector do tutorial, usa o método getMultiValue para receber uma lista de nomes de repositório do GitHub:

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

Receber o mapeamento de todos os usuários

Modifique listUsers() para recuperar o mapeamento de todos os usuários do repositório de identidade. O método listUsers() aceita um checkpoint que representa a última identidade a ser sincronizada. O checkpoint poderá ser usado para retomar a sincronização caso o processo seja interrompido. Realize estas etapas no método listUsers() para cada usuário no repositório:

  1. Receba um mapeamento que consiste na identidade do Google e na identidade externa associada.
  2. Empacote o par em um iterador retornado pelo método listUsers().

Receber um mapeamento de usuários

Veja no snippet de código a seguir como recuperar os mapeamentos de identidade armazenados em um arquivo 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);
    }
  }
  // ...
}

Empacotar um mapeamento de usuários em um iterador

O método listUsers() retorna um Iterator, especificamente um CheckpointCloseableIterable, de objetos IdentityUser. Você pode usar a classe CheckpointClosableIterableImpl.Builder para criar e retornar um iterador. Veja no snippet de código a seguir como empacotar cada mapeamento na lista e criar o iterador a partir dela:

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

Receber um grupo

Modifique listGroups() para recuperar todos os grupos e os respectivos membros do repositório de identidades. O método listGroups() aceita um checkpoint que representa a última identidade a ser sincronizada. O checkpoint poderá ser usado para retomar a sincronização caso o processo seja interrompido. Realize estas etapas no método listGroups() para cada usuário no repositório:

  1. Receba o grupo e seus membros.
  2. Empacote cada grupo e seus membros em um iterador retornado pelo método listGroups().

Receber a identidade do grupo

Veja no snippet de código abaixo como recuperar os grupos e seus membros armazenados em um arquivo 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);
    }
  }
  // ...

}

Empacotar o grupo e respectivos membros em um iterador

O método listGroups() retorna um Iterator, especificamente um CheckpointCloseableIterable, de objetos IdentityGroup. Você pode usar a classe CheckpointClosableIterableImpl.Builder para criar e retornar um iterador. Veja no snippet de código a seguir como empacotar cada grupo e seus membros em uma lista e criar o iterador a partir dela:

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

A seguir

Veja a seguir algumas das próximas etapas: