Firebase Admin SDK – это набор серверных библиотек, который позволяет взаимодействовать с Firebase из сред с повышенными привилегиями, например выполнять запросы и изменения в сервисе Firebase SQL Connect для массового управления данными и другие операции с повышенными привилегиями и олицетворенными учетными данными.
Admin SDK предоставляет API для вызова операций в режимах чтения/записи и только для чтения. Операции только для чтения позволяют выполнять административные функции, не изменяя данные в базах данных.
Настройка Admin SDK
Чтобы начать использовать Firebase SQL Connect на сервере, сначала необходимо установить и настроить Admin SDK для Node.js.
Инициализируйте Admin SDK в скриптах
Чтобы инициализировать SDK, импортируйте расширения SQL Connect и укажите идентификатор сервиса и местоположение проекта.
import { initializeApp } from 'firebase-admin/app';
import { getDataConnect } from 'firebase-admin/data-connect';
// If you'd like to use OAuth2 flows and other credentials to log in,
// visit https://firebase.google.com/docs/admin/setup#initialize-sdk
// for alternative ways to initialize the SDK.
const app = initializeApp();
const dataConnect = getDataConnect({
serviceId: 'serviceId',
location: 'us-west2'
});
Создание запросов и мутаций для использования с Admin SDK
Admin SDK полезен для выполнения операций SQL Connect с учетом следующих соображений.
Как работает директива @auth(level: NO_ACCESS) в SDK
Поскольку Admin SDK работает с привилегиями, он может выполнять любые ваши запросы и изменения независимо от уровней доступа, заданных с помощью директив @auth, включая уровень NO_ACCESS.
Если наряду с клиентскими операциями вы организуете административные запросы и изменения в исходных файлах .gql для импорта в административные скрипты, Firebase рекомендует помечать административные операции без какого-либо уровня доступа для авторизации или, возможно, быть более явными и устанавливать для них значение NO_ACCESS. В любом случае это предотвращает выполнение таких операций клиентами или в других непривилегированных контекстах.
Как использовать SDK с эмулятором SQL Connect
В прототипах и тестовых средах может быть полезно выполнять заполнение данных и другие операции с локальными данными. Admin SDK позволяет упростить рабочие процессы, поскольку может игнорировать аутентификацию и авторизацию для локальных потоков. Вы также можете явно включить соблюдение настроек аутентификации и авторизации для операций с входом под чужим именем.
Firebase Admin SDK автоматически подключается к эмулятору SQL Connect, если задана переменная среды DATA_CONNECT_EMULATOR_HOST:
export DATA_CONNECT_EMULATOR_HOST="127.0.0.1:9399"
Статьи по теме
- Руководство по заполнению базы данных при локальной разработке
- Документация по эмулятору SQL Connect.
Выполнение административных операций
Admin SDK предоставляется для привилегированных операций с критически важными данными.
Admin SDK предоставляет три набора API:
- Сгенерированные Admin SDK – это типобезопасные SDK, созданные на основе определений
gqlтак же, как и клиентские SDK. - Общий интерфейс для выполнения произвольных операций GraphQL, в котором ваш код реализует запросы и изменения и передает их методу
executeGraphql(чтение и запись) или методуexecuteGraphqlRead(только чтение). - Специализированный интерфейс для массовых операций с данными, который вместо общих методов
executeGraphqlпредоставляет специальные методы для операций изменения:insert,insertMany,upsertиupsertMany.
Управление данными с помощью сгенерированных SDK
Создание SDK администратора на основе определений gql выполняется так же, как и создание клиентских SDK.
Сгенерированный Admin 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 предназначены для запуска из доверенных сред и поэтому имеют неограниченный доступ к вашим базам данных.
При выполнении общедоступных операций с помощью Admin SDK не следует использовать полные права администратора (в соответствии с принципом минимальных привилегий). Вместо этого вам следует выполнить операцию от имени пользователя (см. следующий раздел) или от имени неаутентифицированного пользователя.
Неаутентифицированные пользователи могут выполнять только операции, помеченные как PUBLIC.
В примере выше запрос getSongs выполняется от имени неаутентифицированного пользователя.
Выдача себя за другое лицо
Вы также можете выполнять операции от имени определенных пользователей, передавая часть или весь токен Firebase Authentication в параметре impersonate. Как минимум, вы должны указать идентификатор пользователя в утверждении sub. Это значение совпадает со значением сервера auth.uid, на которое можно ссылаться в операциях SQL 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). Это гарантирует, что только вызывающие абоненты с правами администратора смогут выполнять эти операции.
Управление данными с помощью методов executeGraphql
Если вам нужно выполнить разовые операции, для которых не определены изменения или запросы gql, вы можете использовать метод executeGraphql или метод executeGraphqlRead, предназначенный только для чтения.
Выполнение пользовательских вложенных операций отношения (один ко многим и один к одному) с помощью executeGraphql.
При вызове операций из Admin SDK в привилегированной среде вам не нужно указывать директиву @allow для переменных при стандартных плоских (невложенных) вставках (например, при вставке отдельных записей Movie или Actor).
Однако для вложенных реляционных вставок (например, вставка фильма вместе с его первыми рецензиями или пользователя и его профиля) вам все равно необходимо объявить директиву @allow в определении переменной, чтобы статически разрешить вложенные реляционные поля внутри скомпилированной схемы переменной.
// Custom GraphQL mutation with nested relational insert (one-to-many)
const customMutation = `
mutation CustomBatchInsert(
$data: [Movie_Data!]!
@allow(fields: "title genre reviews_on_movie { rating reviewText }")
) {
movie_insertMany(data: $data)
}
`;
const variables = {
data: [
{
title: "Interstellar",
genre: "Sci-Fi",
reviews_on_movie: [
{
rating: 5,
reviewText: "Visually stunning and emotionally powerful.",
user: { id: "user-789" }
}
]
}
]
};
const response = await dataConnect.executeGraphql(customMutation, { variables });
// Custom GraphQL mutation with a one-to-one nested relational insert
const customMutationOneToOne = `
mutation CustomBatchInsertOneToOne(
$data: [User_Data!]!
@allow(fields: "name profile_on_user { bio }")
) {
user_insertMany(data: $data)
}
`;
const variablesOneToOne = {
data: [
{
name: "Alice",
profile_on_user: {
bio: "Avid moviegoer and critic."
}
}
]
};
const responseOneToOne = await dataConnect.executeGraphql(customMutationOneToOne, { variables: variablesOneToOne });
Выдавать себя за неаутентифицированного пользователя
При выполнении общедоступных операций с помощью Admin SDK не следует использовать полные права администратора (в соответствии с принципом минимальных привилегий). Вместо этого вам следует выполнить операцию от имени пользователя (см. следующий раздел) или от имени неаутентифицированного пользователя. Неаутентифицированные пользователи могут выполнять только операции, помеченные как PUBLIC.
// Query to get posts, with authentication level PUBLIC
const queryGetPostsImpersonation = `
query getPosts @auth(level: PUBLIC) {
posts {
description
}
}`;
// Attempt to access data as an unauthenticated user
const optionsUnauthenticated: GraphqlOptions<undefined> = {
impersonate: {
unauthenticated: true
}
};
// executeGraphql with impersonated unauthenticated user scope
const gqlResponse = await dataConnect.executeGraphql<UserData, undefined>(queryGetPostsImpersonation, optionsUnauthenticated);
Выдача себя за другое лицо
В некоторых случаях скрипты должны изменять пользовательские данные на основе ограниченных учетных данных от имени определенного пользователя. Этот подход соответствует принципу наименьших привилегий.
Чтобы использовать этот интерфейс, соберите информацию из специального токена аутентификации JWT, который соответствует формату токена Authentication. Также ознакомьтесь с руководством по специальным токенам.
// Get the current user's data
const queryGetUserImpersonation = `
query getUser @auth(level: USER) {
user(key: {uid_expr: "auth.uid"}) {
id,
name
}
}`;
// Impersonate a user with the specified auth claims
const optionsAuthenticated: GraphqlOptions<undefined> = {
impersonate: {
authClaims: {
sub: 'QVBJcy5ndXJ1'
}
}
};
// executeGraphql with impersonated authenticated user scope
const gqlResponse = await dataConnect.executeGraphql<UserData, undefined>(queryGetUserImpersonation, optionsAuthenticated);
// gqlResponse -> { "data": { "user": { "id": "QVBJcy5ndXJ1", "name": "Fred" } } }
Использовать учетные данные администратора
Если вы выполняете операцию, для которой требуются права администратора, не указывайте в вызове параметр impersonate:
// User can be publicly accessible, or restricted to admins
const query = "query getProfile(id: AuthID) { user(id: $id) { id name } }";
interface UserData {
user: {
id: string;
name: string;
};
}
export interface UserVariables {
id: string;
}
const options:GraphqlOptions<UserVariables> = { variables: { id: "QVBJcy5ndXJ1" } };
// executeGraphql
const gqlResponse = await dataConnect.executeGraphql<UserData, UserVariables>(query, options);
// executeGraphqlRead (similar to previous sample but only for read operations)
const gqlResponse = await dataConnect.executeGraphqlRead<UserData, UserVariables>(query, options);
// gqlResponse -> { "data": { "user": { "id": "QVBJcy5ndXJ1", "name": "Fred" } } }
Операция, вызванная таким образом, имеет полный доступ к базе данных. Если у вас есть запросы или мутации, предназначенные только для административных целей, вы должны определить их с помощью директивы @auth(level: NO_ACCESS). Это гарантирует, что только вызывающие абоненты с правами администратора смогут выполнять эти операции.
Выполнять массовую вставку данных.
Для массовых операций с данными в рабочих базах данных Firebase рекомендует использовать Admin SDK.
SDK предоставляет следующие методы для работы с массовыми данными. На основе предоставленных аргументов каждый метод создает и выполняет мутацию GraphQL.
// Methods of the bulk operations API
// dc is a SQL Connect admin instance from getDataConnect
const resp = await dc.insert("movie" /*table name*/, data[0]);
const resp = await dc.insertMany("movie" /*table name*/, data);
const resp = await dc.upsert("movie" /*table name*/, data[0]);
const resp = await dc.upsertMany("movie" /*table name*/, data);
Вложенные реляционные операции (один ко многим и один к одному)
Admin SDK поддерживает реляционные данные в массовых операциях. Как и в случае с пользовательскими изменениями, используйте массив объектов для отношений "один ко многим" и один объект для отношений "один к одному":
// Example of inserting a movie with nested reviews (one-to-many)
const moviesData = [
{
title: "Interstellar",
genre: "Sci-Fi",
reviews_on_movie: [
{
rating: 5,
reviewText: "Visually stunning and emotionally powerful.",
user: { id: "user-789" }
}
]
}
];
// Atomically insert movies and their reviews in a single database round-trip.
const response = await dc.insertMany("movie", moviesData);
// Example of inserting a user with a nested profile (one-to-one)
const usersData = [
{
name: "Alice",
profile_on_user: {
bio: "Avid moviegoer and critic."
}
}
];
// Atomically insert users and their profiles.
const userResponse = await dc.insertMany("user", usersData);
Примечания по эффективности массовых операций
Каждый запрос к серверной части будет вызывать один цикл обмена данными с Cloud SQL, поэтому чем больше пакет, тем выше пропускная способность.
Однако чем больше размер пакета, тем длиннее сгенерированная инструкция SQL. При достижении лимита длины инструкции SQL PostgreSQL возникнет ошибка.
На практике поэкспериментируйте, чтобы найти подходящий размер пакета для вашей рабочей нагрузки.
Что дальше?
- Подробнее о том, как заполнять базы данных с помощью Admin SDK…
- Ознакомьтесь с API для Admin SDK.
- Используйте CLI Firebase и консоль Google Cloud для других операций по управлению проектами, например для управления схемами и коннекторами и управления сервисами и базами данных.