Segurança no nível da linha do BigQuery com o Advanced Services

Por exemplo, digamos que você esteja tentando criar uma solução para sua organização de vendas, que é dividida em várias regiões, e cada uma tem um gerente regional. O conjunto de dados de vendas no BigQuery separa o valor monetário por região e data.

A solução proposta terá um único painel, onde os gerentes regionais podem visualizar dados de vendas somente das suas regiões.

Requisitos

  • Os visualizadores do painel precisam estar conectados a uma Conta do Google.
  • Precisa haver um mapeamento disponível entre o email do usuário e os dados/linhas aos quais ele tem acesso.
  • Uma conta de serviço deverá ser utilizada para acessar as informações do BigQuery. Assim, o faturamento será centralizado e gerenciado pelo provedor do painel.

Limitações

  • O painel requer uma autorização única de cada visualizador na primeira vez.
  • Os usuários não podem editar o painel nem compartilhar com outras pessoas.
  • Se você é um cliente do Workspace e seu administrador desativou o compartilhamento os arquivos do Drive para "Qualquer pessoa com o link" ou remova o compartilhamento ou desenvolver a solução em uma conta do Gmail.com.

Solução

Realize todas as etapas a seguir para implementar a solução.

Criar um novo conector da comunidade

Para começar, assista ao vídeo Como os conectores da comunidade funcionam (em inglês) e siga as etapas do codelab relacionado. Use a Ferramenta para Desenvolvedores de criação de conectores, tornando o processo de desenvolvimento mais prático.

Gravar o código do conector

  1. getAuthType() precisa retornar NONE.
  2. getConfig() precisa retornar uma configuração vazia.
    • Opcional: se você precisar de entradas específicas para configurar o painel, solicite ao usuário neste campo.
  3. getSchema() precisa retornar o esquema para sua consulta.
    • Opcional: é possível adicionar campos e cálculos personalizados na consulta SQL ou usando campos calculados como parte do esquema.
  4. getData() será preenchido posteriormente.

Atualizar o manifesto

Consulte a referência do manifesto e preencha o manifesto com todas as informações necessárias, incluindo o seguinte:

  1. Defina dataStudio.forceViewersCredentials como true.
  2. Defina dataStudio.advancedServices.data como true.
  3. Para oauthScopes, adicione https://www.googleapis.com/auth/userinfo.email e https://www.googleapis.com/auth/script.external_request. Consulte Escopos de autorização do Apps Script para ver mais informações.
    • Condicional: adicione todos os escopos relevantes para os serviços usados no conector.

O manifesto será semelhante a este:

{
 
...
 
"dataStudio": {
   
"forceViewersCredentials": true,
   
"advancedServices": {
       
"data": true
   
},
   
...
 
}
 
"oauthScopes": [
   
"https://www.googleapis.com/auth/script.external_request",
   
"https://www.googleapis.com/auth/userinfo.email"
   
],
 
...
}

Implementar uma conta de serviço

  1. Crie uma conta de serviço no seu projeto do Google Cloud, que será o projeto de faturamento.
  2. Verifique se essa conta tem acesso ao BigQuery no projeto.
    • Papéis de gerenciamento de identidade e acesso (IAM) obrigatórios: BigQuery Data Viewer, BigQuery Job User
  3. Faça o download do arquivo JSON para usar as chaves das contas de serviço. Armazene as chaves nas propriedades de script do projeto do seu conector.
  4. Inclua a biblioteca OAuth2 para Apps Script no seu projeto do Apps Script.
  5. Implemente o código OAuth2 necessário para a conta de serviço:
    var SERVICE_ACCOUNT_CREDS = 'SERVICE_ACCOUNT_CREDS';
    var SERVICE_ACCOUNT_KEY = 'private_key';
    var SERVICE_ACCOUNT_EMAIL = 'client_email';
    var BILLING_PROJECT_ID = 'project_id';

    /**
     * Copy the entire credentials JSON file from creating a service account in GCP.
     */

    function getServiceAccountCreds() {
     
    return JSON.parse(scriptProperties.getProperty(SERVICE_ACCOUNT_CREDS));
    }

    function getOauthService() {
     
    var serviceAccountCreds = getServiceAccountCreds();
     
    var serviceAccountKey = serviceAccountCreds[SERVICE_ACCOUNT_KEY];
     
    var serviceAccountEmail = serviceAccountCreds[SERVICE_ACCOUNT_EMAIL];

     
    return OAuth2.createService('RowLevelSecurity')
       
    .setAuthorizationBaseUrl('https://accounts.google.com/o/oauth2/auth')
       
    .setTokenUrl('https://accounts.google.com/o/oauth2/token')
       
    .setPrivateKey(serviceAccountKey)
       
    .setIssuer(serviceAccountEmail)
       
    .setPropertyStore(scriptProperties)
       
    .setCache(CacheService.getScriptCache())
       
    .setScope(['https://www.googleapis.com/auth/bigquery.readonly']);
    }

Implemente o getData().

  1. Crie sua consulta do BigQuery.
    • No e-mail, procure o mapeamento entre o e-mail e os dados.
    • Use a cláusula JOIN e/ou WHERE para filtrar as informações.
  2. Descubra o e-mail do usuário efetivo (referência de identidade).
  3. Use os Advanced Services do Looker Studio para retornar a configuração da consulta de getData.
    • Envie a consulta criada, o projeto de faturamento e o token OAuth da conta de serviço.
    • Condicional: se você estiver recebendo dados do usuário pelo conector getConfig, incorpore-os como parâmetros do BigQuery.

Criar o painel

  1. Entenda como as implementações e versões funcionam para os conectores.
  2. Crie uma implantação de produção para o conector.
  3. Use a implantação de produção para criar uma fonte de dados e um novo relatório no Looker Studio.
  4. Adicione todas as tabelas e gráficos ao relatório.
  5. O painel poderá ser compartilhado com seus usuários.

Disponibilizar o painel aos usuários

  1. Compartilhe o script do conector com os usuários selecionados ou "Todos com o link".
  2. Compartilhe o painel com os usuários escolhidos ou "Todos com o link".
  3. Opcional: use um serviço de redução de URL e crie um link curto para o painel. Compartilhe com seus usuários. Isso ajuda a substituir o URL do painel posteriormente, se necessário.
  4. Opcional: analise a utilização do painel configurando o Google Analytics para seu relatório.

Exemplo de código

main.js

var cc = DataStudioApp.createCommunityConnector();
var scriptProperties = PropertiesService.getScriptProperties();

function isAdminUser() {
 
return true;
}

function getAuthType() {
 
var AuthTypes = cc.AuthType;
 
return cc
   
.newAuthTypeResponse()
   
.setAuthType(AuthTypes.NONE)
   
.build();
}

function getConfig(request) {
 
var config = cc.getConfig();

  config
   
.newInfo()
   
.setId('generalInfo')
   
.setText('This is an example connector to showcase row level security.');

 
return config.build();
}

function getFields() {
 
var fields = cc.getFields();
 
var types = cc.FieldType;
 
var aggregations = cc.AggregationType;

  fields
   
.newDimension()
   
.setId('region')
   
.setName('Region')
   
.setType(types.TEXT);

  fields
   
.newMetric()
   
.setId('sales')
   
.setName('Sales')
   
.setType(types.NUMBER)
   
.setAggregation(aggregations.SUM);

  fields
   
.newDimension()
   
.setId('date')
   
.setName('Date')
   
.setType(types.YEAR_MONTH_DAY);

 
return fields;
}

function getSchema(request) {
 
return {schema: getFields().build()};
}

var SERVICE_ACCOUNT_CREDS = 'SERVICE_ACCOUNT_CREDS';
var SERVICE_ACCOUNT_KEY = 'private_key';
var SERVICE_ACCOUNT_EMAIL = 'client_email';
var BILLING_PROJECT_ID = 'project_id';

/**
 * Copy the entire credentials JSON file from creating a service account in GCP.
 */

function getServiceAccountCreds() {
 
return JSON.parse(scriptProperties.getProperty(SERVICE_ACCOUNT_CREDS));
}

function getOauthService() {
 
var serviceAccountCreds = getServiceAccountCreds();
 
var serviceAccountKey = serviceAccountCreds[SERVICE_ACCOUNT_KEY];
 
var serviceAccountEmail = serviceAccountCreds[SERVICE_ACCOUNT_EMAIL];

 
return OAuth2.createService('RowLevelSecurity')
   
.setAuthorizationBaseUrl('https://accounts.google.com/o/oauth2/auth')
   
.setTokenUrl('https://accounts.google.com/o/oauth2/token')
   
.setPrivateKey(serviceAccountKey)
   
.setIssuer(serviceAccountEmail)
   
.setPropertyStore(scriptProperties)
   
.setCache(CacheService.getScriptCache())
   
.setScope(['https://www.googleapis.com/auth/bigquery.readonly']);
}

var BASE_SQL =
 
'SELECT d.region, d.sales, d.date ' +
 
'FROM `datastudio-solutions.row_level_security.data` d ' +
 
'INNER JOIN `datastudio-solutions.row_level_security.access` a ' +
 
'ON d.region = a.region ' +
 
'where a.email=@email';

function getData(request) {
 
var accessToken = getOauthService().getAccessToken();
 
var serviceAccountCreds = getServiceAccountCreds();
 
var billingProjectId = serviceAccountCreds[BILLING_PROJECT_ID];
 
var email = Session.getEffectiveUser().getEmail();

 
var bqTypes = DataStudioApp.createCommunityConnector().BigQueryParameterType;

 
return cc
   
.newBigQueryConfig()
   
.setAccessToken(accessToken)
   
.setBillingProjectId(billingProjectId)
   
.setUseStandardSql(true)
   
.setQuery(BASE_SQL)
   
.addQueryParameter('email', bqTypes.STRING, email)
   
.build();
}