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

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

Как мы уже упоминали ранее, важно отметить, что запросы и мутации Data Connect не отправляются клиентами в момент запроса. Вместо этого, при развёртывании, операции Data Connect сохраняются на сервере, как облачные функции. Это означает, что при каждом развёртывании изменений в запросах и мутациях вам также необходимо повторно генерировать административные 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 } }
);

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

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

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

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

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

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

Если вы выдаете себя за пользователя, операция завершится успешно только в том случае, если предоставленные вами данные пользователя пройдут проверку подлинности, указанную в вашем определении 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) . Это гарантирует, что выполнять эти операции смогут только вызывающие пользователи с правами администратора.