データアクセスの保護
Squid が提供する一連のデコレーターを使用して、Squid に接続されている任意のデータベース(組み込みの内部データベースを含む)を保護します。
各デコレーターは、データベースの異なる部分を保護するように設計されています。
特定の種類のアクションを保護するために、別の context オブジェクトが関数に渡されます。たとえば、read 操作を保護したい場合、context オブジェクトは QueryContext 型になり、write 操作では MutationContext オブジェクトが使用されます。
適切なデコレーターを使用して、次のいずれかの種類のデータアクセスを保護できます。
readinsertupdatedeletewriteall
write デコレーターには insert、update、delete 操作が含まれるため、データベース上のあらゆる種類の書き込み操作を包括的に保護します。
@secureDatabase
Squid が提供する @secureDatabase デコレーターは、アクセス対象の table や collection に関係なく、すべてのデータベースアクセスを保護するために使用できます。このデコレーターは、データベースにアクセスするすべてのアクションに対して認可(authorization)チェックを強制します。
たとえば、開発者は @secureDatabase デコレーターを使用して、認証済みユーザーのみがデータベースにアクセスできるようにできます。そのために、ユーザーが認証されているかどうかを確認する関数を定義します。
import { secureDatabase, SquidService } from '@squidcloud/backend';
export class ExampleService extends SquidService {
@secureDatabase('all', 'usersDatabase')
verifyUserAuthenticated(): boolean {
return this.isAuthenticated();
}
}
admin プロパティを持つユーザーのみが usersDatabase を変更できるようにするには、開発者は @secureDatabase デコレーターを、ユーザーが認証されていて admin プロパティを持つかどうかをチェックする verifyUserAuthenticated 関数と組み合わせて使用できます。
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 デコレーターを使用して、データベース内の特定の collection を保護できます。これにより、特定の collection 内のデータへアクセスおよび変更できるのは認可されたユーザーのみになります。
たとえば開発者は、owner カラムに自分の userId が入っているドキュメントだけをユーザーが読み取れるようにするために、@secureCollection デコレーターを使用できます。以下はサンプルコードです。
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 のみを update、delete、insert できるようにしたい場合もあるでしょう。
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 を保護するには、@secureNativeQuery() デコレーターを使用し、データベースの integration ID を渡します。次の例では、ユーザーが認証されているかをチェックする関数を定義しています。
import { secureNativeQuery, SquidService } from '@squidcloud/backend';
export class ExampleService extends SquidService {
@secureNativeQuery('YOUR_INTEGRATION_ID')
verifyUserAuthenticated(): boolean {
return this.isAuthenticated();
}
}
次の例のように auth token attributes を使って、より細かなアクセス制御を追加できます。
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'];
}
}
collection を使用して auth permissions を保存することもできます。その collection が 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;
}
}
Native query context
ネイティブの relational または MongoDB クエリを実行する際、Squid backend にはクライアントが実行したい native query の種類('relational' または 'mongo')や、クエリ種別に基づくその他の属性を示す context が用意されています。
ネイティブ relational クエリの実行時には、次の context type が提供されます。
RelationalNativeQueryContext {
type: 'relational';
query: string;
params: Record<string, any>;
}
この context を使用して、クライアントが実行できる native query の種類を制限できます。たとえば次のコードでは、SQUIDS という collection(または table の行)から YEAR フィールドの値が 1980 より大きいものを select するタイプの native query を 1 種類だけ実行できるようにします。クライアントが別のクエリを実行しようとしたり、1980 より前の値を table に対してクエリしようとした場合、クエリは失敗します。
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 クエリの実行時には、次の context type が提供されます。
MongoNativeQueryContext {
type: 'mongo';
collectionName: string;
aggregationPipeline: Array<any | undefined>;
}
この context を使用して、クライアントがあなたの Mongo aggregation pipeline に対して実行できる aggregation pipeline クエリの種類を制限できます。次の例では、ユーザーが ORDERS collection に対してのみ 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;
}
}