Используйте сгенерированные административные SDK

SDK для администрирования Firebase Data Connect позволяют вызывать запросы и мутации из доверенных сред, таких как Cloud Functions, пользовательские бэкэнды или ваша собственная рабочая станция. Подобно тому, как вы генерируете SDK для своих клиентских приложений, вы можете параллельно создавать собственный SDK для администрирования, разрабатывая схемы, запросы и мутации, которые вы развертываете в своем сервисе Data Connect. Затем вы интегрируете методы из этого SDK в свою бэкэнд-логику или административные скрипты.

Как мы уже упоминали ранее, важно отметить, что запросы и мутации Data Connect не отправляются клиентами в момент запроса. Вместо этого, при развертывании операции Data Connect сохраняются на сервере, как и в случае с Cloud Functions. Это означает, что при каждом развертывании изменений в запросах и мутациях вам также необходимо перегенерировать административные SDK и повторно развернуть все сервисы, которые от них зависят.

Прежде чем начать

Сгенерировать SDK администратора

После создания схем, запросов и мутаций Data Connect вы можете сгенерировать соответствующий административный SDK:

  1. Откройте или создайте файл connector.yaml и добавьте определение adminNodeSdk :

    connectorId: default
    generate:
      adminNodeSdk:
        outputDir: ../../dataconnect-generated/admin-generated
        package: "@dataconnect/admin-generated"
        packageJsonDir: ../..
    

    Файл connector.yaml обычно находится в том же каталоге, что и файлы GraphQL (.gql), содержащие определения запросов и мутаций. Если вы уже сгенерировали клиентские SDK, этот файл уже создан.

  2. Сгенерируйте SDK.

    Если у вас установлено расширение Data Connect для VS Code, оно всегда будет поддерживать сгенерированные SDK в актуальном состоянии.

    В противном случае используйте Firebase CLI:

    firebase dataconnect:sdk:generate

    Или же, чтобы автоматически перегенерировать SDK при обновлении файлов gql :

    firebase dataconnect:sdk:generate --watch

Выполнение операций из административного SDK.

Сгенерированный административный SDK содержит интерфейсы и функции, соответствующие вашим определениям gql , которые вы можете использовать для выполнения операций с вашей базой данных. Например, предположим, вы сгенерировали SDK для базы данных песен вместе с запросом getSongs :

import { initializeApp } from "firebase-admin/app";
import { getSongs } from "@dataconnect/admin-generated";

const adminApp = initializeApp();

const songs = await getSongs(
  { limit: 4 },
  { impersonate: { unauthenticated: true } }
);

Или, чтобы указать конфигурацию разъема:

import { initializeApp } from "firebase-admin/app";
import { getDataConnect } from "firebase-admin/data-connect";
import {
  connectorConfig,
  getSongs,
} from "@dataconnect/admin-generated";

const adminApp = initializeApp();
const adminDc = getDataConnect(connectorConfig);

const songs = await getSongs(
  adminDc,
  { limit: 4 },
  { impersonate: { unauthenticated: true } }
);

Выдача себя за неавторизованного пользователя

SDK администратора предназначены для запуска в доверенных средах и, следовательно, имеют неограниченный доступ к вашим базам данных.

При выполнении общедоступных операций с помощью SDK администратора следует избегать выполнения операции с полными правами администратора (следуя принципу минимальных привилегий). Вместо этого следует выполнять операцию либо от имени пользователя, имитирующего пользователя (см. следующий раздел), либо от имени неаутентифицированного пользователя, имитирующего пользователя. Неаутентифицированные пользователи могут выполнять только операции, помеченные как PUBLIC .

В приведенном выше примере запрос getSongs выполняется от имени неаутентифицированного пользователя.

Выдача себя за пользователя

Вы также можете выполнять операции от имени конкретных пользователей, передавая часть или весь токен Firebase Authentication в параметре impersonate ; как минимум, необходимо указать идентификатор пользователя в утверждении sub. (Это то же значение, что и значение сервера auth.uid , на которое можно ссылаться в операциях GraphQL Data Connect.)

При имитации пользователя операция будет успешной только в том случае, если предоставленные вами данные пользователя пройдут проверки аутентификации, указанные в вашем определении GraphQL.

Если вы вызываете сгенерированный SDK из общедоступной конечной точки, крайне важно, чтобы эта конечная точка требовала аутентификации, и чтобы вы проверяли целостность токена аутентификации, прежде чем использовать его для имитации пользователя.

При использовании вызываемых Cloud Functions токен аутентификации проверяется автоматически, и вы можете использовать его, как показано в следующем примере:

import { HttpsError, onCall } from "firebase-functions/https";

export const callableExample = onCall(async (req) => {
    const authClaims = req.auth?.token;
    if (!authClaims) {
        throw new HttpsError("unauthenticated", "Unauthorized");
    }

    const favoriteSongs = await getMyFavoriteSongs(
        undefined,
        { impersonate: { authClaims } }
    );

    // ...
});

В противном случае используйте метод verifyIdToken из Admin SDK для проверки и декодирования токена аутентификации. Например, предположим, что ваша конечная точка реализована как обычная HTTP-функция, и вы передали токен Firebase Authentication в вашу конечную точку, используя заголовок authorization , как это принято:

import { getAuth } from "firebase-admin/auth";
import { onRequest } from "firebase-functions/https";

const auth = getAuth();

export const httpExample = onRequest(async (req, res) => {
    const token = req.header("authorization")?.replace(/^bearer\s+/i, "");
    if (!token) {
        res.sendStatus(401);
        return;
    }
    let authClaims;
    try {
        authClaims = await auth.verifyIdToken(token);
    } catch {
        res.sendStatus(401);
        return;
    }

    const favoriteSongs = await getMyFavoriteSongs(
        undefined,
        { impersonate: { authClaims } }
    );

    // ...
});

Указывать идентификатор пользователя, не полученный из проверяемого источника, следует только при выполнении действительно административных задач, таких как миграция данных, из защищенной, недоступной для публичного доступа среды:

// Never do this if end users can initiate execution of the code!
const favoriteSongs = await getMyFavoriteSongs(
  undefined,
  { impersonate: { authClaims } }
);

Запуск с неограниченным доступом

Если вы выполняете операцию, требующую прав администратора, опустите параметр impersonate из вызова:

await upsertSong(adminDc, {
  title: songTitle_one,
  instrumentsUsed: [Instrument.VOCAL],
});

Операция, вызываемая таким образом, имеет полный доступ к базе данных. Если у вас есть запросы или мутации, предназначенные только для административных целей, их следует определять с помощью директивы @auth(level: NO_ACCESS) . Это гарантирует, что только пользователи с правами администратора смогут выполнять эти операции.