Sailsのポリシーは、承認とアクセス制御のための汎用性の高いツールです。アクションが実行される前に何らかのロジックを実行して、リクエストの処理を続行するかどうかを決定できます。ポリシーの最も一般的なユースケースは、特定のアクションをログインユーザーのみに制限することです。
注:ポリシーはコントローラーとアクションのみに適用され、ビューには適用されません。routes.js 設定ファイルでビューを直接指すルートを定義した場合、ポリシーは適用されません。ポリシーを適用するには、ビューを表示するアクションを定義し、そのアクションにルートを指定します。
アプリケーションで多数または複雑なポリシーを実装することは避けるのが最善です。代わりに、粒度の細かいロールベースの権限などの機能を実装する際には、アクションを使用して不要なアクセスを拒否します。アクションは、レスポンスで送信するビューローカルとJSONレスポンスデータの必要なパーソナライズも担当する必要があります。
たとえば、アプリケーションにユーザーレベルまたはロールベースの権限を実装する必要がある場合、最も簡単な解決策は、コントローラーアクションの先頭で関連するチェックを行うことです(インラインで行うか、ヘルパーを呼び出すかのどちらかです)。このベストプラクティスに従うことで、コードの保守性が大幅に向上します。
Sailsには、config/policies.js
にある組み込みのACL(アクセス制御リスト)があります。このファイルは、ポリシーをアクションとコントローラーにマッピングするために使用されます。
このファイルは宣言型であり、アプリケーションの権限がどのように機能するのではなく、どのようなものであるかを記述します。これにより、新しい開発者が何が起こっているかを理解しやすくなり、要件が時間の経過とともに変化してもアプリケーションの柔軟性が向上します。
config/policies.js
ファイルは、コントローラーまたはスタンドアロンアクションにポリシーを適用するかどうかによって、プロパティと値が異なるディクショナリです。コントローラー
コントローラーにポリシーを適用するには、config/policies.js
ディクショナリのプロパティ名としてコントローラー名を使用し、その値を、そのコントローラー内のアクションを適用するポリシーにマッピングするディクショナリに設定します。「マップされていないすべてのアクション」を表すには*
を使用します。ポリシーの名前は、ファイル拡張子を削除したファイル名と同じです。
module.exports.policies = {
UserController: {
// By default, require requests to come from a logged-in user
// (runs the policy in api/policies/isLoggedIn.js)
'*': 'isLoggedIn',
// Only allow admin users to delete other users
// (runs the policy in api/policies/isAdmin.js)
'delete': 'isAdmin',
// Allow anyone to access the login action, even if they're not logged in.
'login': true
}
};
1つ以上のスタンドアロンアクションにポリシーを適用するには、api/controllers
を基準としたアクションパスをconfig/policies.js
ディクショナリのプロパティ名として使用し、その値を、それらのアクションに適用するポリシーに設定します。アクションパスの最後にワイルドカード*
を使用することにより、そのパスで始まるすべてのアクションにポリシーを適用できます。上記と同じポリシーセットを、スタンドアロンアクションに適用するように書き直した例を以下に示します。
module.exports.policies = {
'user/*': 'isLoggedIn',
'user/delete': 'isAdmin',
'user/login': true
}
この例は、コントローラーベースのポリシーとは少し異なり、
isLoggedIn
ポリシーはapi/controllers/user
フォルダーおよびサブフォルダー内のすべてのアクションに適用されます(次のセクションで説明するように、user/delete
とuser/login
を除きます)。
ポリシーはカスケードしないことに注意することが重要です。上記の例では、isLoggedIn
ポリシーは、UserController.js
ファイル(またはapi/controllers/user
にあるスタンドアロンアクション)内のすべてのアクションに適用されますが、delete
とlogin
を除きます。アクションに複数のポリシーを適用する場合は、ポリシーを配列にリストします。例:
'getEncryptedData': ['isLoggedIn', 'isInValidRegion']
Sailsの組み込みブループリントAPIは、通常のSailsアクションを使用して実装されています。唯一の違いは、ブループリントアクションは暗黙的であることです。
ポリシーをブループリントアクションに適用するには、上記の例のようにポリシーマッピングを設定しますが、コントローラー内の関連する暗黙的なブループリントアクションの名前(またはスタンドアロンアクションとして)を指定します。例:
module.exports.policies = {
UserController: {
// Apply the 'isLoggedIn' policy to the 'update' action of 'UserController'
update: 'isLoggedIn'
}
};
または
module.exports.policies = {
'user/update': 'isLoggedIn'
};
*
プロパティを使用して、それ以外に明示的にマップされていないすべてのアクションにポリシーを適用できます。例:
module.exports.policies = {
'*': 'isLoggedIn',
'user/login': true
};
これにより、api/controllers/user/login.js
(またはapi/controllers/UserController.js
)内のlogin
アクションを除くすべてのアクションにisLoggedIn
ポリシーが適用されます。
Sailsには、グローバルまたは特定のコントローラーまたはアクションに適用できる2つの組み込みポリシーが用意されています。
true
:パブリックアクセス(誰でもマップされたコントローラー/アクションにアクセスできます)false
:アクセス不可(誰もマップされたコントローラー/アクションにアクセスできません)
'*': true
は、すべてのコントローラーとアクションのデフォルトポリシーです。本番環境では、意図せず公開した可能性のあるロジックへのアクセスを防ぐために、これをfalse
に設定することをお勧めします。
認証されていないユーザーのアクセスを防ぐための簡単なisLoggedIn
ポリシーを以下に示します。セッションでuserId
プロパティをチェックし、見つからない場合は、デフォルトのforbidden
レスポンスを送信します。真偽値多くのアプリケーションでは、これが必要な唯一のポリシーになる可能性があります。次の例では、ユーザーを認証するコントローラーアクションで、req.session.userId
を真偽値に設定することを前提としています。
// policies/isLoggedIn.js
module.exports = async function (req, res, proceed) {
// If `req.me` is set, then we know that this request originated
// from a logged-in user. So we can safely proceed to the next policy--
// or, if this is the last policy, the relevant action.
// > For more about where `req.me` comes from, check out this app's
// > custom hook (`api/hooks/custom/index.js`).
if (req.me) {
return proceed();
}
//--•
// Otherwise, this request did not come from a logged-in user.
return res.forbidden();
};