データアクセスの保護
Squidが提供するさまざまなデコレータを使用して、Squidに接続されている(内蔵の内部データベースを含む)すべてのデータベースを保護します。
各デコレータは、データベースの異なる部分を保護するように設計されています。
特定の種類の操作を保護するには、関数に異なるcontextオブジェクトが渡されます。たとえば、read操作を保護する場合、contextオブジェクトはQueryContext型となり、write操作の場合はMutationContextオブジェクトが使用されます。
適切なデコレータを使用して、次のいずれかのタイプのデータアクセスを保護できます:
readinsertupdatedeletewriteall
なお、writeデコレータはinsert、update、およびdelete操作も含むため、データベース上のすべての種類の書き込み操作に対して包括的な保護を提供します。
@secureDatabase
Squidが提供する@secureDatabaseデコレータは、アクセスされるテーブルやコレクションに関係なくすべてのデータベースアクセスを保護するために使用できます。このデコレータは、データベースにアクセスするすべての操作に対して認可チェックを強制します。
たとえば、開発者は@secureDatabaseデコレータを使用して、認証されたユーザーのみがデータベースにアクセスできるようにすることができます。そのために、ユーザーが認証されているかどうかを確認する関数を定義します:
import { secureDatabase, SquidService } from '@squidcloud/backend';
export class ExampleService extends SquidService {
@secureDatabase('all', 'usersDatabase')
verifyUserAuthenticated(): boolean {
return this.isAuthenticated();
}
}
usersDatabaseを変更できるのはadminプロパティを持つユーザーのみにするために、開発者はユーザーが認証され、かつadminプロパティを持っているかをチェックするverifyUserAuthenticated関数と組み合わせて@secureDatabaseデコレータを使用できます:
import { secureDatabase, SquidService, MutationContext } from '@squidcloud/backend';
export class ExampleService extends SquidService {
@secureDatabase('write', 'usersDatabase')
verifyUserAuthenticated(context: MutationContext): boolean {
const userAuth = this.getUserAuth();
if (!userAuth) return false;
return !!userAuth.attributes['admin'];
}
}
場合によっては、クライアントが認可されているかどうかを判断するために、操作のcontextにアクセスする必要があります。たとえば、以下の関数は、ユーザーが認証されているか、そして試みている書き込みがinsert(updateではなく)であるかをチェックします:
import { secureDatabase, SquidService, MutationContext } from '@squidcloud/backend';
export class ExampleService extends SquidService {
@secureDatabase('write', 'usersDatabase')
allowOnlyInsertsAndAuthenticated(context: MutationContext): boolean {
return this.isAuthenticated() && context.getMutationType() === 'insert';
}
}
@secureCollection
開発者は@secureCollectionデコレータを使用して、データベース内の特定のコレクションを保護できます。これにより、認可されたユーザーのみが特定のコレクション内のデータにアクセスおよび変更できるようになります。
たとえば、開発者は@secureCollectionデコレータを使用して、ユーザーが自分のuserIdがownerカラムに含まれるドキュメントのみを読み取れるようにすることができます。以下はその例です:
import { secureCollection, SquidService, QueryContext } from '@squidcloud/backend';
export class ExampleService extends SquidService {
@secureCollection('Items', 'read')
secureItemsRead(context: QueryContext<Item>): boolean {
const userId = this.getUserAuth()?.userId;
if (!userId) return false;
return context.isSubqueryOf('owner', '==', userId);
}
}
また、ユーザーが所有するItemsのみを更新、削除、または挿入できるようにすることも考慮できます:
import { secureCollection, SquidService, MutationContext } from '@squidcloud/backend';
export class ExampleService extends SquidService {
@secureCollection('Items', 'write')
secureItemWrite(context: MutationContext<Item>): boolean {
const userId = this.getUserAuth()?.userId;
if (!userId) return false;
const { before, after } = context.beforeAndAfterDocs;
if (before && before.owner !== userId) return false;
if (after && after.owner !== userId) return false;
return true;
}
}
ネイティブクエリの保護
native queryを保護するには、データベースのintegration IDを渡して@secureNativeQuery()デコレータを使用します。以下の例は、ユーザーが認証されているかどうかをチェックする関数を定義しています:
import { secureNativeQuery, SquidService } from '@squidcloud/backend';
export class ExampleService extends SquidService {
@secureNativeQuery('YOUR_INTEGRATION_ID')
verifyUserAuthenticated(): boolean {
return this.isAuthenticated();
}
}
この例のように、auth token属性を使用してより細かいアクセス制御を追加することもできます:
import { secureNativeQuery, SquidService } from '@squidcloud/backend';
export class ExampleService extends SquidService {
@secureNativeQuery('YOUR_INTEGRATION_ID')
verifyAdminUser(): boolean {
// Get the authenticated user's details
const userAuth = this.getUserAuth();
if (!userAuth) {
return false;
}
// Check for the admin attribute
return !!userAuth.attributes['admin'];
}
}
また、auth permissionsを保存するためにコレクションを使用することもできます。コレクションがSecurity Service functionで保護されていることを確認してください。
import { secureNativeQuery, SquidService } from '@squidcloud/backend';
export class ExampleService extends SquidService {
@secureNativeQuery('YOUR_INTEGRATION_ID')
verifyNativeQueryAccess(): Promise<boolean> {
// Get the authenticated user's ID
const userId = this.getUserAuth()?.userId;
if (!userId) {
return false;
}
// Check if the user's ID is in the collection listing users permitted to access native query
const userTableAccess = await this.squid.collection('table_access').doc(userId).snapshot();
return !!userTableAccess;
}
}
ネイティブクエリのコンテキスト
ネイティブのリレーショナルまたはMongoDBクエリを実行する際、Squidバックエンドでは、クライアントが実行したいネイティブクエリの種類('relational'または'mongo')や、その他のクエリの種類に基づく属性を示すコンテキストが利用可能です。
ネイティブリレーショナルクエリを実行する際に提供されるコンテキストの型は次のとおりです:
RelationalNativeQueryContext {
type: 'relational';
query: string;
params: Record<string, any>;
}
このコンテキストを使用して、クライアントが実行できるネイティブクエリの種類を制限します。たとえば、以下のコードは、クライアントがSQUIDSというコレクション(またはテーブルの行)から、YEARフィールドの値が1980より大きいドキュメントを選択するという1種類のネイティブクエリのみを実行できるようにします。もしクライアントが異なるクエリを実行したり、1980より前の値を照会しようとした場合、クエリは失敗します。
import { secureNativeQuery, SquidService, RelationalNativeQueryContext } from '@squidcloud/backend';
export class ExampleService extends SquidService {
@secureNativeQuery('YOUR_INTEGRATION_ID')
verifyNativeQueryAccess(context: RelationalNativeQueryContext): boolean {
if (context.query !== 'SELECT * FROM SQUIDS WHERE YEAR = ${year}' || context.params.year < 1980) {
return false;
}
return true;
}
}
ネイティブMongoDBクエリを実行する際に提供されるコンテキストの型は次のとおりです:
MongoNativeQueryContext {
type: 'mongo';
collectionName: string;
aggregationPipeline: Array<any | undefined>;
}
このコンテキストを利用して、クライアントが実行できるaggregation pipelineクエリの種類を制限します。以下の例では、ユーザーがORDERSコレクション内のみでMongoDB aggregation pipelineを実行できるようにしています:
import { secureNativeQuery, SquidService, MongoNativeQueryContext } from '@squidcloud/backend';
export class ExampleService extends SquidService {
@secureNativeQuery('YOUR_INTEGRATION_ID')
verifyNativeMongoQueryAccess(context: MongoNativeQueryContext): boolean {
if (context.collectionName !== 'ORDERS') {
return false;
}
return true;
}
}