ServiceNowのACLの設計時には、標準や既存のACLを整理した上でどこを変更しなければならないかを見極める必要があります。それを効率的に行うために作成すべき3つの表を紹介します。その上で、複数の導入案件でACLを設計した経験をもとに、設計する際のコツを紹介します。
ACLの概要と評価プロセス
この記事はACLの概要と評価プロセスを理解していることを前提としているため、理解が怪しい方はまずこちらをご覧ください。
ACLの設計は3つの表を作成する
ACLは次の3つの表を作成することで、誰でもミスなく簡単に設計できます。
- Table-levelの表 … 縦軸に操作(CRUD)、横軸に*(全テーブル)から当該テーブルまでの階層をとり、各操作に対して最も優先度の高いTable-level ACL ruleを視覚的に表現するための表
- Field-level(*)の表 … 縦軸に操作(CRUD)、横軸に*(全テーブル)から当該テーブルまでの階層をとり、各操作に対して最も優先度の高いField-level(*)のACL ruleを視覚的に表現するための表
- Field-level(個別フィールド)の表 … 縦軸にフィールド名と操作(CRUD)の組み合わせ、横軸に親テーブルから当該テーブルまでの階層をとり、各フィールドと操作の組み合わせに対して最も優先度の高いField-level ACL ruleを視覚的に表現するための表
表の見方
これらの表を書くと、各表において最も右側に定義されたACL ruleが優先されます。
また、Field-level(個別フィールド)が存在するフィールドと操作の組み合わせの場合は、その行の最も右に定義されたruleが、Field-level(*)内の同じ操作の行の最も右に定義されたruleよりも優先されます。
表の書き方
表を書く際のポイントを紹介します。
各セルにはそのポイントで設定されているACLを簡単に記載する
各セルには、そのポイントで設定されているACLを簡単に記載しましょう。複数のACLが設定されている場合は箇条書きにする、ACLが存在しない場合は”-“を記載するなど、統一的な記載方法にすると見やすくなります。
横軸はテーブル階層に応じて列を増減させる
各表の横軸は、テーブル階層に応じて列を増減させます。親テーブルが存在しなければ、*(全テーブル)と該当テーブルの2列だけでOKです。Incident(インシデント)テーブルを継承したテーブルを作る場合、テーブル階層はTask > Incident > 該当テーブルとなるので、*(全テーブル)と合わせて4列必要になります。
標準や現行を調査して水色セルを埋めた上で、上書きしたい部分のみオレンジ色セルを埋める
水色セルは、標準や現行で存在しているACL ruleを調査して埋めます。その上で、上書きしたい部分のみオレンジ色セルを埋めます。標準通りで問題ない場合は、”-“などとしておきます。
ACL設計のコツ
基本的にはこの表を作っていくことで、ACLを効率的に、ミスなく設計できると思います。ここからは、実際によくあるシチュエーションごとにポイントを説明します
ACL設計の基本パターン
ACL設計の基本パターンは、Table-level ACL ruleでベースの権限を決め、その権限よりも特別厳しく制御したいフィールドに対してField-level ACL ruleを定義することです。Field-level(*)は定義しません。実際に表を書くとわかるのですが、Task(タスク)テーブルなど、標準の多くのテーブルのACLもこの方針で設計されています。
Field-level(*)は特定のフィールドだけ権限を緩くしたい場合に使う
Field-level(*)は、特定のフィールドだけ権限を緩くしたい場合に使うことが多いです。
ACLの評価プロセスは、Table-levelとField-levelの両方をパスしないと操作が許可されないため、Table-levelでベースの権限を決めてしまうと、特定のフィールドだけそれよりも緩くする、ということができません。
具体例として、「基本的にはAssigned toが自分(フォームを開いている人)のレコードのみWrite(書き込み・更新)可能にしたいが、例外的にコメント欄だけは誰でも書き込めるようにしたい、という場合を考えます。この場合に、下記のようにACLを設定すると、Table-levelとField-levelは両方をパスしなければならない(Andで繋がれる)ため、目的の制御ができません。
このような場合に用いるのが、Field-level(*) ACLです。特定のフィールドだけ権限を緩くしたい場合は、最も緩い権限のフィールドと同じTable-level ACLを設定し、もともと行いたかった権限制御をField-level(*) ACLで実現した上で、権限を緩くしたい特定フィールドのField-level(特定フィールド) ACLを定義してField-level(*) ACLを上書きします。
Field-level(*)を使ってベースの権限制御を行う場合は、添付ファイルの制御に注意
ServiceNowのフォーム画面には標準でファイルを添付するボタンが付いています。そして、ファイル添付ボタンは、Table-levelのWrite権限に応じて表示/非表示が切り替わります。そのため、上記の方法でベースの権限制御を行うと、最も緩いフィールドが書き込めるタイミングでファイル添付もできてしまうことになります。ファイル添付を防ぎたい場合は、Client Scriptのg_form.disableAttachments();などで別途制御する必要があります。
(フォームについては下記記事でまとめていますので、合わせてご参照ください)
標準テーブルに対してACLを変更する場合、緩めるのは簡単だが厳しくするのが難しい
標準テーブルのACLを変更したいという場合があります。このときに覚えておくとよいのが、ACLは緩めるのは簡単だが厳しくするのが難しい、ということです。なぜなら、同じ具体度=優先度に複数のACLルールが存在する場合は、いずれか1つを通ればよいからです。
例えば、Incident(インシデント)テーブルの標準のWrite操作に対するACLを見てみると、次の4つが定義されています。(ちなみに、ITSM系テーブルに対する標準ACLは数年前に変更があったので、かなり前のバージョンからのインスタンスを使っている場合、異なる可能性があります)
- ルール1: “sn_incident_write”ロールを持つ
- ルール2: “itil”ロールを持つ
- ルール3: (Callar項目またはOpened by項目が自分)かつIncident state項目が”Closed”でも”Canceled”でもない
- ルール4: “sn_incident_comments_write”を持つ、かつIncident state項目が”Closed”でも”Canceled”でもない
※ ルールの番号は筆者が適当に振っています
権限を緩くしたい場合は、新たにACL ruleを定義すればよい
これに対して、上記の4つの条件を満たしていなくても、自分で定義した”myrole”ロールを持っていればWrite可能としたい場合、つまり権限を緩くしたい場合は、次のルールを追加するだけで、既存ルールには変更を加えず実現可能です。
- ルール5: “myrole”ロールを持つ
権限を厳しくするためには、既存の全てのACLを変更する必要がある
これに対して“myrole”ロールを持つユーザだけがIncidentテーブルにWrite可能にしたい場合、つまり権限を厳しくしたい場合は、既存のルールを一つでもパスすると操作が許可されてしまうため、既存の全てのルールを変更or非アクティブにした上で、ルール5を追加する必要があります。
canCreate, canRead(), canWrite(), canDeleteを用いて同じ条件のTable-level ACLの設定を共通化
レコードの閲覧・読み取り(Write)と、更新・書き込み(Write)で同じ権限制御を行いたい、という場合があります。このときは、どちら一方のACL ruleだけを具体的に定義しておき、もう一方からは、Scriptを用いてそのACL ruleを参照するようにすることができます。これは標準のテーブルのACLやボタンの表示権限制御の際にも使われているテクニックです。
例えば、あるテーブルにおいてRead操作に対して複数のルールからなる複雑なACLが設定されており、これと同じ条件でWrite権限を設定したいとします。この時、ReadとWriteに同じ設定を入れると、権限を変更する際に両方の設定を変更しなければならず、保守性が下がってしまいます。
このような場合は、WriteのACL ruleはScript内でcanRead()関数を呼び出してあげると、ReadのACLを流用することができます。
answer = current.canRead();
なお、canRead()はGlideRecord APIの関数で、current変数には現在ACLの評価対象になっているレコードのGlideRecordオブジェクトが自動で格納されます。同様に、canCreate(), canWrite(), canDelete()関数も存在します。
Table-leve ACLを用いてレコード単位で権限制御を行う場合、レコード作成時は値が入っていないことに注意
Table-leve ACLを用いてレコード単位で権限制御を行う場合、Condition項目やScript項目内でcレコードの項目値を取得し、それに基づいて権限制御を行う場合が多いと思います。この際、レコード作成時には各項目の値が入っていないため、それによって想定外の動作をすることがあることに注意です。
例として、あるテーブルにおいて、Assigned to項目が自分の場合だけWrite可能にするために、次のようなACL ruleを設定したとします。
このように設定した場合、レコード作成時には、Assigned toが設定されていないため、常にこのACL ruleが満たされず、全ての項目が読み取り専用になってしまいます。そのため、このような場合は次のようなScriptで、新規作成の場合を考慮した判定を行う必要があります
if (current && current.assigned_to) { // current.operation() != 'insert' とかでもOK
asnwer = (current.assigned_to.getValue() == gs.getUserID());
} else {
answer = true;
}
Readの場合も同様の注意が必要です。
以上です。
コメント
優良な記事をありがとうございます。
お問合せでご連絡しようと思ったのですが、うまく動いていないのでこちらからご連絡させていただきました。
ご相談したいことがあるのですが、メールをいただければ幸いです!
コメントありがとうございます。
gmailのアドレスで返信させていただいております。
また、お問い合わせフォームの不具合は再現できておらず、可能でしたら詳細を伺えればと思います。