Sử dụng SDK dành cho quản trị viên đã tạo

Firebase Data Connect admin SDK cho phép bạn gọi các truy vấn và đột biến từ các môi trường đáng tin cậy như Cloud Functions, các phần phụ trợ tuỳ chỉnh hoặc máy trạm của riêng bạn. Tương tự như cách tạo SDK cho các ứng dụng khách, bạn có thể tạo một SDK quản trị tuỳ chỉnh song song khi thiết kế các giản đồ, truy vấn và đột biến mà bạn triển khai cho dịch vụ Data Connect. Sau đó, bạn tích hợp các phương thức từ SDK này vào logic phụ trợ hoặc tập lệnh quản trị.

Như chúng tôi đã đề cập ở nơi khác, điều quan trọng cần lưu ý là các truy vấn và đột biến Data Connect không được khách hàng gửi tại thời điểm yêu cầu. Thay vào đó, khi được triển khai, các thao tác Data Connect sẽ được lưu trữ trên máy chủ như Cloud Functions. Điều này có nghĩa là bất cứ khi nào triển khai các thay đổi đối với truy vấn và đột biến, bạn cũng cần tạo lại Admin SDK và triển khai lại mọi dịch vụ dựa vào các thay đổi đó.

Trước khi bắt đầu

Tạo SDK dành cho quản trị viên

Sau khi tạo các giản đồ, truy vấn và đột biến Data Connect, bạn có thể tạo một SDK quản trị tương ứng:

  1. Mở hoặc tạo tệp connector.yaml rồi thêm một định nghĩa adminNodeSdk:

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

    Tệp connector.yaml thường nằm trong cùng thư mục với các tệp GraphQL (.gql) chứa định nghĩa truy vấn và đột biến của bạn. Nếu bạn đã tạo SDK ứng dụng khách, thì tệp này đã được tạo.

  2. Tạo SDK.

    Nếu bạn đã cài đặt tiện ích Data Connect VS Code, thì tiện ích này sẽ luôn cập nhật các SDK đã tạo.

    Nếu không, hãy sử dụng Giao diện dòng lệnh (CLI) của Firebase:

    firebase dataconnect:sdk:generate

    Hoặc để tự động tạo lại SDK khi bạn cập nhật tệp gql:

    firebase dataconnect:sdk:generate --watch

Thực thi các thao tác từ SDK quản trị

SDK quản trị được tạo chứa các giao diện và hàm tương ứng với định nghĩa gql của bạn. Bạn có thể dùng các giao diện và hàm này để thực hiện các thao tác trên cơ sở dữ liệu. Ví dụ: giả sử bạn đã tạo một SDK cho cơ sở dữ liệu bài hát, cùng với một truy vấn, 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 } }
);

Hoặc để chỉ định cấu hình trình kết nối:

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

Nhập vai người dùng chưa được xác thực

Admin SDK được thiết kế để chạy trong các môi trường đáng tin cậy, do đó có quyền truy cập không hạn chế vào cơ sở dữ liệu của bạn.

Khi chạy các thao tác công khai bằng SDK dành cho quản trị viên, bạn nên tránh chạy thao tác có đầy đủ đặc quyền của quản trị viên (tuân theo nguyên tắc về đặc quyền tối thiểu). Thay vào đó, bạn nên chạy thao tác này với tư cách là người dùng được mạo danh (xem phần tiếp theo) hoặc với tư cách là người dùng chưa xác thực được mạo danh. Người dùng chưa xác thực chỉ có thể chạy các thao tác được đánh dấu là PUBLIC.

Trong ví dụ trên, truy vấn getSongs được thực thi dưới dạng người dùng chưa xác thực.

Nhập vai người dùng

Bạn cũng có thể thực hiện các thao tác thay cho một số người dùng cụ thể bằng cách truyền một phần hoặc toàn bộ mã thông báo Firebase Authentication trong lựa chọn impersonate; tối thiểu, bạn phải chỉ định mã nhận dạng người dùng của người dùng trong yêu cầu phụ. (Đây là giá trị giống với giá trị máy chủ auth.uid mà bạn có thể tham chiếu trong các thao tác Data Connect GraphQL.)

Khi bạn mạo danh một người dùng, thao tác sẽ chỉ thành công nếu dữ liệu người dùng mà bạn cung cấp vượt qua các bước kiểm tra xác thực được chỉ định trong định nghĩa GraphQL.

Nếu bạn đang gọi SDK đã tạo từ một điểm cuối có thể truy cập công khai, thì điều quan trọng là điểm cuối đó phải yêu cầu xác thực và bạn phải xác thực tính toàn vẹn của mã thông báo xác thực trước khi sử dụng mã thông báo đó để mạo danh người dùng.

Khi sử dụng Cloud Functions có thể gọi, mã thông báo xác thực sẽ tự động được xác minh và bạn có thể sử dụng mã thông báo đó như trong ví dụ sau:

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

    // ...
});

Nếu không, hãy sử dụng phương thức verifyIdToken của Admin SDK để xác thực và giải mã mã thông báo xác thực. Ví dụ: giả sử điểm cuối của bạn được triển khai dưới dạng một hàm HTTP đơn giản và bạn đã truyền mã thông báo Firebase Authentication đến điểm cuối bằng cách sử dụng tiêu đề authorization, theo tiêu chuẩn:

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

    // ...
});

Bạn chỉ nên chỉ định mã nhận dạng người dùng không bắt nguồn từ một nguồn có thể xác minh khi thực hiện các tác vụ quản trị thực sự, chẳng hạn như di chuyển dữ liệu, từ một môi trường an toàn mà không thể truy cập công khai:

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

Chạy với quyền truy cập không hạn chế

Nếu bạn đang thực hiện một thao tác yêu cầu quyền ở cấp quản trị viên, hãy bỏ qua tham số impersonate trong lệnh gọi:

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

Một thao tác được gọi theo cách này sẽ có toàn quyền truy cập vào cơ sở dữ liệu. Nếu chỉ có các truy vấn hoặc đột biến được dùng cho mục đích quản trị, bạn nên xác định chúng bằng chỉ thị @auth(level: NO_ACCESS). Việc này đảm bảo rằng chỉ những người gọi ở cấp quản trị mới có thể thực thi các thao tác này.